summaryrefslogtreecommitdiffstats
path: root/fluent-bit/tests/internal
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 02:57:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 02:57:58 +0000
commitbe1c7e50e1e8809ea56f2c9d472eccd8ffd73a97 (patch)
tree9754ff1ca740f6346cf8483ec915d4054bc5da2d /fluent-bit/tests/internal
parentInitial commit. (diff)
downloadnetdata-be1c7e50e1e8809ea56f2c9d472eccd8ffd73a97.tar.xz
netdata-be1c7e50e1e8809ea56f2c9d472eccd8ffd73a97.zip
Adding upstream version 1.44.3.upstream/1.44.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'fluent-bit/tests/internal')
-rw-r--r--fluent-bit/tests/internal/CMakeLists.txt216
-rw-r--r--fluent-bit/tests/internal/README.md3
-rw-r--r--fluent-bit/tests/internal/avro.c382
-rw-r--r--fluent-bit/tests/internal/aws/CMakeLists.txt7
-rw-r--r--fluent-bit/tests/internal/aws/placeholder.c11
-rw-r--r--fluent-bit/tests/internal/aws_compress.c489
-rw-r--r--fluent-bit/tests/internal/aws_credentials.c382
-rw-r--r--fluent-bit/tests/internal/aws_credentials_ec2.c1037
-rw-r--r--fluent-bit/tests/internal/aws_credentials_http.c382
-rw-r--r--fluent-bit/tests/internal/aws_credentials_process.c496
-rw-r--r--fluent-bit/tests/internal/aws_credentials_profile.c405
-rw-r--r--fluent-bit/tests/internal/aws_credentials_sts.c939
-rw-r--r--fluent-bit/tests/internal/aws_credentials_test_internal.h42
-rw-r--r--fluent-bit/tests/internal/aws_util.c395
-rw-r--r--fluent-bit/tests/internal/base64.c50
-rw-r--r--fluent-bit/tests/internal/bucket_queue.c248
-rw-r--r--fluent-bit/tests/internal/config_format.c86
-rw-r--r--fluent-bit/tests/internal/config_format_fluentbit.c353
-rw-r--r--fluent-bit/tests/internal/config_format_yaml.c324
-rw-r--r--fluent-bit/tests/internal/config_map.c386
-rw-r--r--fluent-bit/tests/internal/crypto.c202
-rw-r--r--fluent-bit/tests/internal/csv.c167
-rw-r--r--fluent-bit/tests/internal/data/avro/json_single_map_001.json5
-rw-r--r--fluent-bit/tests/internal/data/avro/live-sample.json25
-rw-r--r--fluent-bit/tests/internal/data/avro/multiline.json27
-rwxr-xr-xfluent-bit/tests/internal/data/aws_credentials/credential_process/aws-credential-process14
-rw-r--r--fluent-bit/tests/internal/data/aws_credentials/shared_config.ini9
-rw-r--r--fluent-bit/tests/internal/data/aws_credentials/shared_credentials_file.ini28
-rw-r--r--fluent-bit/tests/internal/data/aws_credentials/shared_credentials_file_nodefault.ini12
-rw-r--r--fluent-bit/tests/internal/data/aws_credentials/web_identity_token_file.txt1
-rw-r--r--fluent-bit/tests/internal/data/config_format/classic/fluent-bit.conf35
-rw-r--r--fluent-bit/tests/internal/data/config_format/classic/indent_level_error.conf6
-rw-r--r--fluent-bit/tests/internal/data/config_format/classic/issue6281.conf2
-rw-r--r--fluent-bit/tests/internal/data/config_format/classic/issue6281_input.conf2
-rw-r--r--fluent-bit/tests/internal/data/config_format/classic/issue6281_output.conf2
-rw-r--r--fluent-bit/tests/internal/data/config_format/classic/issue_5880.conf14
-rw-r--r--fluent-bit/tests/internal/data/config_format/classic/nolimitline.conf11
-rw-r--r--fluent-bit/tests/internal/data/config_format/classic/recursion.conf1
-rw-r--r--fluent-bit/tests/internal/data/config_format/classic/service.conf4
-rw-r--r--fluent-bit/tests/internal/data/config_format/yaml/fluent-bit.yaml29
-rw-r--r--fluent-bit/tests/internal/data/config_format/yaml/issue_7559.yaml14
-rw-r--r--fluent-bit/tests/internal/data/config_format/yaml/parsers/parsers-conf.yaml3
-rw-r--r--fluent-bit/tests/internal/data/config_format/yaml/parsers/parsers.conf6
-rw-r--r--fluent-bit/tests/internal/data/config_format/yaml/pipelines/slist/even.yaml7
-rw-r--r--fluent-bit/tests/internal/data/config_format/yaml/pipelines/slist/odd.yaml8
-rw-r--r--fluent-bit/tests/internal/data/config_format/yaml/service.yaml5
-rw-r--r--fluent-bit/tests/internal/data/config_format/yaml/test.yaml31
-rw-r--r--fluent-bit/tests/internal/data/config_format/yaml/test/dummy_pipeline.yaml13
-rw-r--r--fluent-bit/tests/internal/data/config_format/yaml/test/nested.yaml7
-rw-r--r--fluent-bit/tests/internal/data/file/empty_file.txt0
-rw-r--r--fluent-bit/tests/internal/data/file/text_file.txt5
-rw-r--r--fluent-bit/tests/internal/data/input_chunk/log/a_thousand_plus_one_bytes.log2
-rw-r--r--fluent-bit/tests/internal/data/input_chunk/log/test_buffer_drop_chunks.h27
-rw-r--r--fluent-bit/tests/internal/data/input_chunk/log/test_buffer_valid.log3
-rw-r--r--fluent-bit/tests/internal/data/input_chunk/out/a_thousand_plus_one_bytes.out0
-rw-r--r--fluent-bit/tests/internal/data/input_chunk/out/test_buffer_valid.out3
-rw-r--r--fluent-bit/tests/internal/data/input_chunk/parser.conf10
-rw-r--r--fluent-bit/tests/internal/data/mp/apache_10k.mpbin0 -> 993663 bytes
-rw-r--r--fluent-bit/tests/internal/data/pack/README.md3
-rw-r--r--fluent-bit/tests/internal/data/pack/bug342.json240
-rw-r--r--fluent-bit/tests/internal/data/pack/dup_keys_in.json1
-rw-r--r--fluent-bit/tests/internal/data/pack/dup_keys_out.json1
-rw-r--r--fluent-bit/tests/internal/data/pack/json_single_map_001.json5
-rw-r--r--fluent-bit/tests/internal/data/pack/json_single_map_002.json4
-rw-r--r--fluent-bit/tests/internal/data/pack/mixed.py31
-rw-r--r--fluent-bit/tests/internal/data/pack/mixed_001.json1
-rw-r--r--fluent-bit/tests/internal/data/pack/mixed_001.mp1
-rw-r--r--fluent-bit/tests/internal/data/pack/mixed_001.txt1
-rw-r--r--fluent-bit/tests/internal/data/pack/mixed_002.json1
-rw-r--r--fluent-bit/tests/internal/data/pack/mixed_002.mpbin0 -> 37 bytes
-rw-r--r--fluent-bit/tests/internal/data/pack/mixed_002.txt7
-rw-r--r--fluent-bit/tests/internal/data/pack/mixed_003.json1
-rw-r--r--fluent-bit/tests/internal/data/pack/mixed_003.mp1
-rw-r--r--fluent-bit/tests/internal/data/pack/mixed_003.txt1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_bell.json1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_bell.mp1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_bell.txt1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_copyright.json1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_copyright.mp1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_copyright.txt1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_gen.py32
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_hokke.json1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_hokke.mp1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_hokke.txt1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_relaxed.json1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_relaxed.mp1
-rw-r--r--fluent-bit/tests/internal/data/pack/utf8_relaxed.txt1
-rw-r--r--fluent-bit/tests/internal/data/parser/json.conf200
-rw-r--r--fluent-bit/tests/internal/data/parser/regex.conf234
-rw-r--r--fluent-bit/tests/internal/data/reload/fluent-bit.conf15
-rw-r--r--fluent-bit/tests/internal/data/reload/yaml/processor.yaml41
-rw-r--r--fluent-bit/tests/internal/data/s3_local_buffer/.gitkeep1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/LICENSE202
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/NOTICE2
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.creq9
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.req6
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.sreq7
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.creq9
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.req6
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.sreq7
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.creq9
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.req7
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.sreq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.creq10
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.req5
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.sreq6
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/normalize-path.txt3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.creq9
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.req4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.sreq5
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.creq9
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.req4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.sreq5
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.sreq5
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.creq9
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.req4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.sreq5
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/readme.txt15
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.creq8
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.req3
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.creq9
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.req6
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sreq7
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sts4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.authz1
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.creq9
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.req4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sreq4
-rw-r--r--fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sts4
-rwxr-xr-xfluent-bit/tests/internal/data/stream_processor/gen_msgpack.sh40
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/1.json4
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/1.mpbin0 -> 257 bytes
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/2.json4
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/2.mpbin0 -> 268 bytes
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/3.json3
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/3.mpbin0 -> 201 bytes
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/4.json4
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/4.mpbin0 -> 260 bytes
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/5.json4
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/5.mpbin0 -> 272 bytes
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/6.json3
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/6.mpbin0 -> 204 bytes
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/7.json4
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-hw/7.mpbin0 -> 262 bytes
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-subkeys.json9
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples-subkeys.mpbin0 -> 479 bytes
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples.json11
-rw-r--r--fluent-bit/tests/internal/data/stream_processor/samples.mpbin0 -> 726 bytes
-rw-r--r--fluent-bit/tests/internal/data/tls/certificate.pem22
-rw-r--r--fluent-bit/tests/internal/data/tls/private_key.pem28
-rw-r--r--fluent-bit/tests/internal/env.c91
-rw-r--r--fluent-bit/tests/internal/error_reporter.c82
-rw-r--r--fluent-bit/tests/internal/file.c48
-rw-r--r--fluent-bit/tests/internal/flb_event_loop.c592
-rw-r--r--fluent-bit/tests/internal/flb_tests_internal.h.in43
-rw-r--r--fluent-bit/tests/internal/flb_time.c335
-rw-r--r--fluent-bit/tests/internal/fstore.c84
-rw-r--r--fluent-bit/tests/internal/fuzzers/CMakeLists.txt60
-rw-r--r--fluent-bit/tests/internal/fuzzers/aws_credentials_fuzzer.c132
-rw-r--r--fluent-bit/tests/internal/fuzzers/aws_util_fuzzer.c107
-rw-r--r--fluent-bit/tests/internal/fuzzers/base64_fuzzer.c20
-rw-r--r--fluent-bit/tests/internal/fuzzers/cmetrics_decode_fuzz.c69
-rw-r--r--fluent-bit/tests/internal/fuzzers/config_fuzzer.c417
-rw-r--r--fluent-bit/tests/internal/fuzzers/config_map_fuzzer.c212
-rw-r--r--fluent-bit/tests/internal/fuzzers/config_random_fuzzer.c57
-rw-r--r--fluent-bit/tests/internal/fuzzers/config_yaml_fuzzer.c65
-rw-r--r--fluent-bit/tests/internal/fuzzers/ctrace_fuzzer.c24
-rw-r--r--fluent-bit/tests/internal/fuzzers/engine_fuzzer.c171
-rw-r--r--fluent-bit/tests/internal/fuzzers/filter_stdout_fuzzer.c62
-rw-r--r--fluent-bit/tests/internal/fuzzers/flb_fuzz_header.h42
-rw-r--r--fluent-bit/tests/internal/fuzzers/flb_json_fuzzer.c82
-rw-r--r--fluent-bit/tests/internal/fuzzers/flb_mp_fuzzer.c55
-rw-r--r--fluent-bit/tests/internal/fuzzers/fstore_fuzzer.c86
-rw-r--r--fluent-bit/tests/internal/fuzzers/http_fuzzer.c116
-rw-r--r--fluent-bit/tests/internal/fuzzers/input_fuzzer.c164
-rw-r--r--fluent-bit/tests/internal/fuzzers/local_test.c85
-rw-r--r--fluent-bit/tests/internal/fuzzers/msgpack_parse_fuzzer.c30
-rw-r--r--fluent-bit/tests/internal/fuzzers/msgpack_to_gelf_fuzzer.c25
-rw-r--r--fluent-bit/tests/internal/fuzzers/multiline_fuzzer.c180
-rw-r--r--fluent-bit/tests/internal/fuzzers/pack_json_state_fuzzer.c25
-rw-r--r--fluent-bit/tests/internal/fuzzers/parse_json_fuzzer.c69
-rw-r--r--fluent-bit/tests/internal/fuzzers/parse_logfmt_fuzzer.c50
-rw-r--r--fluent-bit/tests/internal/fuzzers/parse_ltsv_fuzzer.c35
-rw-r--r--fluent-bit/tests/internal/fuzzers/parser_fuzzer.c203
-rw-r--r--fluent-bit/tests/internal/fuzzers/record_ac_fuzzer.c101
-rw-r--r--fluent-bit/tests/internal/fuzzers/signv4_fuzzer.c111
-rw-r--r--fluent-bit/tests/internal/fuzzers/strp_fuzzer.c35
-rw-r--r--fluent-bit/tests/internal/fuzzers/utils_fuzzer.c242
-rw-r--r--fluent-bit/tests/internal/gelf.c101
-rw-r--r--fluent-bit/tests/internal/gzip.c46
-rw-r--r--fluent-bit/tests/internal/hash.c165
-rw-r--r--fluent-bit/tests/internal/hashtable.c413
-rw-r--r--fluent-bit/tests/internal/hmac.c136
-rw-r--r--fluent-bit/tests/internal/http_client.c478
-rw-r--r--fluent-bit/tests/internal/include/sp_cb_functions.h655
-rw-r--r--fluent-bit/tests/internal/include/sp_helpers.h158
-rw-r--r--fluent-bit/tests/internal/include/sp_invalid_queries.h20
-rw-r--r--fluent-bit/tests/internal/include/sp_select_keys.h131
-rw-r--r--fluent-bit/tests/internal/include/sp_select_subkeys.h84
-rw-r--r--fluent-bit/tests/internal/include/sp_snapshot.h38
-rw-r--r--fluent-bit/tests/internal/include/sp_window.h53
-rw-r--r--fluent-bit/tests/internal/input_chunk.c516
-rw-r--r--fluent-bit/tests/internal/log.c154
-rw-r--r--fluent-bit/tests/internal/log_event_decoder.c281
-rw-r--r--fluent-bit/tests/internal/lua.c325
-rw-r--r--fluent-bit/tests/internal/metrics.c83
-rw-r--r--fluent-bit/tests/internal/mp.c353
-rw-r--r--fluent-bit/tests/internal/multiline.c1478
-rw-r--r--fluent-bit/tests/internal/network.c164
-rw-r--r--fluent-bit/tests/internal/pack.c914
-rw-r--r--fluent-bit/tests/internal/parser.c528
-rw-r--r--fluent-bit/tests/internal/parser_json.c547
-rw-r--r--fluent-bit/tests/internal/parser_logfmt.c398
-rw-r--r--fluent-bit/tests/internal/parser_ltsv.c486
-rw-r--r--fluent-bit/tests/internal/parser_regex.c491
-rw-r--r--fluent-bit/tests/internal/pipe.c89
-rw-r--r--fluent-bit/tests/internal/processor.c125
-rw-r--r--fluent-bit/tests/internal/random.c33
-rw-r--r--fluent-bit/tests/internal/record_accessor.c1731
-rw-r--r--fluent-bit/tests/internal/regex.c330
-rw-r--r--fluent-bit/tests/internal/reload.c249
-rw-r--r--fluent-bit/tests/internal/ring_buffer.c168
-rw-r--r--fluent-bit/tests/internal/router.c55
-rw-r--r--fluent-bit/tests/internal/sds.c88
-rw-r--r--fluent-bit/tests/internal/sds_list.c238
-rw-r--r--fluent-bit/tests/internal/signv4.c666
-rw-r--r--fluent-bit/tests/internal/slist.c205
-rw-r--r--fluent-bit/tests/internal/stream_processor.c934
-rw-r--r--fluent-bit/tests/internal/typecast.c359
-rw-r--r--fluent-bit/tests/internal/unit_sizes.c59
-rw-r--r--fluent-bit/tests/internal/uri.c87
-rw-r--r--fluent-bit/tests/internal/utils.c622
354 files changed, 28874 insertions, 0 deletions
diff --git a/fluent-bit/tests/internal/CMakeLists.txt b/fluent-bit/tests/internal/CMakeLists.txt
new file mode 100644
index 00000000..5288004d
--- /dev/null
+++ b/fluent-bit/tests/internal/CMakeLists.txt
@@ -0,0 +1,216 @@
+find_package(Threads REQUIRED)
+
+include_directories(cutest/)
+
+set(UNIT_TESTS_FILES
+ pack.c
+ pipe.c
+ sds.c
+ sds_list.c
+ hmac.c
+ crypto.c
+ hash.c
+ slist.c
+ router.c
+ network.c
+ unit_sizes.c
+ hashtable.c
+ http_client.c
+ utils.c
+ gzip.c
+ random.c
+ config_map.c
+ mp.c
+ input_chunk.c
+ flb_time.c
+ file.c
+ csv.c
+ multiline.c
+ typecast.c
+ base64.c
+ bucket_queue.c
+ flb_event_loop.c
+ ring_buffer.c
+ regex.c
+ parser_json.c
+ parser_ltsv.c
+ parser_regex.c
+ parser_logfmt.c
+ env.c
+ log.c
+ log_event_decoder.c
+ processor.c
+ uri.c
+ )
+
+# Config format
+set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ config_format.c
+ config_format_fluentbit.c
+)
+
+if(FLB_HAVE_LIBYAML)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ config_format_yaml.c
+ )
+endif()
+
+if (NOT WIN32)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ gelf.c
+ fstore.c
+ reload.c
+ )
+endif()
+
+if(FLB_PARSER)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ parser.c
+ )
+endif()
+
+if(FLB_STREAM_PROCESSOR)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ stream_processor.c
+ )
+endif()
+
+if(FLB_RECORD_ACCESSOR)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ record_accessor.c
+ )
+endif()
+
+if(FLB_METRICS)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ metrics.c
+ )
+endif()
+
+if(FLB_SIGNV4)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ signv4.c
+ )
+endif()
+
+if(FLB_AVRO_ENCODER)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ avro.c
+ )
+endif()
+
+if(FLB_AWS)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ aws_util.c
+ aws_compress.c
+ aws_credentials.c
+ aws_credentials_ec2.c
+ aws_credentials_sts.c
+ aws_credentials_http.c
+ aws_credentials_profile.c
+ )
+ if(FLB_HAVE_AWS_CREDENTIAL_PROCESS)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ aws_credentials_process.c
+ )
+ endif()
+endif()
+
+if(FLB_AWS_ERROR_REPORTER)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ error_reporter.c
+ )
+endif()
+
+if(FLB_LUAJIT)
+ set(UNIT_TESTS_FILES
+ ${UNIT_TESTS_FILES}
+ lua.c
+ )
+endif()
+
+set(UNIT_TESTS_DATA
+ data/tls/certificate.pem
+ data/tls/private_key.pem
+ data/pack/json_single_map_001.json
+ data/pack/json_single_map_002.json
+ data/parser/json.conf
+ data/parser/regex.conf
+ data/input_chunk/log/a_thousand_plus_one_bytes.log
+ data/input_chunk/log/test_buffer_valid.log
+ )
+
+set(FLB_TESTS_DATA_PATH ${CMAKE_CURRENT_SOURCE_DIR}/)
+configure_file(
+ "${CMAKE_CURRENT_SOURCE_DIR}/flb_tests_internal.h.in"
+ "${CMAKE_CURRENT_SOURCE_DIR}/flb_tests_internal.h"
+ )
+
+# Move data files to the path the test binaries can find them
+macro(FLB_TEST_COPY_DATA path)
+ configure_file(${path} ${CMAKE_CURRENT_BINARY_DIR}/${path} COPYONLY)
+endmacro()
+
+foreach(test_data ${UNIT_TESTS_DATA})
+ FLB_TEST_COPY_DATA(${test_data})
+endforeach()
+
+# Prepare list of unit tests function
+function(prepare_unit_tests TEST_PREFIX SOURCEFILES)
+ foreach(source_file ${SOURCEFILES})
+ get_filename_component(source_file_we ${source_file} NAME_WE)
+ set(source_file_we ${TEST_PREFIX}${source_file_we})
+ if(FLB_WITHOUT_${source_file_we})
+ message("Skipping test ${source_file_we}")
+ else()
+ add_executable(
+ ${source_file_we}
+ ${source_file}
+ )
+ add_sanitizers(${source_file_we})
+
+ if(FLB_JEMALLOC)
+ target_link_libraries(${source_file_we} libjemalloc ${CMAKE_THREAD_LIBS_INIT})
+ else()
+ target_link_libraries(${source_file_we} ${CMAKE_THREAD_LIBS_INIT})
+ endif()
+
+ if(FLB_AWS)
+ target_link_libraries(${source_file_we} flb-aws)
+ endif()
+
+ if(FLB_STREAM_PROCESSOR)
+ target_link_libraries(${source_file_we} flb-sp)
+ endif()
+
+ target_link_libraries(${source_file_we} fluent-bit-static cfl-static)
+
+ if(FLB_AVRO_ENCODER)
+ target_link_libraries(${source_file_we} avro-static jansson)
+ endif()
+
+ add_test(NAME ${source_file_we}
+ COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${source_file_we}
+ WORKING_DIRECTORY ${CMAKE_HOME_DIRECTORY}/build)
+ set_tests_properties(${source_file_we} PROPERTIES LABELS "internal")
+ endif()
+ endforeach()
+endfunction(prepare_unit_tests)
+
+prepare_unit_tests(flb-it- "${UNIT_TESTS_FILES}")
+
+if(FLB_TESTS_INTERNAL_FUZZ)
+ add_subdirectory(fuzzers)
+endif()
diff --git a/fluent-bit/tests/internal/README.md b/fluent-bit/tests/internal/README.md
new file mode 100644
index 00000000..4ea04b20
--- /dev/null
+++ b/fluent-bit/tests/internal/README.md
@@ -0,0 +1,3 @@
+# Fluent Bit Internal Tests
+
+The following directory contains unit tests to validate specific functions of Fluent Bit core (not plugins).
diff --git a/fluent-bit/tests/internal/avro.c b/fluent-bit/tests/internal/avro.c
new file mode 100644
index 00000000..45f07343
--- /dev/null
+++ b/fluent-bit/tests/internal/avro.c
@@ -0,0 +1,382 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+#include <errno.h>
+#include <string.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_avro.h>
+#include <fluent-bit/flb_log.h>
+#include <fluent-bit/flb_pack.h>
+#include <msgpack.h>
+
+#include "flb_tests_internal.h"
+
+/* AVRO iteration tests */
+#define AVRO_SINGLE_MAP1 FLB_TESTS_DATA_PATH "/data/avro/json_single_map_001.json"
+#define AVRO_MULTILINE_JSON FLB_TESTS_DATA_PATH "/data/avro/live-sample.json"
+
+const char JSON_SINGLE_MAP_001_SCHEMA[] =
+"{\"type\":\"record\",\
+ \"name\":\"Map001\",\
+ \"fields\":[\
+ {\"name\": \"key001\", \"type\": \"int\"},\
+ {\"name\": \"key002\", \"type\": \"float\"},\
+ {\"name\": \"key003\", \"type\": \"string\"},\
+ {\"name\": \"key004\", \"type\":\
+ {\"type\": \"array\", \"items\":\
+ {\"type\": \"map\",\"values\": \"int\"}}}]}";
+
+msgpack_unpacked test_init(avro_value_t *aobject, avro_schema_t *aschema, const char *json_schema, const char *json_data) {
+ char *out_buf;
+ size_t out_size;
+ int root_type;
+
+ avro_value_iface_t *aclass = flb_avro_init(aobject, (char *)json_schema, strlen(json_schema), aschema);
+ TEST_CHECK(aclass != NULL);
+
+ char *data = mk_file_to_buffer(json_data);
+ TEST_CHECK(data != NULL);
+
+ size_t len = strlen(data);
+
+ TEST_CHECK(flb_pack_json(data, len, &out_buf, &out_size, &root_type, NULL) == 0);
+
+ msgpack_unpacked msg;
+ msgpack_unpacked_init(&msg);
+ TEST_CHECK(msgpack_unpack_next(&msg, out_buf, out_size, NULL) == MSGPACK_UNPACK_SUCCESS);
+
+ avro_value_iface_decref(aclass);
+ flb_free(data);
+ flb_free(out_buf);
+
+ return msg;
+}
+/* Unpack msgpack per avro schema */
+void test_unpack_to_avro()
+{
+ avro_value_t aobject;
+ avro_schema_t aschema;
+
+ msgpack_unpacked mp = test_init(&aobject, &aschema, JSON_SINGLE_MAP_001_SCHEMA, AVRO_SINGLE_MAP1);
+
+ msgpack_object_print(stderr, mp.data);
+ flb_msgpack_to_avro(&aobject, &mp.data);
+
+ avro_value_t test_value;
+ TEST_CHECK(avro_value_get_by_name(&aobject, "key001", &test_value, NULL) == 0);
+
+ int val001 = 0;
+ avro_value_get_int(&test_value, &val001);
+ TEST_CHECK(val001 == 123456789);
+
+ TEST_CHECK(avro_value_get_by_name(&aobject, "key002", &test_value, NULL) == 0);
+
+ float val002 = 0.0f;
+ // for some reason its rounding to this value
+ float val002_actual = 0.999888f;
+ avro_value_get_float(&test_value, &val002);
+ char str1[80];
+ char str2[80];
+ sprintf(str1, "%f", val002);
+ sprintf(str2, "%f", val002_actual);
+ flb_info("val002:%s:\n", str1);
+ flb_info("val002_actual:%s:\n", str2);
+ TEST_CHECK((strcmp(str1, str2) == 0));
+
+ TEST_CHECK(avro_value_get_by_name(&aobject, "key003", &test_value, NULL) == 0);
+ const char *val003 = NULL;
+ size_t val003_size = 0;
+ avro_value_get_string(&test_value, &val003, &val003_size);
+ flb_info("val003_size:%zu:\n", val003_size);
+ TEST_CHECK(val003[val003_size] == '\0');
+
+ TEST_CHECK((strcmp(val003, "abcdefghijk") == 0));
+ // avro_value_get_by_name returns ths string length plus the NUL
+ TEST_CHECK(val003_size == 12);
+
+ TEST_CHECK(avro_value_get_by_name(&aobject, "key004", &test_value, NULL) == 0);
+
+ size_t asize = 0;
+ avro_value_get_size(&test_value, &asize);
+ flb_info("asize:%zu:\n", asize);
+
+ TEST_CHECK(asize == 2);
+
+ // check the first map
+ avro_value_t k8sRecord;
+ TEST_CHECK(avro_value_get_by_index(&test_value, 0, &k8sRecord, NULL) == 0);
+
+ size_t msize = 0;
+ avro_value_get_size(&k8sRecord, &msize);
+ flb_info("msize:%zu:\n", msize);
+
+ TEST_CHECK(msize == 2);
+
+ avro_value_t obj_test;
+ const char *actual_key = NULL;
+ int actual = 0;
+
+ // check the first item in the map
+ TEST_CHECK(avro_value_get_by_index(&k8sRecord, 0, &obj_test, &actual_key) == 0);
+ flb_info("actual_key:%s:\n", actual_key);
+
+ TEST_CHECK(strcmp(actual_key, "a") == 0);
+ TEST_CHECK(avro_value_get_int(&obj_test, &actual) == 0);
+ TEST_CHECK(actual == 1);
+
+ // check the second item in the map
+ TEST_CHECK(avro_value_get_by_index(&k8sRecord, 1, &obj_test, &actual_key) == 0);
+ flb_info("actual_key:%s:\n", actual_key);
+
+ TEST_CHECK(strcmp(actual_key, "b") == 0);
+ TEST_CHECK(avro_value_get_int(&obj_test, &actual) == 0);
+ TEST_CHECK(actual == 2);
+
+ // check the second map
+ TEST_CHECK(avro_value_get_by_index(&test_value, 1, &k8sRecord, NULL) == 0);
+
+ avro_value_get_size(&k8sRecord, &msize);
+ flb_info("msize:%zu:\n", msize);
+
+ TEST_CHECK(msize == 2);
+
+ avro_value_decref(&aobject);
+ avro_schema_decref(aschema);
+ msgpack_unpacked_destroy(&mp);
+}
+
+void test_parse_reordered_schema()
+{
+ // test same schema but different order of fields
+ const char *ts1 = "{\"name\":\"qavrov2_record\",\"type\":\"record\",\"fields\":[{\"name\":\"log\",\"type\":\"string\"},{\"name\":\"capture\",\"type\":\"string\"},{\"name\":\"kubernetes\",\"type\":{\"name\":\"krec\",\"type\":\"record\",\"fields\":[{\"name\":\"pod_name\",\"type\":\"string\"},{\"name\":\"namespace_name\",\"type\":\"string\"},{\"name\":\"pod_id\",\"type\":\"string\"},{\"name\":\"labels\",\"type\":{\"type\":\"map\",\"values\":\"string\"}},{\"name\":\"annotations\",\"type\":{\"type\":\"map\",\"values\":\"string\"}},{\"name\":\"host\",\"type\":\"string\"},{\"name\":\"container_name\",\"type\":\"string\"},{\"name\":\"docker_id\",\"type\":\"string\"},{\"name\":\"container_hash\",\"type\":\"string\"},{\"name\":\"container_image\",\"type\":\"string\"}]}}]}";
+ const char *ts2 = "{\"name\":\"qavrov2_record\",\"type\":\"record\",\"fields\":[{\"name\":\"capture\",\"type\":\"string\"},{\"name\":\"log\",\"type\":\"string\"},{\"name\":\"kubernetes\",\"type\":{\"name\":\"krec\",\"type\":\"record\",\"fields\":[{\"name\":\"namespace_name\",\"type\":\"string\"},{\"name\":\"pod_name\",\"type\":\"string\"},{\"name\":\"pod_id\",\"type\":\"string\"},{\"name\":\"annotations\",\"type\":{\"type\":\"map\",\"values\":\"string\"}},{\"name\":\"labels\",\"type\":{\"type\":\"map\",\"values\":\"string\"}},{\"name\":\"host\",\"type\":\"string\"},{\"name\":\"container_name\",\"type\":\"string\"},{\"name\":\"docker_id\",\"type\":\"string\"},{\"name\":\"container_hash\",\"type\":\"string\"},{\"name\":\"container_image\",\"type\":\"string\"}]}}]}";
+ const char *ts3 = "{\"name\":\"qavrov2_record\",\"type\":\"record\",\"fields\":[{\"name\":\"newnovalue\",\"type\":\"string\"},{\"name\":\"capture\",\"type\":\"string\"},{\"name\":\"log\",\"type\":\"string\"},{\"name\":\"kubernetes\",\"type\":{\"name\":\"krec\",\"type\":\"record\",\"fields\":[{\"name\":\"namespace_name\",\"type\":\"string\"},{\"name\":\"pod_name\",\"type\":\"string\"},{\"name\":\"pod_id\",\"type\":\"string\"},{\"name\":\"annotations\",\"type\":{\"type\":\"map\",\"values\":\"string\"}},{\"name\":\"labels\",\"type\":{\"type\":\"map\",\"values\":\"string\"}},{\"name\":\"host\",\"type\":\"string\"},{\"name\":\"container_name\",\"type\":\"string\"},{\"name\":\"docker_id\",\"type\":\"string\"},{\"name\":\"container_hash\",\"type\":\"string\"},{\"name\":\"container_image\",\"type\":\"string\"}]}}]}";
+
+ const char *schemas[] = {ts1, ts2, ts3, ts2, ts1, NULL};
+
+ int i=0;
+ for (i=0; schemas[i] != NULL ; i++) {
+
+ avro_value_t aobject = {0};
+ avro_schema_t aschema = {0};
+
+ msgpack_unpacked msg = test_init(&aobject, &aschema, schemas[i], AVRO_MULTILINE_JSON);
+
+ msgpack_object_print(stderr, msg.data);
+
+ flb_msgpack_to_avro(&aobject, &msg.data);
+
+ avro_value_t log0;
+ TEST_CHECK(avro_value_get_by_name(&aobject, "log", &log0, NULL) == 0);
+
+ size_t size1 = 0;
+ const char *log_line = NULL;
+ TEST_CHECK(avro_value_get_string(&log0, &log_line, &size1) == 0);
+ char *pre = "2020-08-21T15:49:48.154291375Z";
+ TEST_CHECK((strncmp(pre, log_line, strlen(pre)) == 0));
+ flb_info("log_line len:%zu:\n", strlen(log_line));
+
+ avro_value_t kubernetes0;
+ TEST_CHECK(avro_value_get_by_name(&aobject, "kubernetes", &kubernetes0, NULL) == 0);
+
+ avro_value_get_size(&kubernetes0, &size1);
+ flb_info("asize:%zu:\n", size1);
+ TEST_CHECK(size1 == 10);
+
+ avro_value_t pn;
+ TEST_CHECK(avro_value_get_by_name(&kubernetes0, "pod_name", &pn, NULL) == 0);
+
+ const char *pod_name = NULL;
+ size_t pod_name_size = 0;
+ TEST_CHECK(avro_value_get_string(&pn, &pod_name, &pod_name_size) == 0);
+ TEST_CHECK(strcmp(pod_name, "rrrr-bert-completion-tb1-6786c9c8-wj25m") == 0);
+ TEST_CHECK(pod_name[pod_name_size] == '\0');
+ TEST_CHECK(strlen(pod_name) == (pod_name_size-1));
+
+ avro_value_t nn;
+ TEST_CHECK(avro_value_get_by_name(&kubernetes0, "namespace_name", &nn, NULL) == 0);
+
+ const char *namespace_name = NULL;
+ size_t namespace_name_size = 0;
+ TEST_CHECK(avro_value_get_string(&nn, &namespace_name, &namespace_name_size) == 0);
+ TEST_CHECK(strcmp(namespace_name, "k8s-fgg") == 0);
+
+ avro_value_t mapX;
+ TEST_CHECK(avro_value_get_by_name(&kubernetes0, "annotations", &mapX, NULL) == 0);
+
+ avro_value_get_size(&mapX, &size1);
+ flb_info("asize:%zu:\n", size1);
+
+ TEST_CHECK(size1 == 5);
+
+ // check the first item in the map
+ avro_value_t doas;
+ TEST_CHECK(avro_value_get_by_name(&mapX, "doAs", &doas, NULL) == 0);
+ const char *doaser = NULL;
+ size_t doaser_size;
+ TEST_CHECK(avro_value_get_string(&doas, &doaser, &doaser_size) == 0);
+ TEST_CHECK((strcmp(doaser, "weeb") == 0));
+
+ // check the second item in the map
+ avro_value_t iddecorator;
+ TEST_CHECK(avro_value_get_by_name(&mapX, "iddecorator.dkdk.username", &iddecorator, NULL) == 0);
+ const char *idder = NULL;
+ size_t idder_size;
+ TEST_CHECK(avro_value_get_string(&iddecorator, &idder, &idder_size) == 0);
+ TEST_CHECK((strcmp(idder, "rrrr") == 0));
+
+ avro_schema_decref(aschema);
+ msgpack_unpacked_destroy(&msg);
+ avro_value_decref(&aobject);
+ }
+
+}
+
+// int msgpack2avro(avro_value_t *val, msgpack_object *o)
+// get a schema for a type like this:
+// http://avro.apache.org/docs/current/api/c/index.html#_examples
+// ../lib/msgpack-3.2.0/include/msgpack/pack.h
+// static int msgpack_pack_nil(msgpack_packer* pk);
+void test_msgpack2avro()
+{
+ avro_value_t aobject;
+ avro_schema_t schema = avro_schema_null();
+ avro_value_iface_t *aclass = avro_generic_class_from_schema(schema);
+ avro_generic_value_new(aclass, &aobject);
+
+ msgpack_sbuffer sbuf;
+ msgpack_packer pk;
+ msgpack_zone mempool;
+ msgpack_object deserialized;
+
+ /* msgpack::sbuffer is a simple buffer implementation. */
+ msgpack_sbuffer_init(&sbuf);
+
+ /* serialize values into the buffer using msgpack_sbuffer_write callback function. */
+ msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_nil(&pk);
+
+ /* deserialize the buffer into msgpack_object instance. */
+ /* deserialized object is valid during the msgpack_zone instance alive. */
+ msgpack_zone_init(&mempool, 2048);
+ msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized);
+
+ TEST_CHECK((msgpack2avro(&aobject, &deserialized) == FLB_TRUE));
+
+ msgpack_zone_destroy(&mempool);
+ msgpack_sbuffer_destroy(&sbuf);
+}
+const char JSON_SINGLE_MAP_001_SCHEMA_WITH_UNION[] =
+"{\"type\":\"record\",\
+ \"name\":\"Map001\",\
+ \"fields\":[\
+ {\"name\": \"key001\", \"type\": \"int\"},\
+ {\"name\": \"key002\", \"type\": \"float\"},\
+ {\"name\": \"key003\", \"type\": \"string\"},\
+ { \
+ \"name\": \"status\", \
+ \"default\": null, \
+ \"type\": [\"null\", \"string\"] \
+ }, \
+ {\"name\": \"key004\", \"type\":\
+ {\"type\": \"array\", \"items\":\
+ {\"type\": \"map\",\"values\": \"int\"}}}]}";
+void test_union_type_sanity()
+{
+ avro_value_t aobject;
+ avro_schema_t aschema;
+
+ msgpack_unpacked msg = test_init(&aobject, &aschema, JSON_SINGLE_MAP_001_SCHEMA_WITH_UNION, AVRO_SINGLE_MAP1);
+
+ msgpack_object_print(stderr, msg.data);
+ flb_msgpack_to_avro(&aobject, &msg.data);
+
+ size_t totalSize = 0;
+ avro_value_get_size(&aobject, &totalSize);
+ flb_info("totalSize:%zu:\n", totalSize);
+ // this is key001,2,3,4 and the status field which is the union type
+ TEST_CHECK(totalSize == 5);
+
+ avro_value_t test_value;
+ TEST_CHECK(avro_value_get_by_name(&aobject, "key001", &test_value, NULL) == 0);
+
+ int val001 = 0;
+ avro_value_get_int(&test_value, &val001);
+ TEST_CHECK(val001 == 123456789);
+
+ TEST_CHECK(avro_value_get_by_name(&aobject, "key002", &test_value, NULL) == 0);
+
+ float val002 = 0.0f;
+ // for some reason its rounding to this value
+ float val002_actual = 0.999888f;
+ avro_value_get_float(&test_value, &val002);
+ char str1[80];
+ char str2[80];
+ sprintf(str1, "%f", val002);
+ sprintf(str2, "%f", val002_actual);
+ flb_info("val002:%s:\n", str1);
+ flb_info("val002_actual:%s:\n", str2);
+ TEST_CHECK((strcmp(str1, str2) == 0));
+
+ TEST_CHECK(avro_value_get_by_name(&aobject, "key003", &test_value, NULL) == 0);
+ const char *val003 = NULL;
+ size_t val003_size = 0;
+ TEST_CHECK(avro_value_get_string(&test_value, &val003, &val003_size) == 0);
+ flb_info("val003_size:%zu:\n", val003_size);
+ TEST_CHECK(val003[val003_size] == '\0');
+
+ TEST_CHECK((strcmp(val003, "abcdefghijk") == 0));
+ // avro_value_get_by_name returns ths string length plus the NUL
+ TEST_CHECK(val003_size == 12);
+
+ TEST_CHECK(avro_value_get_by_name(&aobject, "key004", &test_value, NULL) == 0);
+
+ size_t asize = 0;
+ avro_value_get_size(&test_value, &asize);
+ flb_info("asize:%zu:\n", asize);
+
+ TEST_CHECK(asize == 2);
+
+ TEST_CHECK(avro_value_get_by_name(&aobject, "status", &test_value, NULL) == 0);
+
+ avro_value_decref(&aobject);
+ avro_schema_decref(aschema);
+ msgpack_unpacked_destroy(&msg);
+}
+
+void test_union_type_branches()
+{
+ avro_value_t aobject;
+ avro_schema_t aschema;
+
+ msgpack_unpacked mp = test_init(&aobject, &aschema, JSON_SINGLE_MAP_001_SCHEMA_WITH_UNION, AVRO_SINGLE_MAP1);
+
+ flb_msgpack_to_avro(&aobject, &mp.data);
+
+ avro_value_t test_value;
+ TEST_CHECK(avro_value_get_by_name(&aobject, "status", &test_value, NULL) == 0);
+ TEST_CHECK(avro_value_get_type(&test_value) == AVRO_UNION);
+
+ int discriminant = 0;
+ TEST_CHECK(avro_value_get_discriminant(&test_value, &discriminant) == 0);
+ TEST_CHECK(discriminant == -1);
+
+ avro_value_t branch;
+ TEST_CHECK(avro_value_get_current_branch(&test_value, &branch) != 0);
+
+ TEST_CHECK(avro_value_set_branch(&test_value, 0, &branch) == 0);
+ TEST_CHECK(avro_value_set_null(&branch) == 0);
+
+ TEST_CHECK(avro_value_get_null(&branch) == 0);
+
+ avro_value_decref(&aobject);
+ avro_schema_decref(aschema);
+ msgpack_unpacked_destroy(&mp);
+}
+TEST_LIST = {
+ /* Avro */
+ { "msgpack_to_avro_basic", test_unpack_to_avro},
+ { "test_parse_reordered_schema", test_parse_reordered_schema},
+ { "test_union_type_sanity", test_union_type_sanity},
+ { "test_union_type_branches", test_union_type_branches},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/aws/CMakeLists.txt b/fluent-bit/tests/internal/aws/CMakeLists.txt
new file mode 100644
index 00000000..1685c0e2
--- /dev/null
+++ b/fluent-bit/tests/internal/aws/CMakeLists.txt
@@ -0,0 +1,7 @@
+# AWS unit tests
+set(UNIT_TESTS_FILES
+ placeholder.c
+ )
+
+# Prepare list of unit tests
+prepare_unit_tests(flb-it-aws_ "${UNIT_TESTS_FILES}")
diff --git a/fluent-bit/tests/internal/aws/placeholder.c b/fluent-bit/tests/internal/aws/placeholder.c
new file mode 100644
index 00000000..133c9a32
--- /dev/null
+++ b/fluent-bit/tests/internal/aws/placeholder.c
@@ -0,0 +1,11 @@
+#include "../flb_tests_internal.h"
+
+static void test_placeholder()
+{
+ return;
+}
+
+TEST_LIST = {
+ { "placeholder" , test_placeholder },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/aws_compress.c b/fluent-bit/tests/internal/aws_compress.c
new file mode 100644
index 00000000..180a0985
--- /dev/null
+++ b/fluent-bit/tests/internal/aws_compress.c
@@ -0,0 +1,489 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_utils.h>
+#include <fluent-bit/flb_gzip.h>
+
+#include <fluent-bit/aws/flb_aws_compress.h>
+#include "flb_tests_internal.h"
+
+#define FLB_AWS_COMPRESS_TEST_TYPE_COMPRESS 1
+#define FLB_AWS_COMPRESS_TEST_TYPE_B64_TRUNCATE 2
+
+/* test case definition struct */
+struct flb_aws_test_case {
+ char* compression_keyword;
+ char* in_data;
+ char* expect_out_data_b64;
+ int expect_ret;
+};
+
+/* test loop function declarations */
+static unsigned char * base64_encode(const unsigned char *src, size_t len,
+ size_t *out_len);
+static unsigned char * base64_decode(const unsigned char *src, size_t len,
+ size_t *out_len);
+static void flb_aws_compress_general_test_cases(int test_type,
+ struct flb_aws_test_case *cases,
+ size_t max_out_len,
+ int(*decompress)(void *in_data,
+ size_t in_len,
+ void **out_data,
+ size_t *out_len));
+static void flb_aws_compress_test_cases(struct flb_aws_test_case *cases);
+static void flb_aws_compress_truncate_b64_test_cases__gzip_decode(
+ struct flb_aws_test_case *cases,
+ size_t max_out_len);
+
+/** ------ Test Cases ------ **/
+void test_compression_gzip()
+{
+ struct flb_aws_test_case cases[] =
+ {
+ {
+ "gzip",
+ "hello hello hello hello hello hello",
+ "H4sIAAAAAAAA/8tIzcnJV8jARwIAVzdihSMAAAA=",
+ 0
+ },
+ { 0 }
+ };
+
+ flb_aws_compress_test_cases(cases);
+}
+
+void test_b64_truncated_gzip()
+{
+struct flb_aws_test_case cases[] =
+ {
+ {
+ "gzip",
+ "hello hello hello hello hello hello",
+ "hello hello hello hello hello hello", /* Auto decoded via gzip */
+ 0 /* Expected ret */
+ },
+ { 0 }
+ };
+
+ flb_aws_compress_truncate_b64_test_cases__gzip_decode(cases,
+ 41);
+}
+
+void test_b64_truncated_gzip_truncation()
+{
+struct flb_aws_test_case cases[] =
+ {
+ {
+ "gzip",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp"
+ "or incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, qui"
+ "s nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequ"
+ "at. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum do"
+ "lore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proiden"
+ "t, sunt in culpa qui officia deserunt mollit anim id est laborum. xyz",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp"
+ "or incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, qui"
+ "s nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequ"
+ "at. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum do"
+ "lore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proiden"
+ "t, su[Truncated...]"
+ /*"nt in culpa qui officia deserunt mollit anim id est laborum. xyz",*/
+ "",
+ 0 /* Expected ret */
+ },
+ {
+ "gzip",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp"
+ "or incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, qui"
+ "s nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequ"
+ "at. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum do"
+ "lore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proiden"
+ "t, sunt in culpa qui officia deserunt mollit anim id est laborum.",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp"
+ "or incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, qui"
+ "s nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequ"
+ "at. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum do"
+ "lore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proiden"
+ "t, sunt in culpa qui officia deserunt mollit anim id est laborum.",
+ 0 /* Expected ret */
+ },
+ { 0 }
+ };
+
+ flb_aws_compress_truncate_b64_test_cases__gzip_decode(cases,
+ 381);
+}
+
+void test_b64_truncated_gzip_truncation_buffer_too_small()
+{
+struct flb_aws_test_case cases[] =
+ {
+ {
+ "gzip",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp"
+ "or incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, qui"
+ "s nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequ"
+ "at. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum do"
+ "lore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proiden"
+ "t, sunt in culpa qui officia deserunt mollit anim id est laborum.",
+ "",
+ -1 /* Expected ret */
+ },
+ {
+ "gzip",
+ "",
+ "",
+ -1 /* Expected ret: Buffer too small */
+ },
+ { 0 }
+ };
+
+ flb_aws_compress_truncate_b64_test_cases__gzip_decode(cases,
+ 14);
+}
+
+void test_b64_truncated_gzip_truncation_edge()
+{
+struct flb_aws_test_case cases[] =
+ {
+ /*{
+ "gzip",
+ "",
+ "",
+ 0
+ }, *//* This test case fails, because GZIP can zip empty strings but not unzip */
+ {
+ "gzip",
+ "[Truncated...]", /* Endless loop? */
+ "",
+ -1 /* Expected ret */
+ },
+ { 0 }
+ };
+
+ flb_aws_compress_truncate_b64_test_cases__gzip_decode(cases,
+ 51);
+}
+
+void test_b64_truncated_gzip_truncation_multi_rounds()
+{
+struct flb_aws_test_case cases[] =
+ {
+ {
+ "gzip",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp"
+ "or incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, qui"
+ "s nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequ"
+ "at. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum do"
+ "lore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proiden"
+ "t, sunt in culpa qui officia deserunt mollit anim id est laborum."
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "", /* First half of the compression is heavy, the second half is light. */
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp"
+ "or incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, qui"
+ "s nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequ"
+ "at. Duis aute irure dolor in reprehenderit in voluptate velit es"
+ "[Truncated...]", /* Bad estimation of resizing, 3 truncation iterations
+ * needed */
+ 0 /* Expected ret */
+ },
+ { 0 }
+ };
+
+ flb_aws_compress_truncate_b64_test_cases__gzip_decode(cases,
+ 300);
+}
+
+TEST_LIST = {
+ { "test_compression_gzip", test_compression_gzip },
+ { "test_b64_truncated_gzip", test_b64_truncated_gzip },
+ { "test_b64_truncated_gzip_truncation", test_b64_truncated_gzip_truncation },
+ { "test_b64_truncated_gzip_truncation_buffer_too_small",
+ test_b64_truncated_gzip_truncation_buffer_too_small },
+ { "test_b64_truncated_gzip_truncation_edge",
+ test_b64_truncated_gzip_truncation_edge },
+ { "test_b64_truncated_gzip_truncation_multi_rounds",
+ test_b64_truncated_gzip_truncation_multi_rounds },
+ { 0 }
+};
+
+/** ------ Helper Methods ------ **/
+
+/* test case loop for flb_aws_compress */
+static void flb_aws_compress_test_cases(struct flb_aws_test_case *cases)
+{
+ flb_aws_compress_general_test_cases(FLB_AWS_COMPRESS_TEST_TYPE_COMPRESS,
+ cases, 0, NULL);
+}
+
+/* test case loop for flb_aws_compress */
+static void flb_aws_compress_truncate_b64_test_cases__gzip_decode(
+ struct flb_aws_test_case *cases,
+ size_t max_out_len)
+{
+ flb_aws_compress_general_test_cases(FLB_AWS_COMPRESS_TEST_TYPE_B64_TRUNCATE,
+ cases, max_out_len, &flb_gzip_uncompress);
+}
+
+/* General test case loop flb_aws_compress */
+static void flb_aws_compress_general_test_cases(int test_type,
+ struct flb_aws_test_case *cases,
+ size_t max_out_len,
+ int(*decompress)(void *in_data,
+ size_t in_len,
+ void **out_data,
+ size_t *out_len))
+{
+ int ret;
+ size_t len;
+ int compression_type = FLB_AWS_COMPRESS_NONE;
+ unsigned char* out_data;
+ size_t out_data_len;
+ unsigned char* out_data_b64;
+ size_t out_data_b64_len;
+ struct flb_config *config;
+ struct flb_aws_test_case *tcase = cases;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ flb_config_exit(config);
+
+ while (tcase->compression_keyword != 0) {
+
+ size_t in_data_len = strlen(tcase->in_data);
+ compression_type = flb_aws_compression_get_type(tcase->compression_keyword);
+
+ TEST_CHECK(compression_type != -1);
+ TEST_MSG("| flb_aws_get_compression_type: failed to get compression type for "
+ "keyword "
+ "%s", tcase->compression_keyword);
+
+ if (test_type == FLB_AWS_COMPRESS_TEST_TYPE_COMPRESS) {
+ ret = flb_aws_compression_compress(compression_type, (void *) tcase->in_data,
+ in_data_len, (void **) &out_data,
+ &out_data_len);
+ }
+ else {
+ ret = flb_aws_compression_b64_truncate_compress(compression_type, max_out_len,
+ (void *) tcase->in_data,
+ in_data_len,
+ (void **) &out_data,
+ &out_data_len);
+ }
+
+ TEST_CHECK(ret == tcase->expect_ret);
+ TEST_MSG("| Expected return value: %i", tcase->expect_ret);
+ TEST_MSG("| Produced return value: %i", ret);
+
+ if (ret != 0) {
+ TEST_MSG("*- For input data: %s", tcase->in_data);
+ ++tcase;
+ continue;
+ }
+
+ if (test_type == FLB_AWS_COMPRESS_TEST_TYPE_COMPRESS) {
+ out_data_b64 = base64_encode(out_data, out_data_len, &out_data_b64_len);
+ /* remove newline character which is a part of this encode algo */
+ --out_data_b64_len;
+ flb_free(out_data);
+ out_data = NULL;
+ }
+ else {
+ /* decode b64 so we can compare plain text */
+ out_data_b64 = base64_decode(out_data, out_data_len, &out_data_b64_len);
+ flb_free(out_data);
+ out_data = out_data_b64;
+ out_data_len = out_data_b64_len;
+ ret = decompress(out_data, out_data_len, (void *)&out_data_b64,
+ &out_data_b64_len);
+ flb_free(out_data);
+ out_data = NULL;
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("| Decompression failure");
+ out_data_b64 = flb_malloc(1); /* placeholder malloc */
+ }
+ }
+
+ ret = memcmp(tcase->expect_out_data_b64, out_data_b64, out_data_b64_len);
+ TEST_CHECK(ret == 0);
+ TEST_MSG("| Expected output(%s): %s",
+ (test_type == FLB_AWS_COMPRESS_TEST_TYPE_COMPRESS)
+ ? "b64" : "decompressed", tcase->expect_out_data_b64);
+ TEST_MSG("| Produced output(%s): %s",
+ (test_type == FLB_AWS_COMPRESS_TEST_TYPE_COMPRESS)
+ ? "b64" : "decompressed", out_data_b64);
+
+ len = strlen(tcase->expect_out_data_b64);
+ TEST_CHECK(len == out_data_b64_len);
+ TEST_MSG("| Expected length: %zu", len);
+ TEST_MSG("| Produced length: %zu", out_data_b64_len);
+
+ TEST_MSG("*- For input data: %s", tcase->in_data);
+
+ flb_free(out_data_b64);
+ ++tcase;
+ }
+}
+
+/* B64 check script copied from Monkey Auth Plugin */
+/* Change log:
+ * Removed auto new line entry from every 72 characters to make consistant with
+ * the actual base64 conversion
+ */
+/* Copied from monkey/plugins/auth/base64.c */
+
+#define __mem_alloc malloc
+#define __mem_free free
+
+static const unsigned char base64_table[65] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * base64_encode - Base64 encode
+ * @src: Data to be encoded
+ * @len: Length of the data to be encoded
+ * @out_len: Pointer to output length variable, or %NULL if not used
+ * Returns: Allocated buffer of out_len bytes of encoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer. Returned buffer is
+ * nul terminated to make it easier to use as a C string. The nul terminator is
+ * not included in out_len.
+ */
+static unsigned char * base64_encode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ unsigned char *out, *pos;
+ const unsigned char *end, *in;
+ size_t olen;
+ size_t line_len;
+
+ olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
+ olen += olen / 72; /* line feeds */
+ olen++; /* nul termination */
+ if (olen < len)
+ return NULL; /* integer overflow */
+
+ out = __mem_alloc(olen);
+
+ if (out == NULL)
+ return NULL;
+
+ end = src + len;
+ in = src;
+ pos = out;
+ line_len = 0;
+ while (end - in >= 3) {
+ *pos++ = base64_table[in[0] >> 2];
+ *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
+ *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
+ *pos++ = base64_table[in[2] & 0x3f];
+ in += 3;
+ line_len += 4;
+ }
+
+ if (end - in) {
+ *pos++ = base64_table[in[0] >> 2];
+ if (end - in == 1) {
+ *pos++ = base64_table[(in[0] & 0x03) << 4];
+ *pos++ = '=';
+ } else {
+ *pos++ = base64_table[((in[0] & 0x03) << 4) |
+ (in[1] >> 4)];
+ *pos++ = base64_table[(in[1] & 0x0f) << 2];
+ }
+ *pos++ = '=';
+ line_len += 4;
+ }
+
+ if (line_len)
+ *pos++ = '\n';
+
+ *pos = '\0';
+ if (out_len)
+ *out_len = pos - out;
+ return out;
+}
+
+/**
+ * base64_decode - Base64 decode
+ * @src: Data to be decoded
+ * @len: Length of the data to be decoded
+ * @out_len: Pointer to output length variable
+ * Returns: Allocated buffer of out_len bytes of decoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer.
+ */
+static unsigned char * base64_decode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ unsigned char dtable[256], *out, *pos, block[4], tmp;
+ size_t i, count, olen;
+ int pad = 0;
+
+ memset(dtable, 0x80, 256);
+ for (i = 0; i < sizeof(base64_table) - 1; i++)
+ dtable[base64_table[i]] = (unsigned char) i;
+ dtable['='] = 0;
+
+ count = 0;
+ for (i = 0; i < len; i++) {
+ if (dtable[src[i]] != 0x80)
+ count++;
+ }
+
+ if (count == 0 || count % 4)
+ return NULL;
+
+ olen = (count / 4 * 3) + 1;
+ pos = out = __mem_alloc(olen);
+ if (out == NULL)
+ return NULL;
+
+ count = 0;
+ for (i = 0; i < len; i++) {
+ tmp = dtable[src[i]];
+ if (tmp == 0x80)
+ continue;
+
+ if (src[i] == '=')
+ pad++;
+ block[count] = tmp;
+ count++;
+ if (count == 4) {
+ *pos++ = (block[0] << 2) | (block[1] >> 4);
+ *pos++ = (block[1] << 4) | (block[2] >> 2);
+ *pos++ = (block[2] << 6) | block[3];
+ count = 0;
+ if (pad) {
+ if (pad == 1)
+ pos--;
+ else if (pad == 2)
+ pos -= 2;
+ else {
+ /* Invalid padding */
+ __mem_free(out);
+ return NULL;
+ }
+ break;
+ }
+ }
+ }
+ *pos = '\0';
+
+ *out_len = pos - out;
+ return out;
+}
+
+/* End of copied base64.c from monkey */
diff --git a/fluent-bit/tests/internal/aws_credentials.c b/fluent-bit/tests/internal/aws_credentials.c
new file mode 100644
index 00000000..2f8a670e
--- /dev/null
+++ b/fluent-bit/tests/internal/aws_credentials.c
@@ -0,0 +1,382 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_aws_credentials.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_info.h>
+
+#include "flb_tests_internal.h"
+
+#define ACCESS_KEY "akid"
+#define SECRET_KEY "skid"
+#define TOKEN "token"
+
+/* Credentials Environment Variables */
+#define AWS_ACCESS_KEY_ID "AWS_ACCESS_KEY_ID"
+#define AWS_SECRET_ACCESS_KEY "AWS_SECRET_ACCESS_KEY"
+#define AWS_SESSION_TOKEN "AWS_SESSION_TOKEN"
+
+
+static void unsetenv_credentials()
+{
+ int ret;
+
+ ret = unsetenv(AWS_ACCESS_KEY_ID);
+ if (ret < 0) {
+ flb_errno();
+ return;
+ }
+ ret = unsetenv(AWS_SECRET_ACCESS_KEY);
+ if (ret < 0) {
+ flb_errno();
+ return;
+ }
+ ret = unsetenv(AWS_SESSION_TOKEN);
+ if (ret < 0) {
+ flb_errno();
+ return;
+ }
+}
+
+/* test for the env provider */
+static void test_environment_provider()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ /* set environment */
+ ret = setenv(AWS_ACCESS_KEY_ID, ACCESS_KEY, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SECRET_ACCESS_KEY, SECRET_KEY, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SESSION_TOKEN, TOKEN, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_aws_env_provider_create();
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ unsetenv_credentials();
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+/* token is not required */
+static void test_environment_provider_no_token()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ /* set environment */
+ ret = setenv(AWS_ACCESS_KEY_ID, ACCESS_KEY, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SECRET_ACCESS_KEY, SECRET_KEY, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_aws_env_provider_create();
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(creds->session_token == NULL);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(creds->session_token == NULL);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ unsetenv_credentials();
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+/* access and secret key are required */
+static void test_environment_provider_only_access()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ struct flb_config *config;
+ int ret;
+
+ unsetenv_credentials();
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ /* set environment */
+ ret = setenv(AWS_ACCESS_KEY_ID, ACCESS_KEY, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_aws_env_provider_create();
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return -1 (failure) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret < 0);
+
+ unsetenv_credentials();
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+/* test the env provider when no cred env vars are set */
+static void test_environment_provider_unset()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ unsetenv_credentials();
+
+ provider = flb_aws_env_provider_create();
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* refresh should return -1 (failure) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret < 0);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+static void test_credential_expiration()
+{
+ struct tm tm = {0};
+ /* one hour in the future */
+ time_t exp_expected = time(NULL) + 3600;
+ char time_stamp[50];
+ time_t exp_actual;
+ TEST_CHECK(gmtime_r(&exp_expected, &tm) != NULL);
+
+ TEST_CHECK(strftime(time_stamp, 50, "%Y-%m-%dT%H:%M:%SZ", &tm) > 0);
+
+ exp_actual = flb_aws_cred_expiration(time_stamp);
+
+ TEST_CHECK(exp_actual == exp_expected);
+}
+
+static void test_standard_chain_provider()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ /* set environment */
+ ret = setenv(AWS_ACCESS_KEY_ID, ACCESS_KEY, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SECRET_ACCESS_KEY, SECRET_KEY, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SESSION_TOKEN, TOKEN, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_standard_chain_provider_create(config, NULL, "us-west-2",
+ "https://sts.us-west-2.amazonaws.com",
+ NULL,
+ flb_aws_client_generator(),
+ NULL);
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ unsetenv_credentials();
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+TEST_LIST = {
+ { "test_credential_expiration" , test_credential_expiration},
+ { "environment_credential_provider" , test_environment_provider},
+ { "environment_provider_no_token" , test_environment_provider_no_token},
+ { "environment_provider_only_access_key" ,
+ test_environment_provider_only_access},
+ { "environment_credential_provider_unset" ,
+ test_environment_provider_unset},
+ { "test_standard_chain_provider" , test_standard_chain_provider},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/aws_credentials_ec2.c b/fluent-bit/tests/internal/aws_credentials_ec2.c
new file mode 100644
index 00000000..6370b156
--- /dev/null
+++ b/fluent-bit/tests/internal/aws_credentials_ec2.c
@@ -0,0 +1,1037 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include "../include/aws_client_mock.h"
+#include "../include/aws_client_mock.c"
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_aws_credentials.h>
+#include <fluent-bit/aws/flb_aws_imds.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_http_client.h>
+#include <fluent-bit/flb_aws_util.h>
+#include <fluent-bit/flb_log.h>
+
+#include <monkey/mk_core.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "flb_tests_internal.h"
+
+/* Global variables for tests */
+struct flb_aws_provider *provider;
+struct flb_aws_credentials *creds;
+struct flb_config *config;
+struct flb_config *config_fluent;
+int ret;
+
+/*
+ * Hardcoding a copy of the ec2 credential provider struct from flb_aws_credentials_ec2.c
+ * Note: this will require a change if the other copy is changed.
+ * A provider that obtains credentials from EC2 IMDS.
+ */
+struct flb_aws_provider_ec2 {
+ struct flb_aws_credentials *creds;
+ time_t next_refresh;
+
+ /* upstream connection to IMDS */
+ struct flb_aws_client *client;
+
+ /* IMDS interface */
+ struct flb_aws_imds *imds_interface;
+};
+
+/*
+ * Setup test & Initialize test environment
+ * Required for fluent bit debug logs
+ * Note: Log level definitions do not work.
+ * Example: config_fluent->verbose = FLB_LOG_OFF
+ */
+void setup_test(struct flb_aws_client_mock_request_chain *request_chain) {
+ /* Initialize test environment */
+ config_fluent = flb_config_init();
+
+ flb_aws_client_mock_configure_generator(request_chain);
+
+ /* Init provider */
+ config = flb_calloc(1, sizeof(struct flb_config));
+ TEST_ASSERT(config != NULL);
+ mk_list_init(&config->upstreams);
+ provider = flb_ec2_provider_create(config, flb_aws_client_get_mock_generator());
+ TEST_ASSERT(provider != NULL);
+}
+
+/* Test clean up */
+void cleanup_test() {
+ flb_aws_client_mock_destroy_generator();
+ if (provider != NULL) {
+ ((struct flb_aws_provider_ec2 *) (provider->implementation))->client = NULL;
+ flb_aws_provider_destroy(provider);
+ provider = NULL;
+ }
+ if (config != NULL) {
+ flb_free(config);
+ }
+ if (config_fluent != NULL) {
+ flb_config_exit(config_fluent);
+ config_fluent = NULL;
+ }
+}
+
+/*
+ * IMDSv2 -- Test Summary
+ * First call to get_credentials():
+ * -> 2 requests are made to obtain IMDSv2 token
+ * -> 2 requests are made to access credentials
+ * Second call to get_credentials() hits cache:
+ * -> 0 requests are made
+ * refresh():
+ * -> 2 requests are made to access credentials
+ */
+static void test_ec2_provider_v2()
+{
+ setup_test(FLB_AWS_CLIENT_MOCK(
+ /* First call to get_credentials() */
+ response(
+ expect(URI, "/"),
+ expect(HEADER, "X-aws-ec2-metadata-token", "INVALID"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 401)
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ set(STATUS, 200),
+ set(PAYLOAD, "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(PAYLOAD_SIZE, 56)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n"
+ " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"XACCESSEC2XXX\",\n \"SecretAccessKey\""
+ " : \"XSECRETEC2XXXXXXXXXXXXXX\",\n \"Token\" : \"XTOKENEC2XXXXXXXXXXXXXXX==\",\n"
+ " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), /* Expires Year 3021 */
+ set(PAYLOAD_SIZE, 257)
+ ),
+
+ /* Second call to get_credentials() hits cache */
+
+ /* Refresh credentials (No token refesh) */
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name_New"),
+ set(PAYLOAD_SIZE, 20)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name_New"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n"
+ " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"YACCESSEC2XXX\",\n \"SecretAccessKey\""
+ " : \"YSECRETEC2XXXXXXXXXXXXXX\",\n \"Token\" : \"YTOKENEC2XXXXXXXXXXXXXXX==\",\n"
+ " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), // Expires Year 3021
+ set(PAYLOAD_SIZE, 257)
+ )
+ ));
+
+ /* Repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Retrieve from cache */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ /* Retrieve refreshed credentials from cache */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("YACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("YSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("YTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Check we have exhausted our response list */
+ TEST_CHECK(flb_aws_client_mock_generator_count_unused_requests() == 0);
+
+ cleanup_test();
+}
+
+/*
+ * IMDSv1 -- Fallback Test Summary
+ * First call to get_credentials():
+ * -> 1 requests is made to test for IMDSv2
+ * -> 2 requests are made to access credentials
+ * Second call to get_credentials() hits cache
+ * -> 0 requests are made
+ * refresh():
+ * -> 2 requests are made to access credentials
+ */
+static void test_ec2_provider_v1()
+{
+ setup_test(FLB_AWS_CLIENT_MOCK(
+ /* First call to get_credentials() */
+ response(
+ expect(URI, "/"),
+ expect(HEADER, "X-aws-ec2-metadata-token", "INVALID"),
+ expect(HEADER_COUNT, 1),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 200)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER_COUNT, 0),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER_COUNT, 0),
+ set(STATUS, 200),
+ set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n"
+ " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"XACCESSEC2XXX\",\n \"SecretAccessKey\""
+ " : \"XSECRETEC2XXXXXXXXXXXXXX\",\n \"Token\" : \"XTOKENEC2XXXXXXXXXXXXXXX==\",\n"
+ " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), /* Expires Year 3021 */
+ set(PAYLOAD_SIZE, 257)
+ ),
+
+ /* Second call to get_credentials() hits cache */
+
+ /* Refresh credentials (No token refesh) */
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER_COUNT, 0),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name_New"),
+ set(PAYLOAD_SIZE, 20)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name_New"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER_COUNT, 0),
+ set(STATUS, 200),
+ set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n"
+ " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"YACCESSEC2XXX\",\n \"SecretAccessKey\""
+ " : \"YSECRETEC2XXXXXXXXXXXXXX\",\n \"Token\" : \"YTOKENEC2XXXXXXXXXXXXXXX==\",\n"
+ " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), // Expires Year 3021
+ set(PAYLOAD_SIZE, 257)
+ )
+ ));
+
+ /* Repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Retrieve from cache */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ /* Retrieve refreshed credentials from cache */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("YACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("YSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("YTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Check we have exhausted our response list */
+ TEST_CHECK(flb_aws_client_mock_generator_count_unused_requests() == 0);
+
+ cleanup_test();
+}
+
+/*
+ * IMDSv1 -- IMDSv2 Timeout, Fallback Test Summary
+ * First call to get_credentials():
+ * -> 1 requests is made to test for IMDSv2 (IMDSv2)
+ * -> 1 request made to get token (Timeout failure)
+ * -> 1 request made to check IMDSv1 fallback (Failure - IMDSv1 not allowed)
+ *
+ * Second call to get_credentials():
+ * -> 1 requests is made to test for IMDSv2 (IMDSv2)
+ * -> 1 request made to get token (Timeout failure)
+ * -> 1 request made to check IMDSv1 fallback (Success)
+ * -> 2 requests are made to access credentials
+ * Second call to get_credentials() hits cache
+ * -> 0 requests are made
+ * refresh():
+ * -> 2 requests are made to access credentials
+ */
+static void test_ec2_provider_v1_v2_timeout()
+{
+ setup_test(FLB_AWS_CLIENT_MOCK(
+ /* First call to get_credentials() */
+ response(
+ expect(URI, "/"),
+ expect(HEADER, "X-aws-ec2-metadata-token", "INVALID"),
+ expect(HEADER_COUNT, 1),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 401)
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ config(REPLACE, (struct flb_http_client *) NULL) /* Replicate timeout failure */
+ ),
+ response(
+ expect(URI, "/"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 401) /* IMDSv1 not allowed */
+ ),
+
+ /* Second call to get_credentials() */
+ response(
+ expect(URI, "/"),
+ expect(HEADER, "X-aws-ec2-metadata-token", "INVALID"),
+ expect(HEADER_COUNT, 1),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 401)
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ config(REPLACE, (struct flb_http_client *) NULL) /* Replicate timeout failure */
+ ),
+ response(
+ expect(URI, "/"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 200) /* IMDSv1 is allowed */
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER_COUNT, 0),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER_COUNT, 0),
+ set(STATUS, 200),
+ set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n"
+ " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"XACCESSEC2XXX\",\n \"SecretAccessKey\""
+ " : \"XSECRETEC2XXXXXXXXXXXXXX\",\n \"Token\" : \"XTOKENEC2XXXXXXXXXXXXXXX==\",\n"
+ " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), /* Expires Year 3021 */
+ set(PAYLOAD_SIZE, 257)
+ ),
+
+ /* Second call to get_credentials() hits cache */
+
+ /* Refresh credentials (No token refesh) */
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER_COUNT, 0),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name_New"),
+ set(PAYLOAD_SIZE, 20)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name_New"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER_COUNT, 0),
+ set(STATUS, 200),
+ set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n"
+ " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"YACCESSEC2XXX\",\n \"SecretAccessKey\""
+ " : \"YSECRETEC2XXXXXXXXXXXXXX\",\n \"Token\" : \"YTOKENEC2XXXXXXXXXXXXXXX==\",\n"
+ " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), // Expires Year 3021
+ set(PAYLOAD_SIZE, 257)
+ )
+ ));
+
+ /* First call: IMDSv1 and IMDSv2 not accessible */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds == NULL);
+
+ /*
+ * Second call: IMDSv2 timeout, IMDSv1 accessible
+ * Repeated calls to get credentials should return the same set
+ */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Retrieve from cache */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ /* Retrieve refreshed credentials from cache */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("YACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("YSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("YTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Check we have exhausted our response list */
+ TEST_CHECK(flb_aws_client_mock_generator_count_unused_requests() == 0);
+
+ cleanup_test();
+}
+
+
+/*
+ * IMDS Version Detection Error -- Test Summary
+ * First call to get_credentials():
+ * -> 1 request made to test for IMDSv2 (Fails, 404)
+ * Second call to get_credentials():
+ * -> 1 request made to test for IMDSv2 (Fails, null http_client)
+ * Third call to get_credentials():
+ * -> 1 request made to test for IMDSv2 (Success)
+ * -> 1 request made to get token (Failure)
+ * Fourth call to get_credentials():
+ * -> 2 requests made to obtain IMDSv2 token (Success)
+ * -> 2 requests made to access credentials (Success)
+ */
+static void test_ec2_provider_version_detection_error()
+{
+ setup_test(FLB_AWS_CLIENT_MOCK(
+ /* First call to get_credentials(): Version detection failure */
+ response(
+ expect(URI, "/"),
+ expect(HEADER, "X-aws-ec2-metadata-token", "INVALID"),
+ expect(HEADER_COUNT, 1),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 404) // IMDS not found (not likely to happen)
+ ),
+
+ /* Second call to get_credentials(): Version detection failure */
+ response(
+ expect(URI, "/"),
+ expect(HEADER, "X-aws-ec2-metadata-token", "INVALID"),
+ expect(METHOD, FLB_HTTP_GET),
+ config(REPLACE, (struct flb_http_client *) NULL)
+ ),
+
+ /* Third call to get_credentials(): Version detection success */
+ response(
+ expect(URI, "/"),
+ expect(HEADER, "X-aws-ec2-metadata-token", "INVALID"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 401)
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ set(STATUS, 200),
+ set(PAYLOAD, "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(PAYLOAD_SIZE, 56)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n"
+ " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"XACCESSEC2XXX\",\n \"SecretAccessKey\""
+ " : \"XSECRETEC2XXXXXXXXXXXXXX\",\n \"Token\" : \"XTOKENEC2XXXXXXXXXXXXXXX==\",\n"
+ " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), /* Expires Year 3021 */
+ set(PAYLOAD_SIZE, 257)
+ )
+ ));
+
+ /* Version detection failure: Status 404 */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* Version detection failure: NULL response */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* Version detection success: IMDSv2 */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Retrieve from cache */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Check we have exhausted our response list */
+ TEST_CHECK(flb_aws_client_mock_generator_count_unused_requests() == 0);
+
+ cleanup_test();
+}
+
+/*
+ * IMDS Aquire Token Error -- Test Summary
+ * First call to get_credentials():
+ * -> 1 request made to test for IMDSv2 (Success)
+ * -> 1 request made to obtain IMDSv2 token (Fails) <-* Aquire token error
+ * -> 1 request made to check IMDSv1 fallback (Unauthorized)
+ * Second call to get_credentials():
+ * -> 1 request made to access instance name (Invalid token)
+ * -> 1 request made to obtain IMDSv2 token (Success)
+ * -> 1 request made to access instance name (Success)
+ * -> 1 request made to access credentials (Invalid token)
+ * -> 1 request made to obtain IMDSv2 token (Fails) <-* Aquire token error
+ * Third call to get_credentials():
+ * -> 1 request made to access instance name (Invalid token)
+ * -> 1 request made to obtain IMDSv2 token (Success)
+ * -> 1 request made to access instance name (Success)
+ * -> 1 request made to access credentials (Invalid token)
+ * -> 1 request made to obtain IMDSv2 token (Success)
+ * -> 1 request made to access credentials (Success, but credentials are expired as of Year 2000)
+ * Fourth call to get_credentials(): - hits expired cache
+ * -> 1 request made to access credentials (Invalid token)
+ * -> 1 request made to obtain IMDSv2 token (http_client is null) <-* Aquire token error
+ * Fifth call to get_credentials(): - hits expired cache
+ * -> 1 request made to access credentials (Invalid token)
+ * -> 1 request made to obtain IMDSv2 token (Success)
+ * -> 2 requests are made to access credentials
+ */
+static void test_ec2_provider_acquire_token_error()
+{
+ setup_test(FLB_AWS_CLIENT_MOCK(
+
+ /*
+ * First call to get_credentials():
+ * -> 1 request made to test for IMDSv2 (Success)
+ * -> 1 request made to obtain IMDSv2 token (Fails) <-* Aquire token error
+ * -> 1 request made to check IMDSv1 fallback (Unauthorized)
+ */
+ response(
+ expect(URI, "/"),
+ expect(HEADER, "X-aws-ec2-metadata-token", "INVALID"), /* Why is this not invalid_token? */
+ expect(HEADER_COUNT, 1),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 401) /* IMDSv2 */
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ config(REPLACE, NULL) /* HTTP Client is null */
+ ),
+ response(
+ expect(URI, "/"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 401) /* IMDSv1 not allowed */
+ ),
+
+ /*
+ * Second call to get_credentials():
+ * -> 1 request made to test for IMDSv2 (Success)
+ * -> 1 request made to obtain IMDSv2 token (Success) <-* Bad token
+ * -> 1 request made to access instance name (Invalid token)
+ * -> 1 request made to obtain IMDSv2 token (Success)
+ * -> 1 request made to access instance name (Success)
+ * -> 1 request made to access credentials (Invalid token)
+ * -> 1 request made to obtain IMDSv2 token (Fails) <-* Aquire token error
+ */
+ response(
+ expect(URI, "/"),
+ expect(HEADER, "X-aws-ec2-metadata-token", "INVALID"), /* Why is this not invalid_token? */
+ expect(HEADER_COUNT, 1),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 401) /* IMDSv2 */
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ set(STATUS, 200),
+ set(PAYLOAD, "BAD_ANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(PAYLOAD_SIZE, 56)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "BAD_ANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="), /* Token failed to be set */
+ set(STATUS, 401) /* Unauthorized, bad token */
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ set(STATUS, 200),
+ set(PAYLOAD, "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(PAYLOAD_SIZE, 56)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 401) /* Unauthorized, bad token */
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ set(STATUS, 404), /* This should never actually happen */
+ set(PAYLOAD, "Token not found"),
+ set(PAYLOAD_SIZE, 15)
+ ),
+
+ /*
+ * Third call to get_credentials():
+  * -> 1 request made to access instance name (Invalid token)
+  * -> 1 request made to obtain IMDSv2 token (Success)
+  * -> 1 request made to access instance name (Success)
+  * -> 1 request made to access credentials (Invalid token)
+  * -> 1 request made to obtain IMDSv2 token (Success)
+  * -> 1 request made to access credentials (Success, but credentials are expired as of Year 2000)
+ */
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="), /* Token failed to be set */
+ set(STATUS, 401) /* Unauthorized, bad token */
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ set(STATUS, 200),
+ set(PAYLOAD, "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(PAYLOAD_SIZE, 56)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 401) /* Unauthorized, bad token */
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ set(STATUS, 200),
+ set(PAYLOAD, "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(PAYLOAD_SIZE, 56)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n"
+ " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"XACCESSEC2XXX\",\n \"SecretAccessKey\""
+ " : \"XSECRETEC2XXXXXXXXXXXXXX\",\n \"Token\" : \"XTOKENEC2XXXXXXXXXXXXXXX==\",\n"
+ " \"Expiration\" : \"2000-09-17T00:41:00Z\"\n}"), /* Expires Year 2000 */
+ set(PAYLOAD_SIZE, 257)
+ ),
+
+ /*
+ * Fourth call to get_credentials(): - hits expired cache
+ * -> 1 request made to access credentials (Invalid token)
+ * -> 1 request made to obtain IMDSv2 token (http_client is null) <-* Aquire token error
+ */
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 401) /* Unauthorized, bad token */
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ config(REPLACE, NULL) /* HTTP Client is null */
+ ),
+
+ /*
+ * Fifth call to get_credentials(): - hits expired cache
+ * -> 1 request made to access credentials (Invalid token)
+ * -> 1 request made to obtain IMDSv2 token (Success)
+ * -> 2 requests are made to access credentials
+ */
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 401) /* Unauthorized, bad token */
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ set(STATUS, 200),
+ set(PAYLOAD, "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(PAYLOAD_SIZE, 56)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n"
+ " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"YACCESSEC2XXX\",\n \"SecretAccessKey\""
+ " : \"YSECRETEC2XXXXXXXXXXXXXX\",\n \"Token\" : \"YTOKENEC2XXXXXXXXXXXXXXX==\",\n"
+ " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), /* Expires Year 3021 */
+ set(PAYLOAD_SIZE, 257)
+ )
+ ));
+
+ /* 1. Aquire token error */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* 2. Aquire token error */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* 3. Aquire token success */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* 4. Aquire token error */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds); /* Remains unchanged */
+
+ /* 5. Aquire token success */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("YACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("YSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("YTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Check we have exhausted our response list */
+ TEST_CHECK(flb_aws_client_mock_generator_count_unused_requests() == 0);
+
+ cleanup_test();
+}
+
+/*
+ * IMDS Metadata Request Failure -- Test Summary
+ * First call to get_credentials():
+ * -> 2 requests are made to obtain IMDSv2 token
+ * -> 1 request is made to access instance name (fails, null client)
+ * Second call to get_credentials(): -- token cached
+ * -> 1 request is made to access instance name (success)
+ * -> 1 request is made to access credentials (fails, 404)
+ * Third call to get_credentials(): -- token cached
+ * -> 1 request is made to access instance name
+ * -> 1 request is made to access credentials (fails, garbage fuzz input)
+ * Fourth call to get_credentials(): -- token cached
+ * -> 1 request is made to access instance name
+ * -> 1 request is made to access credentials (success)
+ */
+static void test_ec2_provider_metadata_request_error()
+{
+ setup_test(FLB_AWS_CLIENT_MOCK(
+ /* First call to get_credentials() */
+ response(
+ expect(URI, "/"),
+ expect(HEADER, "X-aws-ec2-metadata-token", "INVALID"),
+ expect(METHOD, FLB_HTTP_GET),
+ set(STATUS, 401)
+ ),
+ response(
+ expect(URI, "/latest/api/token"),
+ expect(HEADER, "X-aws-ec2-metadata-token-ttl-seconds", "21600"), /* 6 hours */
+ expect(METHOD, FLB_HTTP_PUT),
+ set(STATUS, 200),
+ set(PAYLOAD, "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(PAYLOAD_SIZE, 56)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ config(REPLACE, NULL)
+ ),
+
+ /* Second call to get_credentials() */
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 404),
+ set(PAYLOAD, "IMDS server not found"), /* This should never happen */
+ set(PAYLOAD_SIZE, 21)
+ ),
+
+ /* Third call to get_credentials() */
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, ("{tsJ@&K+Xo9?'a,uxb)=/iZg\"M4B\\&,qb"
+ "Y\\%%niHubN^I[#arpu9|A':W!JZ@?frM|\""
+ "?aVK<WS3ziAp:\"d=VD(Mu<Bl(e6I?G.3"
+ "rI!f5OxfmkJ)ePc\\5@dGIA!q${iVCF3*"
+ "#y6z<5Nal\\Wmp!0gpeoG!#iY[H&@350v"
+ "$!)i4?6!&}uQ]3%%b25I._H{!.4{42T ,"
+ "b{f\\jpAQ\"j>~<L%%<;k4uh5d+;\\%%soDl<F"
+ "+\\j|WvhX+V)Xb)&tF&'Gj:2Q8@/m3Y46"
+ "79HrM2^1sg11b94kP$)x-*A)ueiR=&L\""
+ "-Hd&sN[M[#BS;ZVIn#j7Lj{E`7 c:%%bK")), /* Random text fuzz */
+ set(PAYLOAD_SIZE, 328)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "My_Instance_Name"),
+ set(PAYLOAD_SIZE, 16)
+ ),
+ response(
+ expect(URI, "/latest/meta-data/iam/security-credentials/My_Instance_Name"),
+ expect(METHOD, FLB_HTTP_GET),
+ expect(HEADER, "X-aws-ec2-metadata-token", "AQAAANjUxxxxxxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Q=="),
+ set(STATUS, 200),
+ set(PAYLOAD, "{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-16T18:29:09Z\",\n"
+ " \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"XACCESSEC2XXX\",\n \"SecretAccessKey\""
+ " : \"XSECRETEC2XXXXXXXXXXXXXX\",\n \"Token\" : \"XTOKENEC2XXXXXXXXXXXXXXX==\",\n"
+ " \"Expiration\" : \"3021-09-17T00:41:00Z\"\n}"), /* Expires Year 3021 */
+ set(PAYLOAD_SIZE, 257)
+ )
+ ));
+
+ /* First call to get_credentials() */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* Second call to get_credentials() */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* Third call to get_credentials() */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* Fourth call to get_credentials() */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Retrieve from cache */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+ TEST_CHECK(strcmp("XACCESSEC2XXX", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("XSECRETEC2XXXXXXXXXXXXXX", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("XTOKENEC2XXXXXXXXXXXXXXX==", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* Check we have exhausted our response list */
+ TEST_CHECK(flb_aws_client_mock_generator_count_unused_requests() == 0);
+
+ cleanup_test();
+}
+
+/* IMDS specific testing */
+
+/*
+ * IMDS Creation and Destruction -- Test Summary
+ * First call to flb_aws_imds_create (fail)
+ * -> upstream not set
+ * Second call to flb_aws_imds_create (fail)
+ * -> upstream set
+ * -> upstream tcp not equal to IMDS (random text)
+ * Third call to flb_aws_imds_create (fail)
+ * -> upstream set
+ * -> upstream tcp not equal to IMDS (one fewer character)
+ * Fourth call to flb_aws_imds_create (fail)
+ * -> upstream set
+ * -> upstream tcp not equal to IMDS (one extra character)
+ * Fifth call to flb_aws_imds_create (fail)
+ * -> upstream set
+ * -> upstream tcp equal to IMDS address
+ * -> upstream port not equal to IMDS port (number 0)
+ * Sixth call to flb_aws_imds_create (success)
+ * -> upstream set
+ * -> upstream tcp equal to IMDS address
+ * -> upstream port equal to IMDS port (80)
+ * First call to flb_aws_imds_destroy (success)
+ */
+static void test_ec2_imds_create_and_destroy()
+{
+ /* Full test setup not needed */
+ /* Initialize test environment */
+ config_fluent = flb_config_init();
+
+ struct flb_aws_imds_config i_config = flb_aws_imds_config_default;
+ struct flb_aws_client a_client = { 0 };
+ struct flb_aws_imds* imds;
+
+ struct flb_upstream u_stream = { 0 };
+
+ /* First call to flb_aws_imds_create */
+ imds = flb_aws_imds_create(&i_config, &a_client);
+ TEST_CHECK(imds == NULL);
+
+ /* Second call to flb_aws_imds_create */
+ a_client.upstream = &u_stream;
+ u_stream.tcp_host = "Invalid host";
+ imds = flb_aws_imds_create(&i_config, &a_client);
+ TEST_CHECK(imds == NULL);
+
+ /* Third call to flb_aws_imds_create */
+ u_stream.tcp_host = "169.254.169.254ExtraInvalid";
+ imds = flb_aws_imds_create(&i_config, &a_client);
+ TEST_CHECK(imds == NULL);
+
+ /* Fourth call to flb_aws_imds_create */
+ u_stream.tcp_host = "169.254.169.254";
+ u_stream.tcp_port = 0xBAD;
+ imds = flb_aws_imds_create(&i_config, &a_client);
+ TEST_CHECK(imds == NULL);
+
+ /* Fifth call to flb_aws_imds_create */
+ u_stream.tcp_host = "169.254.169.254";
+ u_stream.tcp_port = 80;
+ imds = flb_aws_imds_create(&i_config, &a_client);
+ TEST_CHECK(imds != NULL);
+
+ /* Destruction */
+ flb_aws_imds_destroy(imds);
+
+ flb_config_exit(config_fluent);
+}
+
+TEST_LIST = {
+ { "test_ec2_provider_v2" , test_ec2_provider_v2},
+ { "test_ec2_provider_v1" , test_ec2_provider_v1},
+ { "test_ec2_provider_v1_v2_timeout" , test_ec2_provider_v1_v2_timeout},
+ { "test_ec2_provider_version_detection_error" , test_ec2_provider_version_detection_error},
+ { "test_ec2_provider_acquire_token_error" , test_ec2_provider_acquire_token_error},
+ { "test_ec2_provider_metadata_request_error" , test_ec2_provider_metadata_request_error},
+ { "test_ec2_imds_create_and_destroy" , test_ec2_imds_create_and_destroy},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/aws_credentials_http.c b/fluent-bit/tests/internal/aws_credentials_http.c
new file mode 100644
index 00000000..55912da3
--- /dev/null
+++ b/fluent-bit/tests/internal/aws_credentials_http.c
@@ -0,0 +1,382 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_aws_credentials.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_http_client.h>
+
+#include <monkey/mk_core.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "flb_tests_internal.h"
+
+#define ACCESS_KEY_HTTP "http_akid"
+#define SECRET_KEY_HTTP "http_skid"
+#define TOKEN_HTTP "http_token"
+
+#define HTTP_CREDENTIALS_RESPONSE "{\n\
+ \"AccessKeyId\": \"http_akid\",\n\
+ \"Expiration\": \"2025-10-24T23:00:23Z\",\n\
+ \"RoleArn\": \"TASK_ROLE_ARN\",\n\
+ \"SecretAccessKey\": \"http_skid\",\n\
+ \"Token\": \"http_token\"\n\
+}"
+
+/*
+ * Unexpected/invalid HTTP response. The goal of this is not to test anything
+ * that might happen in production, but rather to test the error handling
+ * code for the providers. This helps ensure all code paths are tested and
+ * the error handling code does not introduce memory leaks.
+ */
+#define HTTP_RESPONSE_MALFORMED "{\n\
+ \"AccessKeyId\": \"http_akid\",\n\
+ \"partially-correct\": \"json\",\n\
+ \"RoleArn\": \"TASK_ROLE_ARN\",\n\
+ \"but incomplete\": \"and not terminated with a closing brace\",\n\
+ \"Token\": \"http_token\""
+
+
+/*
+ * Global Variable that allows us to check the number of calls
+ * made in each test
+ */
+int g_request_count;
+
+struct flb_http_client *request_happy_case(struct flb_aws_client *aws_client,
+ int method, const char *uri)
+{
+ struct flb_http_client *c = NULL;
+
+ TEST_CHECK(method == FLB_HTTP_GET);
+
+ TEST_CHECK(strstr(uri, "happy-case") != NULL);
+
+ /* create an http client so that we can set the response */
+ c = flb_calloc(1, sizeof(struct flb_http_client));
+ if (!c) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(&c->headers);
+
+ c->resp.status = 200;
+ c->resp.payload = HTTP_CREDENTIALS_RESPONSE;
+ c->resp.payload_size = strlen(HTTP_CREDENTIALS_RESPONSE);
+
+ return c;
+}
+
+/* unexpected output test- see description for HTTP_RESPONSE_MALFORMED */
+struct flb_http_client *request_malformed(struct flb_aws_client *aws_client,
+ int method, const char *uri)
+{
+ struct flb_http_client *c = NULL;
+
+ TEST_CHECK(method == FLB_HTTP_GET);
+
+ TEST_CHECK(strstr(uri, "malformed") != NULL);
+
+ /* create an http client so that we can set the response */
+ c = flb_calloc(1, sizeof(struct flb_http_client));
+ if (!c) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(&c->headers);
+
+ c->resp.status = 200;
+ c->resp.payload = HTTP_RESPONSE_MALFORMED;
+ c->resp.payload_size = strlen(HTTP_RESPONSE_MALFORMED);
+
+ return c;
+}
+
+struct flb_http_client *request_error_case(struct flb_aws_client *aws_client,
+ int method, const char *uri)
+{
+ struct flb_http_client *c = NULL;
+
+ TEST_CHECK(method == FLB_HTTP_GET);
+
+ TEST_CHECK(strstr(uri, "error-case") != NULL);
+
+ /* create an http client so that we can set the response */
+ c = flb_calloc(1, sizeof(struct flb_http_client));
+ if (!c) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(&c->headers);
+
+ c->resp.status = 400;
+ c->resp.payload = NULL;
+ c->resp.payload_size = 0;
+
+ return c;
+}
+
+/* test/mock version of the flb_aws_client request function */
+struct flb_http_client *test_http_client_request(struct flb_aws_client *aws_client,
+ int method, const char *uri,
+ const char *body, size_t body_len,
+ struct flb_aws_header *dynamic_headers,
+ size_t dynamic_headers_len)
+{
+ g_request_count++;
+ /*
+ * route to the correct test case fn using the uri
+ */
+ if (strstr(uri, "happy-case") != NULL) {
+ return request_happy_case(aws_client, method, uri);
+ } else if (strstr(uri, "error-case") != NULL) {
+ return request_error_case(aws_client, method, uri);
+ } else if (strstr(uri, "malformed") != NULL) {
+ return request_malformed(aws_client, method, uri);
+ }
+
+ /* uri should match one of the above conditions */
+ flb_errno();
+ return NULL;
+
+}
+
+/* Test/mock flb_aws_client */
+static struct flb_aws_client_vtable test_vtable = {
+ .request = test_http_client_request,
+};
+
+struct flb_aws_client *test_http_client_create()
+{
+ struct flb_aws_client *client = flb_calloc(1,
+ sizeof(struct flb_aws_client));
+ if (!client) {
+ flb_errno();
+ return NULL;
+ }
+ client->client_vtable = &test_vtable;
+ return client;
+}
+
+/* Generator that returns clients with the test vtable */
+static struct flb_aws_client_generator test_generator = {
+ .create = test_http_client_create,
+};
+
+struct flb_aws_client_generator *generator_in_test()
+{
+ return &test_generator;
+}
+
+/* http and ecs providers */
+static void test_http_provider()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ int ret;
+ struct flb_config *config;
+ flb_sds_t host;
+ flb_sds_t path;
+
+ g_request_count = 0;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ host = flb_sds_create("127.0.0.1");
+ if (!host) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ path = flb_sds_create("/happy-case");
+ if (!path) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_http_provider_create(config, host, path,
+ generator_in_test());
+
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(ACCESS_KEY_HTTP, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SECRET_KEY_HTTP, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN_HTTP, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(ACCESS_KEY_HTTP, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SECRET_KEY_HTTP, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN_HTTP, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ /*
+ * Request count should be 2:
+ * - One for the first call to get_credentials (2nd should hit cred cache)
+ * - One for the call to refresh
+ */
+ TEST_CHECK(g_request_count == 2);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+static void test_http_provider_error_case()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ int ret;
+ struct flb_config *config;
+ flb_sds_t host;
+ flb_sds_t path;
+
+ g_request_count = 0;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ host = flb_sds_create("127.0.0.1");
+ if (!host) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ path = flb_sds_create("/error-case");
+ if (!path) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_http_provider_create(config, host, path,
+ generator_in_test());
+
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* get_credentials will fail */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* refresh should return -1 (failure) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret < 0);
+
+ /*
+ * Request count should be 3:
+ * - Each call to get_credentials and refresh invokes the client's
+ * request method and returns a request failure.
+ */
+ TEST_CHECK(g_request_count == 3);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+static void test_http_provider_malformed_response()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ int ret;
+ struct flb_config *config;
+ flb_sds_t host;
+ flb_sds_t path;
+
+ g_request_count = 0;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ mk_list_init(&config->upstreams);
+
+ host = flb_sds_create("127.0.0.1");
+ if (!host) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ path = flb_sds_create("/malformed");
+ if (!path) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_http_provider_create(config, host, path,
+ generator_in_test());
+
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* get_credentials will fail */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* refresh should return -1 (failure) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret < 0);
+
+ /*
+ * Request count should be 3:
+ * - Each call to get_credentials and refresh invokes the client's
+ * request method and returns a request failure.
+ */
+ TEST_CHECK(g_request_count == 3);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+TEST_LIST = {
+ { "test_http_provider" , test_http_provider},
+ { "test_http_provider_error_case" , test_http_provider_error_case},
+ { "test_http_provider_malformed_response" ,
+ test_http_provider_malformed_response},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/aws_credentials_process.c b/fluent-bit/tests/internal/aws_credentials_process.c
new file mode 100644
index 00000000..3add81fe
--- /dev/null
+++ b/fluent-bit/tests/internal/aws_credentials_process.c
@@ -0,0 +1,496 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_aws_credentials.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_info.h>
+
+#include "aws_credentials_test_internal.h"
+
+#define _TOSTRING(X) #X
+#define TOSTRING(X) _TOSTRING(X)
+#define TESTCASE_NAME() "aws_credentials_process.c:" TOSTRING(__LINE__)
+
+#define MUST_SETENV(name, value) TEST_ASSERT(setenv(name, value, 1) == 0)
+#define MUST_UNSETENV(name) TEST_ASSERT(unsetenv(name) == 0)
+
+static int unset_process_env()
+{
+ int ret;
+
+ ret = unset_profile_env();
+ if (ret < 0) {
+ return -1;
+ }
+
+ ret = unsetenv("_AWS_SECRET_ACCESS_KEY");
+ if (ret < 0) {
+ flb_errno();
+ return -1;
+ }
+
+ ret = unsetenv("_AWS_SESSION_TOKEN");
+ if (ret < 0) {
+ flb_errno();
+ return -1;
+ }
+
+ ret = unsetenv("_AWS_EXPIRATION");
+ if (ret < 0) {
+ flb_errno();
+ return -1;
+ }
+
+ ret = unsetenv("_AWS_EXIT_CODE");
+ if (ret < 0) {
+ flb_errno();
+ return -1;
+ }
+
+ return 0;
+}
+
+static void test_credential_process_default(void)
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials* creds;
+ char* original_path = getenv("PATH");
+
+ /* Print a newline so the test output starts on its own line. */
+ fprintf(stderr, "\n");
+
+ TEST_CHECK(unset_process_env() == 0);
+
+ MUST_SETENV("AWS_CONFIG_FILE", AWS_TEST_DATA_PATH("shared_config.ini"));
+ MUST_SETENV("PATH", AWS_TEST_DATA_PATH("credential_process"));
+
+ provider = flb_profile_provider_create(NULL);
+ TEST_ASSERT(provider != NULL);
+
+ /* These environment variables are used by the test credential_process. */
+ MUST_SETENV("_AWS_SECRET_ACCESS_KEY", "aws_secret_access_key");
+ MUST_SETENV("_AWS_SESSION_TOKEN", "aws_session_token");
+ MUST_SETENV("_AWS_EXPIRATION", "+15 minutes");
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp("default", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("aws_secret_access_key", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("aws_session_token", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+ creds = NULL;
+
+ /* Repeated calls to get_credentials should return the cached credentials. */
+
+ MUST_SETENV("_AWS_SECRET_ACCESS_KEY", "aws_secret_access_key_2");
+ MUST_SETENV("_AWS_SESSION_TOKEN", "aws_session_token_2");
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp("default", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("aws_secret_access_key", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("aws_session_token", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+ creds = NULL;
+
+ /* Calling refresh should fetch the new credentials. */
+
+ MUST_SETENV("_AWS_SECRET_ACCESS_KEY", "aws_secret_access_key_3");
+ MUST_SETENV("_AWS_SESSION_TOKEN", "aws_session_token_3");
+
+ TEST_ASSERT(provider->provider_vtable->refresh(provider) == 0);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp("default", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("aws_secret_access_key_3", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("aws_session_token_3", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+ creds = NULL;
+
+ flb_aws_provider_destroy(provider);
+ provider = NULL;
+
+ if (original_path) {
+ MUST_SETENV("PATH", original_path);
+ } else {
+ MUST_UNSETENV("PATH");
+ }
+}
+
+static void test_credential_process_no_expiration(void)
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials* creds;
+ char* original_path = getenv("PATH");
+
+ /* Print a newline so the test output starts on its own line. */
+ fprintf(stderr, "\n");
+
+ TEST_CHECK(unset_process_env() == 0);
+
+ MUST_SETENV("AWS_CONFIG_FILE", AWS_TEST_DATA_PATH("shared_config.ini"));
+ MUST_SETENV("AWS_PROFILE", "nondefault");
+ MUST_SETENV("PATH", AWS_TEST_DATA_PATH("credential_process"));
+
+ provider = flb_profile_provider_create(NULL);
+ TEST_ASSERT(provider != NULL);
+
+ /* These environment variables are used by the test credential_process. */
+ MUST_SETENV("_AWS_SECRET_ACCESS_KEY", "aws_secret_access_key");
+ MUST_SETENV("_AWS_SESSION_TOKEN", "aws_session_token");
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp("nondefault", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("aws_secret_access_key", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("aws_session_token", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+ creds = NULL;
+
+ /* Repeated calls to get_credentials should return the cached credentials. */
+
+ MUST_SETENV("_AWS_SECRET_ACCESS_KEY", "aws_secret_access_key_2");
+ MUST_SETENV("_AWS_SESSION_TOKEN", "aws_session_token_2");
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp("nondefault", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("aws_secret_access_key", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("aws_session_token", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+ creds = NULL;
+
+ /* Calling refresh should fetch the new credentials. */
+
+ MUST_SETENV("_AWS_SECRET_ACCESS_KEY", "aws_secret_access_key_3");
+ MUST_SETENV("_AWS_SESSION_TOKEN", "aws_session_token_3");
+
+ TEST_ASSERT(provider->provider_vtable->refresh(provider) == 0);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp("nondefault", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("aws_secret_access_key_3", creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp("aws_session_token_3", creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+ creds = NULL;
+
+ flb_aws_provider_destroy(provider);
+ provider = NULL;
+
+ if (original_path) {
+ MUST_SETENV("PATH", original_path);
+ } else {
+ MUST_UNSETENV("PATH");
+ }
+}
+
+struct credential_process_expired_testcase {
+ char* name;
+ char* expiration;
+};
+
+struct credential_process_expired_testcase credential_process_expired_testcases[] = {
+ /* Credentials that have already expired will be refreshed. */
+ {
+ .name = "expired",
+ .expiration = "-5 minutes",
+ },
+
+ /* Credentials that expire within the next minute will be refreshed. */
+ {
+ .name = "expiring soon",
+ .expiration = "+30 seconds",
+ },
+};
+
+static void test_credential_process_expired_helper(char* expiration)
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials* creds;
+ char* original_path = getenv("PATH");
+
+ TEST_CHECK(unset_process_env() == 0);
+
+ MUST_SETENV("AWS_CONFIG_FILE", AWS_TEST_DATA_PATH("shared_config.ini"));
+ MUST_SETENV("AWS_PROFILE", "nondefault");
+ MUST_SETENV("PATH", AWS_TEST_DATA_PATH("credential_process"));
+
+ provider = flb_profile_provider_create(NULL);
+ TEST_ASSERT(provider != NULL);
+
+ /* These environment variable are used by the test credential_process. */
+ MUST_SETENV("_AWS_SECRET_ACCESS_KEY", "aws_secret_access_key");
+ if (expiration) {
+ MUST_SETENV("_AWS_EXPIRATION", expiration);
+ }
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp("nondefault", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("aws_secret_access_key", creds->secret_access_key) == 0);
+ TEST_CHECK(creds->session_token == NULL);
+
+ flb_aws_credentials_destroy(creds);
+ creds = NULL;
+
+ /* Repeated calls to get_credentials should fetch new credentials. */
+
+ MUST_SETENV("_AWS_SECRET_ACCESS_KEY", "aws_secret_access_key_2");
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp("nondefault", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("aws_secret_access_key_2", creds->secret_access_key) == 0);
+ TEST_CHECK(creds->session_token == NULL);
+
+ flb_aws_credentials_destroy(creds);
+ creds = NULL;
+
+ flb_aws_provider_destroy(provider);
+ provider = NULL;
+
+ if (original_path) {
+ MUST_SETENV("PATH", original_path);
+ } else {
+ MUST_UNSETENV("PATH");
+ }
+}
+
+static void test_credential_process_expired(void)
+{
+ int i;
+ int num_testcases = sizeof(credential_process_expired_testcases) /
+ sizeof(credential_process_expired_testcases[0]);
+ struct credential_process_expired_testcase* current_testcase = NULL;
+
+ /* Print a newline so the test output starts on its own line. */
+ fprintf(stderr, "\n");
+
+ for (i = 0; i < num_testcases; i++) {
+ current_testcase = &credential_process_expired_testcases[i];
+ TEST_CASE(current_testcase->name);
+ test_credential_process_expired_helper(current_testcase->expiration);
+ }
+}
+
+static void test_credential_process_failure(void)
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials* creds;
+ char* original_path = getenv("PATH");
+
+ /* Print a newline so the test output starts on its own line. */
+ fprintf(stderr, "\n");
+
+ TEST_CHECK(unset_process_env() == 0);
+
+ MUST_SETENV("AWS_CONFIG_FILE", AWS_TEST_DATA_PATH("shared_config.ini"));
+ MUST_SETENV("PATH", AWS_TEST_DATA_PATH("credential_process"));
+
+ provider = flb_profile_provider_create(NULL);
+ TEST_ASSERT(provider != NULL);
+
+ /* These environment variables are used by the test credential_process. */
+ MUST_SETENV("_AWS_SECRET_ACCESS_KEY", "aws_secret_access_key");
+ MUST_SETENV("_AWS_EXIT_CODE", "1");
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds == NULL);
+
+ flb_aws_credentials_destroy(creds);
+ creds = NULL;
+
+ /* Repeated calls to get_credentials should try to fetch the credentials again. */
+
+ MUST_SETENV("_AWS_SECRET_ACCESS_KEY", "aws_secret_access_key_2");
+ MUST_UNSETENV("_AWS_EXIT_CODE");
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp("default", creds->access_key_id) == 0);
+ TEST_CHECK(strcmp("aws_secret_access_key_2", creds->secret_access_key) == 0);
+
+ flb_aws_credentials_destroy(creds);
+ creds = NULL;
+
+ flb_aws_provider_destroy(provider);
+ provider = NULL;
+
+ if (original_path) {
+ MUST_SETENV("PATH", original_path);
+ } else {
+ MUST_UNSETENV("PATH");
+ }
+}
+
+struct parse_credential_process_testcase {
+ char* name;
+ char* input;
+
+ /*
+ * NULL-terminated array of tokens that should be returned.
+ * The choice of 10 here is completely arbitrary.
+ * Since a char*[] cannot be NULL, we need a separate flag for the failure cases.
+ */
+ char* expected[10];
+ int should_fail;
+};
+
+struct parse_credential_process_testcase parse_credential_process_testcases[] = {
+ {
+ .name = TESTCASE_NAME(),
+ .input = "my-process",
+ .expected = { "my-process", NULL },
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = " my-process ",
+ .expected = { "my-process", NULL },
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = "my-process arg1 arg2",
+ .expected = { "my-process", "arg1", "arg2", NULL },
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = " my-process arg1 arg2 ",
+ .expected = { "my-process", "arg1", "arg2", NULL },
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = "my-process \"arg1 \" arg2 \"\"",
+ .expected = { "my-process", "arg1 ", "arg2", "", NULL },
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = "\"my process\"",
+ .expected = { "my process", NULL },
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = " \"my process\" \" \" ",
+ .expected = { "my process", " ", NULL },
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = "",
+ .expected = { NULL },
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = "\"unterminated",
+ .should_fail = FLB_TRUE,
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = " \"unterminated ",
+ .should_fail = FLB_TRUE,
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = "abc\"def\"",
+ .should_fail = FLB_TRUE,
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = " abc\"def\" ",
+ .should_fail = FLB_TRUE,
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = "\"abc\"def",
+ .should_fail = FLB_TRUE,
+ },
+ {
+ .name = TESTCASE_NAME(),
+ .input = " \"abc\"def ",
+ .should_fail = FLB_TRUE,
+ },
+};
+
+static void test_parse_credential_process_helper(char* input, char** expected)
+{
+ char* cpy = NULL;
+ char** tokens = NULL;
+ int i = 0;
+ int input_len = strlen(input) + 1;
+
+ /*
+ * String literals are immutable, but parse_credential_process modifies its input.
+ * To circumvent this, copy the literal into a mutable string.
+ * Note: Because the return value of parse_credential_process will contain pointers
+ * into this string, we cannot free the copy until we are done with the token array.
+ */
+ cpy = flb_malloc(input_len + 1);
+ TEST_ASSERT(cpy != NULL);
+ memcpy(cpy, input, input_len);
+
+ tokens = parse_credential_process(cpy);
+
+ if (expected) {
+ TEST_ASSERT(tokens != NULL);
+ for (i = 0; expected[i]; i++) {
+ TEST_ASSERT_(tokens[i] != NULL, "expected='%s', got=(null)", expected[i]);
+ TEST_CHECK_(strcmp(expected[i], tokens[i]) == 0, "expected='%s', got='%s'",
+ expected[i], tokens[i]);
+ }
+ TEST_ASSERT_(tokens[i] == NULL, "expected=(null), got='%s'", tokens[i]);
+ }
+ else {
+ TEST_ASSERT(tokens == NULL);
+ }
+
+ flb_free(tokens);
+ flb_free(cpy);
+}
+
+static void test_parse_credential_process(void)
+{
+ int i;
+ int num_testcases = sizeof(parse_credential_process_testcases) /
+ sizeof(parse_credential_process_testcases[0]);
+ struct parse_credential_process_testcase* current_testcase = NULL;
+ char** expected = NULL;
+
+ /* Print a newline so the test output starts on its own line. */
+ fprintf(stderr, "\n");
+
+ for (i = 0; i < num_testcases; i++) {
+ current_testcase = &parse_credential_process_testcases[i];
+ TEST_CASE(current_testcase->name);
+ if (current_testcase->should_fail) {
+ expected = NULL;
+ }
+ else {
+ expected = current_testcase->expected;
+ }
+ test_parse_credential_process_helper(current_testcase->input, expected);
+ }
+}
+
+TEST_LIST = {
+ { "test_credential_process_default", test_credential_process_default },
+ { "test_credential_process_no_expiration", test_credential_process_no_expiration },
+ { "test_credential_process_expired", test_credential_process_expired },
+ { "test_credential_process_failure", test_credential_process_failure },
+ { "test_parse_credential_process", test_parse_credential_process },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/aws_credentials_profile.c b/fluent-bit/tests/internal/aws_credentials_profile.c
new file mode 100644
index 00000000..1335bd95
--- /dev/null
+++ b/fluent-bit/tests/internal/aws_credentials_profile.c
@@ -0,0 +1,405 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_aws_credentials.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_info.h>
+
+#include "aws_credentials_test_internal.h"
+
+#define TEST_CREDENTIALS_FILE AWS_TEST_DATA_PATH("shared_credentials_file.ini")
+
+#define TEST_CREDENTIALS_NODEFAULT AWS_TEST_DATA_PATH("shared_credentials_file_nodefault.ini")
+
+/* these credentials look real but are not */
+#define AKID_DEFAULT_PROFILE "ASIASDMPIJWXJAXT3O3T"
+#define SKID_DEFAULT_PROFILE "EAUBpd/APPT4Nfi4DWY3gnt5TU/4T49laqS5zh8W"
+#define TOKEN_DEFAULT_PROFILE "IQoJb3JpZ2luX2VjEOD//////////wEaCNVzLWVh\
+c3QtMSJHMEUCIKCn7v/EDowMZvJnciSJbxA7rIV4p1K6pOUvcLHM+9EzNgIgeiYbfA47DGS\
+qoEZS3yrRWGN8Fr4Q/bK7ANRgv09Hth8q1gEIWRABGgwxNDQ3MTg3MTE0NzAiDGSqzyXiic\
+OZp63afiqzAUyWOljOn5HaIxRfpQ5pTf+o4roJ2KPlHn+XHEKJZKien4Ydm7zeVi7SbPLKo\
+cjmjYJd31PrlbJ43C6AyrhmY57qaD7Zz4N3N0V6mekzvlAeARXsa4deflsbemqkp1WVsBLk\
+O6qUuk+N04+MxIVXAxkW9RSPRTVjxeS2m5Yobygto58WLFE8gacRoNd4lCK4JUmEdiaxJEQ\
+QO7leZ3v1XxQr6QBS8P/GmcJYcQTxlA6AFQxIMJKGwfAFOuMB2cEc8cF2Htiqf3LVGMk/6b\
+YKkW7fHUtrnttp28jgWtbbLtFbX/zIdlqwm73Ryp7lI+xkM4XNIT+6ZKa4Xw0/Zw3xLzlk3\
+jic6QWPAcffwR6kOunoTOWJzPskK/RZ4Cd+GyGarxG27Cz6xolAzAsDpdGQwV7kCCUPi6/V\
+HjefwKEk9HjZfejC5WuCS173qFrU9kNb4IrYhnK+wmRzzJfgpWUwerdiJKBz95j1iW9rP1a\
+8p1xLR3EXUMN3LIW0+gP8sFjg5iiqDkaS/tUXWZndM2QdJLcrxwAutFchc0nqJHYTijw="
+
+#define AKID_NONDEFAULT_PROFILE "akid"
+#define SKID_NONDEFAULT_PROFILE "skid"
+
+#define AKID_NOSPACE_PROFILE "akidnospace"
+#define SKID_NOSPACE_PROFILE "skidnospace"
+#define TOKEN_NOSPACE_PROFILE "tokennospace"
+
+#define AKID_WEIRDWHITESPACE_PROFILE "akidweird"
+#define SKID_WEIRDWHITESPACE_PROFILE "skidweird"
+#define TOKEN_WEIRDWHITESPACE_PROFILE "tokenweird///token=="
+
+#define CUSTOM_PROFILE_ACCESS_KEY_ID "custom_access_key_id"
+#define CUSTOM_PROFILE_SECRET_ACCESS_KEY "custom_secret_access_key"
+
+static void test_profile_default()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials*creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ TEST_CHECK(unset_profile_env() == 0);
+
+ ret = setenv("AWS_SHARED_CREDENTIALS_FILE", TEST_CREDENTIALS_FILE, 1);
+ TEST_ASSERT(ret == 0);
+
+ provider = flb_profile_provider_create(NULL);
+ TEST_ASSERT(provider != NULL);
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp(AKID_DEFAULT_PROFILE, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SKID_DEFAULT_PROFILE, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN_DEFAULT_PROFILE, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp(AKID_DEFAULT_PROFILE, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SKID_DEFAULT_PROFILE, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN_DEFAULT_PROFILE, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+
+ TEST_CHECK(unset_profile_env() == 0);
+}
+
+static void test_profile_custom()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials*creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ TEST_CHECK(unset_profile_env() == 0);
+
+ ret = setenv("AWS_SHARED_CREDENTIALS_FILE", TEST_CREDENTIALS_FILE, 1);
+ TEST_ASSERT(ret == 0);
+
+ provider = flb_profile_provider_create("custom");
+ TEST_ASSERT(provider != NULL);
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp(CUSTOM_PROFILE_ACCESS_KEY_ID, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(CUSTOM_PROFILE_SECRET_ACCESS_KEY, creds->secret_access_key) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp(CUSTOM_PROFILE_ACCESS_KEY_ID, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(CUSTOM_PROFILE_SECRET_ACCESS_KEY, creds->secret_access_key) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+
+ TEST_CHECK(unset_profile_env() == 0);
+}
+
+static void test_profile_non_default()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials*creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ TEST_CHECK(unset_profile_env() == 0);
+
+ ret = setenv("AWS_SHARED_CREDENTIALS_FILE", TEST_CREDENTIALS_FILE, 1);
+ TEST_ASSERT(ret == 0);
+
+ ret = setenv("AWS_PROFILE", "nondefault", 1);
+ TEST_ASSERT(ret == 0);
+
+ provider = flb_profile_provider_create(NULL);
+ TEST_ASSERT(provider != NULL);
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp(AKID_NONDEFAULT_PROFILE, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SKID_NONDEFAULT_PROFILE, creds->secret_access_key) == 0);
+ TEST_CHECK(creds->session_token == NULL);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp(AKID_NONDEFAULT_PROFILE, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SKID_NONDEFAULT_PROFILE, creds->secret_access_key) == 0);
+ TEST_CHECK(creds->session_token == NULL);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+
+ TEST_CHECK(unset_profile_env() == 0);
+}
+
+static void test_profile_no_space()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials*creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ TEST_CHECK(unset_profile_env() == 0);
+
+ ret = setenv("AWS_SHARED_CREDENTIALS_FILE", TEST_CREDENTIALS_FILE, 1);
+ TEST_ASSERT(ret == 0);
+
+ ret = setenv("AWS_DEFAULT_PROFILE", "nospace", 1);
+ TEST_ASSERT(ret == 0);
+
+ provider = flb_profile_provider_create(NULL);
+ TEST_ASSERT(provider != NULL);
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp(AKID_NOSPACE_PROFILE, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SKID_NOSPACE_PROFILE, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN_NOSPACE_PROFILE, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_ASSERT(creds != NULL);
+
+ TEST_CHECK(strcmp(AKID_NOSPACE_PROFILE, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SKID_NOSPACE_PROFILE, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN_NOSPACE_PROFILE, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+
+ TEST_CHECK(unset_profile_env() == 0);
+}
+
+static void test_profile_weird_whitespace()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials*creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ TEST_CHECK(unset_profile_env() == 0);
+
+ ret = setenv("AWS_SHARED_CREDENTIALS_FILE", TEST_CREDENTIALS_FILE, 1);
+ TEST_ASSERT(ret == 0);
+
+ ret = setenv("AWS_DEFAULT_PROFILE", "weirdwhitespace", 1);
+ TEST_ASSERT(ret == 0);
+
+ provider = flb_profile_provider_create(NULL);
+ TEST_ASSERT(provider != NULL);
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(AKID_WEIRDWHITESPACE_PROFILE, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SKID_WEIRDWHITESPACE_PROFILE,
+ creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN_WEIRDWHITESPACE_PROFILE, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(AKID_WEIRDWHITESPACE_PROFILE, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(SKID_WEIRDWHITESPACE_PROFILE,
+ creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(TOKEN_WEIRDWHITESPACE_PROFILE, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+
+ TEST_CHECK(unset_profile_env() == 0);
+}
+
+static void test_profile_missing()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials*creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ TEST_CHECK(unset_profile_env() == 0);
+
+ ret = setenv("AWS_SHARED_CREDENTIALS_FILE", TEST_CREDENTIALS_FILE, 1);
+ TEST_ASSERT(ret == 0);
+
+ ret = setenv("AWS_DEFAULT_PROFILE", "missing", 1);
+ TEST_ASSERT(ret == 0);
+
+ provider = flb_profile_provider_create(NULL);
+ TEST_ASSERT(provider != NULL);
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return -1 (failure) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret < 0);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+
+ TEST_CHECK(unset_profile_env() == 0);
+}
+
+static void test_profile_nodefault()
+{
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials*creds;
+ struct flb_config *config;
+ int ret;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ TEST_CHECK(unset_profile_env() == 0);
+
+ ret = setenv("AWS_SHARED_CREDENTIALS_FILE", TEST_CREDENTIALS_NODEFAULT, 1);
+ TEST_ASSERT(ret == 0);
+
+ provider = flb_profile_provider_create(NULL);
+ TEST_ASSERT(provider != NULL);
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return -1 (failure) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret < 0);
+
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+
+ TEST_CHECK(unset_profile_env() == 0);
+}
+
+
+TEST_LIST = {
+ { "test_profile_default", test_profile_default },
+ { "test_profile_non_default", test_profile_non_default },
+ { "test_profile_no_space", test_profile_no_space },
+ { "test_profile_weird_whitespace", test_profile_weird_whitespace },
+ { "test_profile_missing", test_profile_missing },
+ { "test_profile_nodefault", test_profile_nodefault },
+ { "test_profile_custom", test_profile_custom },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/aws_credentials_sts.c b/fluent-bit/tests/internal/aws_credentials_sts.c
new file mode 100644
index 00000000..146d937e
--- /dev/null
+++ b/fluent-bit/tests/internal/aws_credentials_sts.c
@@ -0,0 +1,939 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_aws_credentials.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_http_client.h>
+
+#include <monkey/mk_core.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "flb_tests_internal.h"
+
+#define EKS_ACCESS_KEY "eks_akid"
+#define EKS_SECRET_KEY "eks_skid"
+#define EKS_TOKEN "eks_token"
+
+#define STS_ACCESS_KEY "sts_akid"
+#define STS_SECRET_KEY "sts_skid"
+#define STS_TOKEN "sts_token"
+
+/* standard environment variables */
+#define AWS_ACCESS_KEY_ID "AWS_ACCESS_KEY_ID"
+#define AWS_SECRET_ACCESS_KEY "AWS_SECRET_ACCESS_KEY"
+#define AWS_SESSION_TOKEN "AWS_SESSION_TOKEN"
+
+#define TOKEN_FILE_ENV_VAR "AWS_WEB_IDENTITY_TOKEN_FILE"
+#define ROLE_ARN_ENV_VAR "AWS_ROLE_ARN"
+#define SESSION_NAME_ENV_VAR "AWS_ROLE_SESSION_NAME"
+
+#define WEB_TOKEN_FILE FLB_TESTS_DATA_PATH "/data/aws_credentials/\
+web_identity_token_file.txt"
+
+#define STS_RESPONSE_EKS "<AssumeRoleWithWebIdentityResponse \
+xmlns=\"https://sts.amazonaws.com/doc/2011-06-15/\">\n\
+ <AssumeRoleWithWebIdentityResult>\n\
+ <SubjectFromWebIdentityToken>amzn1.account.AF6RHO7KZU5XRVQJGXK6HB56KR2A\n\
+</SubjectFromWebIdentityToken>\n\
+ <Audience>client.5498841531868486423.1548@apps.example.com</Audience>\n\
+ <AssumedRoleUser>\n\
+ <Arn>arn:aws:sts::123456789012:assumed-role/WebIdentityRole/app1</Arn>\n\
+ <AssumedRoleId>AROACLKWSDQRAOEXAMPLE:app1</AssumedRoleId>\n\
+ </AssumedRoleUser>\n\
+ <Credentials>\n\
+ <SessionToken>eks_token</SessionToken>\n\
+ <SecretAccessKey>eks_skid</SecretAccessKey>\n\
+ <Expiration>2025-10-24T23:00:23Z</Expiration>\n\
+ <AccessKeyId>eks_akid</AccessKeyId>\n\
+ </Credentials>\n\
+ <Provider>www.amazon.com</Provider>\n\
+ </AssumeRoleWithWebIdentityResult>\n\
+ <ResponseMetadata>\n\
+ <RequestId>ad4156e9-bce1-11e2-82e6-6b6efEXAMPLE</RequestId>\n\
+ </ResponseMetadata>\n\
+</AssumeRoleWithWebIdentityResponse>"
+
+#define STS_RESPONSE_ASSUME_ROLE "<AssumeRoleResponse \
+xmlns=\"https://sts.amazonaws.com/doc/\n\
+2011-06-15/\">\n\
+ <AssumeRoleResult>\n\
+ <AssumedRoleUser>\n\
+ <Arn>arn:aws:sts::123456789012:assumed-role/demo/TestAR</Arn>\n\
+ <AssumedRoleId>ARO123EXAMPLE123:TestAR</AssumedRoleId>\n\
+ </AssumedRoleUser>\n\
+ <Credentials>\n\
+ <AccessKeyId>sts_akid</AccessKeyId>\n\
+ <SecretAccessKey>sts_skid</SecretAccessKey>\n\
+ <SessionToken>sts_token</SessionToken>\n\
+ <Expiration>2025-11-09T13:34:41Z</Expiration>\n\
+ </Credentials>\n\
+ <PackedPolicySize>6</PackedPolicySize>\n\
+ </AssumeRoleResult>\n\
+ <ResponseMetadata>\n\
+ <RequestId>c6104cbe-af31-11e0-8154-cbc7ccf896c7</RequestId>\n\
+ </ResponseMetadata>\n\
+</AssumeRoleResponse>"
+
+/*
+ * Unexpected/invalid STS response. The goal of this is not to test anything
+ * that might happen in production, but rather to test the error handling
+ * code for the providers. This helps ensure all code paths are tested and
+ * the error handling code does not introduce memory leaks.
+ */
+
+#define STS_RESPONSE_MALFORMED "{\n\
+ \"__type\": \"some unexpected response\",\n\
+ \"this tests\": the error handling code\",\n\
+\"This looks like JSON but is not valid.\"\n\
+<Credentials><AccessKeyId>It also contains xml tags that a correct\n\
+response would have</SecretAccessKey>"
+
+/*
+ * Global Variable that allows us to check the number of calls
+ * made in each test
+ */
+int g_request_count;
+
+/* Each test case has its own request function */
+
+/* unexpected output test- see description for STS_RESPONSE_MALFORMED */
+struct flb_http_client *request_unexpected_response(struct flb_aws_client
+ *aws_client, int method,
+ const char *uri)
+{
+ struct flb_http_client *c;
+ TEST_CHECK(method == FLB_HTTP_GET);
+
+ /* create an http client so that we can set the response */
+ c = flb_calloc(1, sizeof(struct flb_http_client));
+ if (!c) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(&c->headers);
+
+ c->resp.status = 200;
+ c->resp.payload = STS_RESPONSE_MALFORMED;
+ c->resp.payload_size = strlen(STS_RESPONSE_MALFORMED);
+
+ return c;
+}
+struct flb_http_client *request_eks_test1(struct flb_aws_client *aws_client,
+ int method, const char *uri)
+{
+ struct flb_http_client *c;
+
+ TEST_CHECK(method == FLB_HTTP_GET);
+ TEST_CHECK(strstr(uri, "Action=AssumeRoleWithWebIdentity") != NULL);
+ TEST_CHECK(strstr(uri, "RoleArn=arn:aws:iam::123456789012:role/test")
+ != NULL);
+ TEST_CHECK(strstr(uri, "WebIdentityToken=this-is-a-fake-jwt") != NULL);
+ TEST_CHECK(strstr(uri, "RoleSessionName=session_name") != NULL);
+
+ /* create an http client so that we can set the response */
+ c = flb_calloc(1, sizeof(struct flb_http_client));
+ if (!c) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(&c->headers);
+
+ c->resp.status = 200;
+ c->resp.payload = STS_RESPONSE_EKS;
+ c->resp.payload_size = strlen(STS_RESPONSE_EKS);
+
+ return c;
+}
+
+struct flb_http_client *request_eks_flb_sts_session_name(struct flb_aws_client
+ *aws_client,
+ int method,
+ const char *uri)
+{
+ struct flb_http_client *c;
+
+ TEST_CHECK(method == FLB_HTTP_GET);
+ TEST_CHECK(strstr(uri, "Action=AssumeRoleWithWebIdentity") != NULL);
+ TEST_CHECK(strstr(uri, "RoleArn=arn:aws:iam::123456789012:role/"
+ "randomsession") != NULL);
+ TEST_CHECK(strstr(uri, "WebIdentityToken=this-is-a-fake-jwt") != NULL);
+ /* this test case has a random session name */
+ TEST_CHECK(strstr(uri, "RoleSessionName=") != NULL);
+ /* session name should not be the same as test 1 */
+ TEST_CHECK(strstr(uri, "RoleSessionName=session_name") == NULL);
+
+ /* create an http client so that we can set the response */
+ c = flb_calloc(1, sizeof(struct flb_http_client));
+ if (!c) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(&c->headers);
+
+ c->resp.status = 200;
+ c->resp.payload = STS_RESPONSE_EKS;
+ c->resp.payload_size = strlen(STS_RESPONSE_EKS);
+
+ return c;
+}
+
+struct flb_http_client *request_eks_api_error(struct flb_aws_client *aws_client,
+ int method, const char *uri)
+{
+ struct flb_http_client *c;
+
+ TEST_CHECK(method == FLB_HTTP_GET);
+ TEST_CHECK(strstr(uri, "Action=AssumeRoleWithWebIdentity") != NULL);
+ TEST_CHECK(strstr(uri, "RoleArn=arn:aws:iam::123456789012:role/apierror")
+ != NULL);
+ TEST_CHECK(strstr(uri, "WebIdentityToken=this-is-a-fake-jwt") != NULL);
+ /* this test case has a random session name */
+ TEST_CHECK(strstr(uri, "RoleSessionName=") != NULL);
+ /* session name should not be the same as test 1 */
+ TEST_CHECK(strstr(uri, "RoleSessionName=session_name") == NULL);
+
+ /* create an http client so that we can set the response */
+ c = flb_calloc(1, sizeof(struct flb_http_client));
+ if (!c) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(&c->headers);
+
+ c->resp.status = 500;
+ c->resp.payload = NULL;
+ c->resp.payload_size = 0;
+
+ return c;
+}
+
+struct flb_http_client *request_sts_test1(struct flb_aws_client *aws_client,
+ int method, const char *uri)
+{
+ struct flb_http_client *c;
+
+ TEST_CHECK(method == FLB_HTTP_GET);
+ TEST_CHECK(strstr(uri, "Action=AssumeRole") != NULL);
+ TEST_CHECK(strstr(uri, "RoleArn=arn:aws:iam::123456789012:role/test")
+ != NULL);
+ TEST_CHECK(strstr(uri, "ExternalId=external_id") != NULL);
+ TEST_CHECK(strstr(uri, "RoleSessionName=session_name") != NULL);
+
+ /* create an http client so that we can set the response */
+ c = flb_calloc(1, sizeof(struct flb_http_client));
+ if (!c) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(&c->headers);
+
+ c->resp.status = 200;
+ c->resp.payload = STS_RESPONSE_ASSUME_ROLE;
+ c->resp.payload_size = strlen(STS_RESPONSE_ASSUME_ROLE);
+
+ return c;
+}
+
+struct flb_http_client *request_sts_api_error(struct flb_aws_client *aws_client,
+ int method, const char *uri)
+{
+ struct flb_http_client *c;
+
+ TEST_CHECK(method == FLB_HTTP_GET);
+ TEST_CHECK(strstr(uri, "Action=AssumeRole") != NULL);
+ TEST_CHECK(strstr(uri, "RoleArn=arn:aws:iam::123456789012:role/apierror")
+ != NULL);
+ TEST_CHECK(strstr(uri, "ExternalId=external_id") != NULL);
+ TEST_CHECK(strstr(uri, "RoleSessionName=session_name") != NULL);
+
+ /* create an http client so that we can set the response */
+ c = flb_calloc(1, sizeof(struct flb_http_client));
+ if (!c) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(&c->headers);
+
+ c->resp.status = 400;
+ c->resp.payload = NULL;
+ c->resp.payload_size = 0;
+
+ return c;
+}
+
+/* test/mock version of the flb_aws_client request function */
+struct flb_http_client *test_http_client_request(struct flb_aws_client *aws_client,
+ int method, const char *uri,
+ const char *body, size_t body_len,
+ struct flb_aws_header
+ *dynamic_headers,
+ size_t dynamic_headers_len)
+{
+ g_request_count++;
+ if (strcmp(aws_client->name, "sts_client_eks_provider") == 0) {
+ /*
+ * route to the correct test case fn using the uri - the role
+ * name is different in each test case.
+ */
+ if (strstr(uri, "test1") != NULL) {
+ return request_eks_test1(aws_client, method, uri);
+ } else if (strstr(uri, "randomsession") != NULL) {
+ return request_eks_flb_sts_session_name(aws_client, method, uri);
+ } else if (strstr(uri, "apierror") != NULL) {
+ return request_eks_api_error(aws_client, method, uri);
+ } else if (strstr(uri, "unexpected_api_response") != NULL) {
+ return request_unexpected_response(aws_client, method, uri);
+ }
+
+ /* uri should match one of the above conditions */
+ flb_errno();
+ return NULL;
+ } else if (strcmp(aws_client->name, "sts_client_assume_role_provider") == 0)
+ {
+ if (strstr(uri, "test1") != NULL) {
+ return request_sts_test1(aws_client, method, uri);
+ } else if (strstr(uri, "apierror") != NULL) {
+ return request_sts_api_error(aws_client, method, uri);
+ } else if (strstr(uri, "unexpected_api_response") != NULL) {
+ return request_unexpected_response(aws_client, method, uri);
+ }
+ /* uri should match one of the above conditions */
+ flb_errno();
+ return NULL;
+ }
+
+ /* client name should match one of the above conditions */
+ flb_errno();
+ return NULL;
+
+}
+
+/* Test/mock flb_aws_client */
+static struct flb_aws_client_vtable test_vtable = {
+ .request = test_http_client_request,
+};
+
+struct flb_aws_client *test_http_client_create()
+{
+ struct flb_aws_client *client = flb_calloc(1,
+ sizeof(struct flb_aws_client));
+ if (!client) {
+ flb_errno();
+ return NULL;
+ }
+ client->client_vtable = &test_vtable;
+ return client;
+}
+
+/* Generator that returns clients with the test vtable */
+static struct flb_aws_client_generator test_generator = {
+ .create = test_http_client_create,
+};
+
+struct flb_aws_client_generator *generator_in_test()
+{
+ return &test_generator;
+}
+
+static void unsetenv_eks()
+{
+ int ret;
+
+ ret = unsetenv(TOKEN_FILE_ENV_VAR);
+ if (ret < 0) {
+ flb_errno();
+ return;
+ }
+ ret = unsetenv(ROLE_ARN_ENV_VAR);
+ if (ret < 0) {
+ flb_errno();
+ return;
+ }
+ ret = unsetenv(SESSION_NAME_ENV_VAR);
+ if (ret < 0) {
+ flb_errno();
+ return;
+ }
+}
+
+static void test_flb_sts_session_name()
+{
+ char *session_name = flb_sts_session_name();
+
+ TEST_CHECK(strlen(session_name) == 32);
+
+ flb_free(session_name);
+}
+
+static void test_sts_uri()
+{
+ flb_sds_t uri;
+
+ uri = flb_sts_uri("AssumeRole", "myrole", "mysession",
+ "myexternalid", NULL);
+ TEST_CHECK(strcmp(uri, "/?Version=2011-06-15&Action=AssumeRole"
+ "&RoleSessionName=mysession&RoleArn=myrole"
+ "&ExternalId=myexternalid") == 0);
+ flb_sds_destroy(uri);
+}
+
+static void test_process_sts_response()
+{
+ struct flb_aws_credentials *creds;
+ struct flb_config *config;
+ time_t expiration;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+ creds = flb_parse_sts_resp(STS_RESPONSE_EKS, &expiration);
+
+ TEST_CHECK(strcmp(EKS_ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(EKS_SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(EKS_TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+ flb_config_exit(config);
+}
+
+static void test_eks_provider() {
+ struct flb_config *config;
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ int ret;
+
+ g_request_count = 0;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ /* set env vars */
+ ret = setenv(ROLE_ARN_ENV_VAR, "arn:aws:iam::123456789012:role/test1", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(SESSION_NAME_ENV_VAR, "session_name", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(TOKEN_FILE_ENV_VAR, WEB_TOKEN_FILE, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_eks_provider_create(config, NULL, "us-west-2",
+ "https://sts.us-west-2.amazonaws.com",
+ NULL, generator_in_test());
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(EKS_ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(EKS_SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(EKS_TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(EKS_ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(EKS_SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(EKS_TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ /*
+ * Request count should be 2:
+ * - One for the first call to get_credentials (2nd should hit cred cache)
+ * - One for the call to refresh
+ */
+ TEST_CHECK(g_request_count == 2);
+
+ flb_aws_provider_destroy(provider);
+ unsetenv_eks();
+ flb_config_exit(config);
+}
+
+static void test_eks_provider_random_session_name() {
+ struct flb_config *config;
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ int ret;
+
+ g_request_count = 0;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ /* set env vars - session name is not set */
+ unsetenv_eks();
+ ret = setenv(ROLE_ARN_ENV_VAR,
+ "arn:aws:iam::123456789012:role/randomsession", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(TOKEN_FILE_ENV_VAR, WEB_TOKEN_FILE, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_eks_provider_create(config, NULL, "us-west-2",
+ "https://sts.us-west-2.amazonaws.com",
+ NULL, generator_in_test());
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(EKS_ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(EKS_SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(EKS_TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(EKS_ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(EKS_SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(EKS_TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ /*
+ * Request count should be 2:
+ * - One for the first call to get_credentials (2nd should hit cred cache)
+ * - One for the call to refresh
+ */
+ TEST_CHECK(g_request_count == 2);
+
+ flb_aws_provider_destroy(provider);
+ unsetenv_eks();
+ flb_config_exit(config);
+}
+
+/* unexpected output test- see description for STS_RESPONSE_MALFORMED */
+static void test_eks_provider_unexpected_api_response() {
+ struct flb_config *config;
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ int ret;
+
+ g_request_count = 0;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ unsetenv_eks();
+ ret = setenv(ROLE_ARN_ENV_VAR, "arn:aws:iam::123456789012:role/"
+ "unexpected_api_response", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(TOKEN_FILE_ENV_VAR, WEB_TOKEN_FILE, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_eks_provider_create(config, NULL, "us-west-2",
+ "https://sts.us-west-2.amazonaws.com",
+ NULL, generator_in_test());
+
+ /* API will return an error - creds will be NULL */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* refresh should return -1 (failure) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret < 0);
+
+ /*
+ * Request count should be 3:
+ * - Each call to get_credentials and refresh invokes the client's
+ * request method and returns a request failure.
+ */
+ TEST_CHECK(g_request_count == 3);
+
+ flb_aws_provider_destroy(provider);
+ unsetenv_eks();
+ flb_config_exit(config);
+}
+
+static void test_eks_provider_api_error() {
+ struct flb_config *config;
+ struct flb_aws_provider *provider;
+ struct flb_aws_credentials *creds;
+ int ret;
+
+ g_request_count = 0;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ unsetenv_eks();
+ ret = setenv(ROLE_ARN_ENV_VAR, "arn:aws:iam::123456789012:role/apierror",
+ 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(TOKEN_FILE_ENV_VAR, WEB_TOKEN_FILE, 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_eks_provider_create(config, NULL, "us-west-2",
+ "https://sts.us-west-2.amazonaws.com",
+ NULL, generator_in_test());
+
+ /* API will return an error - creds will be NULL */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* refresh should return -1 (failure) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret < 0);
+
+ /*
+ * Request count should be 3:
+ * - Each call to get_credentials and refresh invokes the client's
+ * request method and returns a request failure.
+ */
+ TEST_CHECK(g_request_count == 3);
+
+ flb_aws_provider_destroy(provider);
+ unsetenv_eks();
+ flb_config_exit(config);
+}
+
+static void test_sts_provider() {
+ struct flb_config *config;
+ struct flb_aws_provider *provider;
+ struct flb_aws_provider *base_provider;
+ struct flb_aws_credentials *creds;
+ int ret;
+
+ g_request_count = 0;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ /* use the env provider as the base provider */
+ /* set environment */
+ ret = setenv(AWS_ACCESS_KEY_ID, "base_akid", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SECRET_ACCESS_KEY, "base_skid", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SESSION_TOKEN, "base_token", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ base_provider = flb_aws_env_provider_create();
+ if (!base_provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_sts_provider_create(config, NULL, base_provider, "external_id",
+ "arn:aws:iam::123456789012:role/test1",
+ "session_name", "cn-north-1",
+ "https://sts.us-west-2.amazonaws.com",
+ NULL, generator_in_test());
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(STS_ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(STS_SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(STS_TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ if (!creds) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ TEST_CHECK(strcmp(STS_ACCESS_KEY, creds->access_key_id) == 0);
+ TEST_CHECK(strcmp(STS_SECRET_KEY, creds->secret_access_key) == 0);
+ TEST_CHECK(strcmp(STS_TOKEN, creds->session_token) == 0);
+
+ flb_aws_credentials_destroy(creds);
+
+ /* refresh should return 0 (success) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret == 0);
+
+ /*
+ * Request count should be 2:
+ * - One for the first call to get_credentials (2nd should hit cred cache)
+ * - One for the call to refresh
+ */
+ TEST_CHECK(g_request_count == 2);
+
+ flb_aws_provider_destroy(base_provider);
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+static void test_sts_provider_api_error() {
+ struct flb_config *config;
+ struct flb_aws_provider *provider;
+ struct flb_aws_provider *base_provider;
+ struct flb_aws_credentials *creds;
+ int ret;
+
+ g_request_count = 0;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ /* use the env provider as the base provider */
+ /* set environment */
+ ret = setenv(AWS_ACCESS_KEY_ID, "base_akid", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SECRET_ACCESS_KEY, "base_skid", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SESSION_TOKEN, "base_token", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ base_provider = flb_aws_env_provider_create();
+ if (!base_provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_sts_provider_create(config, NULL, base_provider, "external_id",
+ "arn:aws:iam::123456789012:role/apierror",
+ "session_name", "cn-north-1",
+ "https://sts.us-west-2.amazonaws.com",
+ NULL,
+ generator_in_test());
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* refresh should return -1 (failure) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret < 0);
+
+ /*
+ * Request count should be 3:
+ * - Each call to get_credentials and refresh invokes the client's
+ * request method and returns a request failure.
+ */
+ TEST_CHECK(g_request_count == 3);
+
+ flb_aws_provider_destroy(base_provider);
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+/* unexpected output test- see description for STS_RESPONSE_MALFORMED */
+static void test_sts_provider_unexpected_api_response() {
+ struct flb_config *config;
+ struct flb_aws_provider *provider;
+ struct flb_aws_provider *base_provider;
+ struct flb_aws_credentials *creds;
+ int ret;
+
+ g_request_count = 0;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ /* use the env provider as the base provider */
+ /* set environment */
+ ret = setenv(AWS_ACCESS_KEY_ID, "base_akid", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SECRET_ACCESS_KEY, "base_skid", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+ ret = setenv(AWS_SESSION_TOKEN, "base_token", 1);
+ if (ret < 0) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ base_provider = flb_aws_env_provider_create();
+ if (!base_provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ provider = flb_sts_provider_create(config, NULL, base_provider, "external_id",
+ "arn:aws:iam::123456789012:role/"
+ "unexpected_api_response",
+ "session_name", "cn-north-1",
+ "https://sts.us-west-2.amazonaws.com",
+ NULL,
+ generator_in_test());
+ if (!provider) {
+ flb_errno();
+ flb_config_exit(config);
+ return;
+ }
+
+ /* repeated calls to get credentials should return the same set */
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ creds = provider->provider_vtable->get_credentials(provider);
+ TEST_CHECK(creds == NULL);
+
+ /* refresh should return -1 (failure) */
+ ret = provider->provider_vtable->refresh(provider);
+ TEST_CHECK(ret < 0);
+
+ /*
+ * Request count should be 3:
+ * - Each call to get_credentials and refresh invokes the client's
+ * request method and returns a request failure.
+ */
+ TEST_CHECK(g_request_count == 3);
+
+ flb_aws_provider_destroy(base_provider);
+ flb_aws_provider_destroy(provider);
+ flb_config_exit(config);
+}
+
+
+TEST_LIST = {
+ { "test_flb_sts_session_name" , test_flb_sts_session_name},
+ { "test_sts_uri" , test_sts_uri},
+ { "process_sts_response" , test_process_sts_response},
+ { "eks_credential_provider" , test_eks_provider},
+ { "eks_credential_provider_random_session_name" ,
+ test_eks_provider_random_session_name},
+ { "test_eks_provider_unexpected_api_response" ,
+ test_eks_provider_unexpected_api_response},
+ { "eks_credential_provider_api_error" , test_eks_provider_api_error},
+ { "sts_credential_provider" , test_sts_provider},
+ { "sts_credential_provider_api_error" , test_sts_provider_api_error},
+ { "sts_credential_provider_unexpected_api_response" ,
+ test_sts_provider_unexpected_api_response},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/aws_credentials_test_internal.h b/fluent-bit/tests/internal/aws_credentials_test_internal.h
new file mode 100644
index 00000000..bd335782
--- /dev/null
+++ b/fluent-bit/tests/internal/aws_credentials_test_internal.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#ifndef AWS_CREDENTIALS_TEST_INTERNAL_H
+
+#define AWS_CREDENTIALS_TEST_INTERNAL_H
+
+#include "flb_tests_internal.h"
+
+#define AWS_TEST_DATA_PATH(FILE_PATH) FLB_TESTS_DATA_PATH "data/aws_credentials/" FILE_PATH
+
+static int unset_profile_env()
+{
+ int ret;
+ ret = unsetenv("HOME");
+ if (ret < 0) {
+ flb_errno();
+ return -1;
+ }
+ ret = unsetenv("AWS_CONFIG_FILE");
+ if (ret < 0) {
+ flb_errno();
+ return -1;
+ }
+ ret = unsetenv("AWS_SHARED_CREDENTIALS_FILE");
+ if (ret < 0) {
+ flb_errno();
+ return -1;
+ }
+ ret = unsetenv("AWS_DEFAULT_PROFILE");
+ if (ret < 0) {
+ flb_errno();
+ return -1;
+ }
+ ret = unsetenv("AWS_PROFILE");
+ if (ret < 0) {
+ flb_errno();
+ return -1;
+ }
+ return 0;
+}
+
+#endif /* AWS_CREDENTIALS_TEST_INTERNAL_H */
diff --git a/fluent-bit/tests/internal/aws_util.c b/fluent-bit/tests/internal/aws_util.c
new file mode 100644
index 00000000..6f6fbdb1
--- /dev/null
+++ b/fluent-bit/tests/internal/aws_util.c
@@ -0,0 +1,395 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_aws_util.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_pthread.h>
+
+#include "flb_tests_internal.h"
+
+#define S3_KEY_FORMAT_TAG_PART "logs/$TAG[2]/$TAG[0]/%Y/%m/%d"
+#define S3_OBJECT_KEY_TAG_PART "logs/ccc/aa/2020/08/15"
+
+#define S3_KEY_FORMAT_FULL_TAG "logs/$TAG/%Y/%m/%d"
+#define S3_OBJECT_KEY_FULL_TAG "logs/aa.bb.ccc/2020/08/15"
+
+#define S3_KEY_FORMAT_SPECIAL_CHARCATERS_TAG "logs/my.great_photos-2020:jan/$TAG/%Y/%m/%d"
+#define S3_OBJECT_KEY_SPECIAL_CHARCATERS_TAG "logs/my.great_photos-2020:jan/aa.bb.ccc/2020/08/15"
+
+#define S3_OBJECT_KEY_INVALID_DELIMITER "logs/aa.bb-ccc[2]/aa.bb-ccc/2020/08/15"
+
+#define S3_KEY_FORMAT_INVALID_TAG "logs/$TAG[2]/$TAG[-1]/%Y/%m/%d"
+#define S3_OBJECY_KEY_INVALID_TAG "logs/ccc/aa.bb.ccc[-1]/2020/08/15"
+
+#define S3_KEY_FORMAT_OUT_OF_BOUNDS_TAG "logs/$TAG[2]/$TAG[]/%Y/%m/%d"
+
+#define S3_KEY_FORMAT_STATIC_STRING "logs/fluent-bit"
+
+#define S3_KEY_FORMAT_UUID "logs/$UUID"
+#define S3_OBJECT_KEY_UUID "logs/"
+
+#define S3_KEY_FORMAT_ALL_OPTIONS "logs/$TAG[2]/$TAG[1]/$TAG[0]/%Y/%m/%d/file-$INDEX-$UUID"
+#define S3_OBJECT_KEY_ALL_OPTIONS "logs/ccc/bb/aa/2020/08/15/file-0-"
+
+#define S3_KEY_FORMAT_VALID_INDEX "logs/a-$INDEX-b-c"
+#define S3_OBJECT_KEY_VALID_INDEX "logs/a-12-b-c"
+#define S3_OBJECT_KEY_PRE_OVERFLOW_INDEX "logs/a-18446744073709551615-b-c"
+#define S3_OBJECT_KEY_POST_OVERFLOW_INDEX "logs/a-0-b-c"
+
+#define S3_KEY_FORMAT_MIXED_TIMESTAMP "logs/%Y/m/%m/d/%d/%q"
+#ifdef FLB_SYSTEM_MACOS
+/* macOS's strftime throws away for % character for % and suqsequent invalid format character. */
+#define S3_OBJECT_KEY_MIXED_TIMESTAMP "logs/2020/m/08/d/15/q"
+#else
+#define S3_OBJECT_KEY_MIXED_TIMESTAMP "logs/2020/m/08/d/15/%q"
+#endif
+
+#define NO_TAG ""
+#define TAG "aa.bb.ccc"
+#define MULTI_DELIMITER_TAG "aa.bb-ccc"
+#define TAG_DELIMITER "."
+#define TAG_DELIMITERS ".-"
+#define INVALID_TAG_DELIMITERS ",/"
+#define VALID_SEQ_INDEX 0
+
+static void initialization_crutch()
+{
+ struct flb_config *config;
+
+ config = flb_config_init();
+
+ if (config == NULL) {
+ return;
+ }
+
+ flb_config_exit(config);
+}
+
+
+pthread_mutex_t env_mutex = PTHREAD_MUTEX_INITIALIZER;
+static int mktime_utc(struct tm *day, time_t *tm)
+{
+ int ret;
+ char *tzvar = NULL;
+ char orig_tz[256] = {0};
+ time_t t;
+
+ if (!TEST_CHECK(day != NULL)) {
+ TEST_MSG("struct tm is null");
+ return -1;
+ }
+ if (!TEST_CHECK(tm != NULL)) {
+ TEST_MSG("time_t is null");
+ return -1;
+ }
+
+ pthread_mutex_lock(&env_mutex);
+
+ /* save current TZ var */
+ tzvar = getenv("TZ");
+ if (tzvar != NULL) {
+ if (!TEST_CHECK(strlen(tzvar) <= sizeof(orig_tz))) {
+ TEST_MSG("TZ is large. len=%ld TZ=%s", strlen(tzvar), tzvar);
+ pthread_mutex_unlock(&env_mutex);
+ return -1;
+ }
+ strncpy(&orig_tz[0], tzvar, sizeof(orig_tz));
+ }
+
+ /* setenv is not thread safe */
+ ret = setenv("TZ", "UTC", 1);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("setenv failed");
+ pthread_mutex_unlock(&env_mutex);
+ return -1;
+ }
+
+ t = mktime(day);
+ *tm = t;
+
+ /* restore TZ */
+ if (tzvar != NULL) {
+ ret = setenv("TZ", &orig_tz[0], 1);
+ }
+ else {
+ ret = unsetenv("TZ");
+ }
+
+ pthread_mutex_unlock(&env_mutex);
+
+ return ret;
+}
+
+static void test_flb_aws_error()
+{
+ flb_sds_t error_type;
+ char *api_response = "{\"__type\":\"IncompleteSignatureException\","
+ "\"message\": \"Credential must have exactly 5 "
+ "slash-delimited elements, e.g. keyid/date/region/"
+ "service/term, got '<Credential>'\"}";
+ char *garbage = "garbage"; /* something that can't be parsed */
+
+ initialization_crutch();
+
+ error_type = flb_aws_error(api_response, strlen(api_response));
+
+ TEST_CHECK(strcmp("IncompleteSignatureException", error_type) == 0);
+
+ flb_sds_destroy(error_type);
+
+ error_type = flb_aws_error(garbage, strlen(garbage));
+
+ TEST_CHECK(error_type == NULL);
+
+ flb_sds_destroy(error_type);
+}
+
+static void test_flb_aws_endpoint()
+{
+ char *endpoint;
+
+ initialization_crutch();
+
+ endpoint = flb_aws_endpoint("cloudwatch", "ap-south-1");
+
+ TEST_CHECK(strcmp("cloudwatch.ap-south-1.amazonaws.com",
+ endpoint) == 0);
+ flb_free(endpoint);
+
+ /* China regions have a different TLD */
+ endpoint = flb_aws_endpoint("cloudwatch", "cn-north-1");
+
+ TEST_CHECK(strcmp("cloudwatch.cn-north-1.amazonaws.com.cn",
+ endpoint) == 0);
+ flb_free(endpoint);
+
+}
+
+static void test_flb_get_s3_key_multi_tag_exists()
+{
+ flb_sds_t s3_key_format = NULL;
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_TAG_PART, t, TAG, TAG_DELIMITER, 0);
+ TEST_CHECK(strcmp(s3_key_format, S3_OBJECT_KEY_TAG_PART) == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_full_tag()
+{
+ flb_sds_t s3_key_format = NULL;
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_FULL_TAG, t, TAG, TAG_DELIMITER, 0);
+ TEST_CHECK(strcmp(s3_key_format, S3_OBJECT_KEY_FULL_TAG) == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_tag_special_characters()
+{
+ flb_sds_t s3_key_format = NULL;
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_SPECIAL_CHARCATERS_TAG, t, TAG,
+ TAG_DELIMITER, 0);
+ TEST_CHECK(strcmp(s3_key_format, S3_OBJECT_KEY_SPECIAL_CHARCATERS_TAG) == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_multi_tag_delimiter()
+{
+ flb_sds_t s3_key_format = NULL;
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_TAG_PART, t, MULTI_DELIMITER_TAG,
+ TAG_DELIMITERS, 0);
+ TEST_CHECK(strcmp(s3_key_format, S3_OBJECT_KEY_TAG_PART) == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_invalid_tag_delimiter()
+{
+ flb_sds_t s3_key_format = NULL;
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_TAG_PART, t, MULTI_DELIMITER_TAG,
+ INVALID_TAG_DELIMITERS, 0);
+ TEST_CHECK(strcmp(s3_key_format, S3_OBJECT_KEY_INVALID_DELIMITER) == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_invalid_tag_index()
+{
+ flb_sds_t s3_key_format = NULL;
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_INVALID_TAG, t, TAG, TAG_DELIMITER, 0);
+ TEST_CHECK(strcmp(s3_key_format, S3_OBJECY_KEY_INVALID_TAG) == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_invalid_key_length()
+{
+ int i;
+ char buf[1100] = "";
+ char tmp[1024] = "";
+ flb_sds_t s3_key_format = NULL;
+
+ initialization_crutch();
+
+ for (i = 0; i <= 975; i++){
+ tmp[i] = 'a';
+ }
+ snprintf(buf, sizeof(buf), "%s%s", S3_KEY_FORMAT_SPECIAL_CHARCATERS_TAG, tmp);
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(buf, t, TAG, TAG_DELIMITER, 0);
+ TEST_CHECK(strlen(s3_key_format) <= 1024);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_static_string()
+{
+ flb_sds_t s3_key_format = NULL;
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_STATIC_STRING, t, NO_TAG,
+ TAG_DELIMITER, 0);
+ TEST_CHECK(strcmp(s3_key_format, S3_KEY_FORMAT_STATIC_STRING) == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_valid_index()
+{
+ flb_sds_t s3_key_format = NULL;
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_VALID_INDEX, t, NO_TAG,
+ TAG_DELIMITER, 12);
+ TEST_CHECK(strcmp(s3_key_format, S3_OBJECT_KEY_VALID_INDEX) == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_increment_index()
+{
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+ flb_sds_t s3_key_format = NULL;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_VALID_INDEX, t, NO_TAG,
+ TAG_DELIMITER, 5);
+
+ TEST_CHECK(strcmp(s3_key_format, "logs/a-5-b-c") == 0);
+
+ flb_sds_destroy(s3_key_format);
+
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_VALID_INDEX, t, NO_TAG,
+ TAG_DELIMITER, 10);
+
+ TEST_CHECK(strcmp(s3_key_format, "logs/a-10-b-c") == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_index_overflow()
+{
+ flb_sds_t s3_key_format = NULL;
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+ uint64_t index = 18446744073709551615U;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_VALID_INDEX, t, NO_TAG,
+ TAG_DELIMITER, index);
+ TEST_CHECK(strcmp(s3_key_format, S3_OBJECT_KEY_PRE_OVERFLOW_INDEX) == 0);
+ flb_sds_destroy(s3_key_format);
+
+ index++;
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_VALID_INDEX, t, NO_TAG,
+ TAG_DELIMITER, index);
+ TEST_CHECK(strcmp(s3_key_format, S3_OBJECT_KEY_POST_OVERFLOW_INDEX) == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+static void test_flb_get_s3_key_mixed_timestamp()
+{
+ flb_sds_t s3_key_format = NULL;
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+
+ initialization_crutch();
+
+ mktime_utc(&day, &t);
+ s3_key_format = flb_get_s3_key(S3_KEY_FORMAT_MIXED_TIMESTAMP, t, NO_TAG,
+ TAG_DELIMITER, 12);
+ TEST_CHECK(strcmp(s3_key_format, S3_OBJECT_KEY_MIXED_TIMESTAMP) == 0);
+
+ flb_sds_destroy(s3_key_format);
+}
+
+TEST_LIST = {
+ { "parse_api_error" , test_flb_aws_error},
+ { "flb_aws_endpoint" , test_flb_aws_endpoint},
+ {"flb_get_s3_key_multi_tag_exists", test_flb_get_s3_key_multi_tag_exists},
+ {"flb_get_s3_key_full_tag", test_flb_get_s3_key_full_tag},
+ {"flb_get_s3_key_tag_special_characters", test_flb_get_s3_key_tag_special_characters},
+ {"flb_get_s3_key_multi_tag_delimiter", test_flb_get_s3_key_multi_tag_delimiter},
+ {"flb_get_s3_key_invalid_tag_delimiter", test_flb_get_s3_key_invalid_tag_delimiter},
+ {"flb_get_s3_key_invalid_tag_index", test_flb_get_s3_key_invalid_tag_index},
+ {"flb_get_s3_key_invalid_key_length", test_flb_get_s3_key_invalid_key_length},
+ {"flb_get_s3_key_static_string", test_flb_get_s3_key_static_string},
+ {"flb_get_s3_key_valid_index", test_flb_get_s3_key_valid_index},
+ {"flb_get_s3_key_increment_index", test_flb_get_s3_key_increment_index},
+ {"flb_get_s3_key_index_overflow", test_flb_get_s3_key_index_overflow},
+ {"flb_get_s3_key_mixed_timestamp", test_flb_get_s3_key_mixed_timestamp},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/base64.c b/fluent-bit/tests/internal/base64.c
new file mode 100644
index 00000000..4375870f
--- /dev/null
+++ b/fluent-bit/tests/internal/base64.c
@@ -0,0 +1,50 @@
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_base64.h>
+#include <fluent-bit/flb_time.h>
+
+#include "flb_tests_internal.h"
+
+static void b64_basic_test_encode()
+{
+ char* data = "Hello world";
+ char out[100];
+ char* expect = "SGVsbG8gd29ybGQ=";
+ size_t olen;
+ out[16] = 'X';
+
+ flb_base64_encode((unsigned char *) out, 100, &olen, (unsigned char *)data, 11);
+
+ TEST_CHECK(strlen(out) == 16 && olen == 16);
+ TEST_MSG("Base64 encode failed to output result of expected length");
+
+ TEST_CHECK(strcmp(out, expect) == 0);
+ TEST_MSG("Base64 encode failed to output result of expected value");
+
+ TEST_CHECK(out[16] == 0);
+ TEST_MSG("Base64 not null terminated");
+ return;
+}
+
+static void b64_basic_test_decode()
+{
+ char* data = "SGVsbG8gd29ybGQ=";
+ char out[100] = { 0 };
+ char* expect = "Hello world";
+ size_t olen;
+
+ flb_base64_decode((unsigned char *) out, 100, &olen, (unsigned char *)data, 16);
+
+ TEST_CHECK(strlen(out) == 11 && olen == 11);
+ TEST_MSG("Base64 decode failed to output result of expected length");
+
+ TEST_CHECK(strcmp(out, expect) == 0);
+ TEST_MSG("Base64 decode failed to output result of expected value");
+ return;
+}
+
+TEST_LIST = {
+ { "b64_basic_test_encode" , b64_basic_test_encode },
+ { "b64_basic_test_decode", b64_basic_test_decode },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/bucket_queue.c b/fluent-bit/tests/internal/bucket_queue.c
new file mode 100644
index 00000000..ddc93597
--- /dev/null
+++ b/fluent-bit/tests/internal/bucket_queue.c
@@ -0,0 +1,248 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_mp.h>
+#include <msgpack.h>
+
+#include "flb_tests_internal.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fluent-bit/flb_bucket_queue.h>
+#include <monkey/mk_core/mk_list.h>
+
+#define TST_PRIORITY_OP_ADD 1
+#define TST_PRIORITY_OP_DELETE 2
+#define TST_PRIORITY_OP_EXPECT 3
+#define TST_PRIORITY_OP_POP 3
+
+
+struct bucket_queue_entry {
+ char *tag;
+ size_t priority;
+ struct mk_list item;
+ size_t add_idx;
+};
+
+struct bucket_queue_op_add {
+ struct bucket_queue_entry *entries;
+};
+
+struct bucket_queue_op_expect {
+ char *tag;
+};
+
+struct bucket_queue_op_pop {
+ char *tag;
+};
+
+union bucket_queue_op_union {
+ struct bucket_queue_op_pop pop;
+ struct bucket_queue_op_add add;
+ struct bucket_queue_op_expect expect;
+};
+
+struct bucket_queue_op {
+ char op;
+ void *op_description;
+};
+
+void test_create_destroy()
+{
+ struct flb_bucket_queue *bucket_queue;
+ bucket_queue = flb_bucket_queue_create(100);
+ flb_bucket_queue_destroy(bucket_queue);
+}
+
+void test_add_priorities()
+{
+ struct flb_bucket_queue *bucket_queue;
+
+ struct bucket_queue_op operations[] = {
+ {
+ TST_PRIORITY_OP_ADD,
+ &(struct bucket_queue_op_add) {
+ (struct bucket_queue_entry[]) {
+ {
+ "a",
+ 2 /* priority */
+ },
+ {
+ "b",
+ 2 /* priority */
+ },
+ {
+ "c",
+ 0 /* priority */
+ },
+ {
+ "d",
+ 2 /* priority */
+ },
+ {
+ "e",
+ 3 /* priority */
+ },
+ { 0 }
+ }
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "c"
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "a"
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "b"
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "d"
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "e"
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "<EXPECT_NULL>"
+ }
+ },
+ {
+ TST_PRIORITY_OP_ADD,
+ &(struct bucket_queue_op_add) {
+ (struct bucket_queue_entry[]) {
+ {
+ "f",
+ 1 /* priority */
+ },
+ {
+ "g",
+ 1 /* priority */
+ },
+ {
+ "h",
+ 0
+ },
+ { 0 }
+ }
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "h"
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "f"
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "g"
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "<EXPECT_NULL>"
+ }
+ },
+ {
+ TST_PRIORITY_OP_POP,
+ &(struct bucket_queue_op_pop) {
+ "<EXPECT_NULL>"
+ }
+ },
+ { 0 }
+ };
+
+ bucket_queue = flb_bucket_queue_create(4);
+ struct bucket_queue_op *op;
+ struct bucket_queue_op_add *op_desc_add;
+ struct bucket_queue_op_pop *op_desc_pop;
+ struct bucket_queue_entry *bucket_queue_entry;
+ struct mk_list *list_entry;
+ size_t add_idx = 0;
+ int ret;
+ int retB;
+
+ for (op = operations; op->op != 0; ++op) {
+ if (op->op == TST_PRIORITY_OP_ADD) {
+ op_desc_add = (struct bucket_queue_op_add *) op->op_description;
+ for (bucket_queue_entry = op_desc_add->entries; bucket_queue_entry->tag != 0;
+ ++bucket_queue_entry) {
+ bucket_queue_entry->add_idx = add_idx++;
+ ret = flb_bucket_queue_add(bucket_queue, &bucket_queue_entry->item,
+ bucket_queue_entry->priority);
+ TEST_CHECK(ret == 0);
+ TEST_MSG("[op %zu][entry %zu] Add failed. Returned %d", op - operations,
+ bucket_queue_entry - op_desc_add->entries, ret);
+ }
+ }
+
+ else if (op->op == TST_PRIORITY_OP_POP) {
+ op_desc_pop = (struct bucket_queue_op_pop *) op->op_description;
+ list_entry = flb_bucket_queue_pop_min(bucket_queue);
+ ret = strcmp("<EXPECT_NULL>", op_desc_pop->tag);
+
+ if (ret == 0) {
+ TEST_CHECK(list_entry == NULL);
+ TEST_MSG("[op %zu] Pop failed.", op - operations);
+ TEST_MSG("Expect: null");
+ TEST_MSG("Produced: list_entry %p", list_entry);
+ }
+ else {
+ TEST_CHECK(list_entry != NULL);
+ TEST_MSG("[op %zu] Pop failed.", op - operations);
+ TEST_MSG("Expect: non-null");
+ TEST_MSG("Produced: null");
+
+ bucket_queue_entry = mk_list_entry(list_entry, struct bucket_queue_entry, item);
+ ret = strcmp(bucket_queue_entry->tag, op_desc_pop->tag);
+ TEST_CHECK(ret == 0);
+ TEST_MSG("[op %zu] Pop failed.", op - operations);
+ TEST_MSG("Expect tag: %s", op_desc_pop->tag);
+ TEST_MSG("Produced tag: %s", bucket_queue_entry->tag);
+
+ ret = strlen(bucket_queue_entry->tag);
+ retB = strlen(op_desc_pop->tag);
+ TEST_CHECK(ret == retB);
+ TEST_MSG("[op %zu] Pop failed.", op - operations);
+ TEST_MSG("Expect tag len: %d", retB);
+ TEST_MSG("Produced tag len: %d", ret);
+ }
+ }
+ }
+
+ flb_bucket_queue_destroy(bucket_queue);
+}
+
+TEST_LIST = {
+ {"create_destroy" , test_create_destroy},
+ {"add_priorities" , test_add_priorities},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/config_format.c b/fluent-bit/tests/internal/config_format.c
new file mode 100644
index 00000000..7f887a54
--- /dev/null
+++ b/fluent-bit/tests/internal/config_format.c
@@ -0,0 +1,86 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_kv.h>
+#include <fluent-bit/flb_config_format.h>
+
+#include <cfl/cfl.h>
+#include <cfl/cfl_variant.h>
+#include <cfl/cfl_kvlist.h>
+
+#include "flb_tests_internal.h"
+
+void test_api()
+{
+ struct flb_cf *cf;
+ struct flb_cf_section *s_tmp;
+ struct flb_cf_section *service;
+ struct flb_cf_group *g_tmp;
+ struct cfl_variant *ret;
+ struct flb_kv *meta;
+
+ /* create context */
+ cf = flb_cf_create();
+ TEST_CHECK(cf != NULL);
+
+ /* create service section */
+ service = flb_cf_section_create(cf, "SERVICE", 7);
+ TEST_CHECK(service != NULL);
+
+ /* add a property */
+ ret = flb_cf_section_property_add(cf, service->properties, "key", 3, "val", 3);
+ TEST_CHECK(ret != NULL);
+
+ /* add a property with empty spaces on left/right */
+ ret = flb_cf_section_property_add(cf, service->properties, " key ", 5, " val ", 7);
+ TEST_CHECK(ret != NULL);
+
+ /* add an invalid property */
+ ret = flb_cf_section_property_add(cf, service->properties, " ", 3, "", 0);
+ TEST_CHECK(ret == NULL);
+
+ /* try to add another 'SERVICE' section, it should return the same one */
+ s_tmp = flb_cf_section_create(cf, "SERVICE", 7);
+ TEST_CHECK(s_tmp == service);
+
+ /* add a valid section */
+ s_tmp = flb_cf_section_create(cf, "INPUT", 5);
+ TEST_CHECK(s_tmp != NULL);
+
+ TEST_CHECK(mk_list_size(&cf->inputs) == 1);
+
+ /* add property to the section recently created */
+ ret = flb_cf_section_property_add(cf, s_tmp->properties, "key", 3, "val", 3);
+ TEST_CHECK(ret != NULL);
+
+ /* groups: add groups to the last section created */
+ g_tmp = flb_cf_group_create(cf, s_tmp, "FLUENT GROUP", 12);
+ TEST_CHECK(g_tmp != NULL);
+
+ /* add properties to the group */
+ ret = flb_cf_section_property_add(cf, g_tmp->properties, "key", 3, "val", 3);
+ TEST_CHECK(ret != NULL);
+
+ /* groups: invalid group */
+ g_tmp = flb_cf_group_create(cf, s_tmp, "", 0);
+ TEST_CHECK(g_tmp == NULL);
+
+ /* Meta commands */
+ meta = flb_cf_meta_property_add(cf, "@SET a=1 ", 20);
+ TEST_CHECK(meta != NULL);
+ TEST_CHECK(flb_sds_len(meta->key) == 3 && strcmp(meta->key, "SET") == 0);
+ TEST_CHECK(flb_sds_len(meta->val) == 3 && strcmp(meta->val, "a=1") == 0);
+
+ /* invalid meta */
+ meta = flb_cf_meta_property_add(cf, "@a=1 ", 5);
+ TEST_CHECK(meta == NULL);
+
+ /* destroy context */
+ flb_cf_destroy(cf);
+}
+
+TEST_LIST = {
+ { "api" , test_api},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/config_format_fluentbit.c b/fluent-bit/tests/internal/config_format_fluentbit.c
new file mode 100644
index 00000000..3f158573
--- /dev/null
+++ b/fluent-bit/tests/internal/config_format_fluentbit.c
@@ -0,0 +1,353 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_kv.h>
+#include <fluent-bit/flb_config_format.h>
+#include <fluent-bit/flb_sds.h>
+#include <sys/stat.h>
+
+#include <cfl/cfl.h>
+#include <cfl/cfl_list.h>
+
+#include "flb_tests_internal.h"
+
+#define FLB_000 FLB_TESTS_DATA_PATH "/data/config_format/classic/fluent-bit.conf"
+#define FLB_001 FLB_TESTS_DATA_PATH "/data/config_format/classic/issue_5880.conf"
+#define FLB_002 FLB_TESTS_DATA_PATH "/data/config_format/classic/indent_level_error.conf"
+#define FLB_003 FLB_TESTS_DATA_PATH "/data/config_format/classic/recursion.conf"
+#define FLB_004 FLB_TESTS_DATA_PATH "/data/config_format/classic/issue6281.conf"
+#define FLB_005 FLB_TESTS_DATA_PATH "/data/config_format/classic/nolimitline.conf"
+
+#define ERROR_LOG "fluentbit_conf_error.log"
+
+/* data/config_format/fluent-bit.conf */
+void test_basic()
+{
+ struct mk_list *head;
+ struct flb_cf *cf;
+ struct flb_cf_section *s;
+ struct flb_cf_group *g;
+
+ cf = flb_cf_fluentbit_create(NULL, FLB_000, NULL, 0);
+ TEST_CHECK(cf != NULL);
+
+ /* Total number of sections */
+ TEST_CHECK(mk_list_size(&cf->sections) == 8);
+
+ /* SERVICE check */
+ TEST_CHECK(cf->service != NULL);
+ if (cf->service) {
+ TEST_CHECK(cfl_list_size(&cf->service->properties->list) == 3);
+ }
+
+ /* Meta commands */
+ TEST_CHECK(mk_list_size(&cf->metas) == 2);
+
+ /* Check number sections per list */
+ TEST_CHECK(mk_list_size(&cf->parsers) == 1);
+ TEST_CHECK(mk_list_size(&cf->multiline_parsers) == 1);
+ TEST_CHECK(mk_list_size(&cf->customs) == 1);
+ TEST_CHECK(mk_list_size(&cf->inputs) == 1);
+ TEST_CHECK(mk_list_size(&cf->filters) == 1);
+ TEST_CHECK(mk_list_size(&cf->outputs) == 1);
+ TEST_CHECK(mk_list_size(&cf->others) == 1);
+
+ /* groups */
+ s = flb_cf_section_get_by_name(cf, "input");
+ TEST_CHECK(s != NULL);
+ TEST_CHECK(mk_list_size(&s->groups) == 2);
+
+ mk_list_foreach(head, &s->groups) {
+ g = mk_list_entry(head, struct flb_cf_group, _head);
+ TEST_CHECK(cfl_list_size(&g->properties->list) == 2);
+ }
+
+ printf("\n");
+ flb_cf_dump(cf);
+
+ flb_cf_destroy(cf);
+}
+
+struct str_list {
+ size_t size;
+ char **lists;
+};
+
+static int check_str_list(struct str_list *list, FILE *fp)
+{
+ flb_sds_t error_str = NULL;
+ char *p = NULL;
+ size_t size;
+ int str_size = 4096;
+ int i;
+ int fd = fileno(fp);
+ struct stat st;
+
+ if (!TEST_CHECK(list != NULL)) {
+ TEST_MSG("list is NULL");
+ return -1;
+ }
+ if (!TEST_CHECK(fp != NULL)) {
+ TEST_MSG("fp is NULL");
+ return -1;
+ }
+
+ if (!TEST_CHECK(fstat(fd, &st) == 0)) {
+ TEST_MSG("fstat failed");
+ return -1;
+ }
+ str_size = st.st_size;
+ error_str = flb_sds_create_size(str_size);
+ if (!TEST_CHECK(error_str != NULL)) {
+ TEST_MSG("flb_sds_create_size failed.");
+ return -1;
+ }
+
+ size = fread(error_str, 1, str_size, fp);
+ if (size < str_size) {
+ if (!TEST_CHECK(ferror(fp) == 0)) {
+ TEST_MSG("fread failed.");
+ clearerr(fp);
+ flb_sds_destroy(error_str);
+ return -1;
+ }
+ clearerr(fp);
+ }
+
+ for (i=0; i<list->size; i++) {
+ p = strstr(error_str, list->lists[i]);
+ if (!TEST_CHECK(p != NULL)) {
+ TEST_MSG(" Got :%s\n expect:%s", error_str, list->lists[i]);
+ }
+ }
+
+ flb_sds_destroy(error_str);
+ return 0;
+}
+
+
+/* https://github.com/fluent/fluent-bit/issues/5880 */
+void missing_value()
+{
+ struct flb_cf *cf;
+ FILE *fp = NULL;
+ char *expected_strs[] = {"undefined value", ":9:" /* lieno*/ };
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ unlink(ERROR_LOG);
+
+ fp = freopen(ERROR_LOG, "w+", stderr);
+ if (!TEST_CHECK(fp != NULL)) {
+ TEST_MSG("freopen failed. errno=%d path=%s", errno, ERROR_LOG);
+ exit(EXIT_FAILURE);
+ }
+
+ cf = flb_cf_fluentbit_create(NULL, FLB_001, NULL, 0);
+ TEST_CHECK(cf == NULL);
+ fflush(fp);
+ fclose(fp);
+
+ fp = fopen(ERROR_LOG, "r");
+ if (!TEST_CHECK(fp != NULL)) {
+ TEST_MSG("fopen failed. errno=%d path=%s", errno, ERROR_LOG);
+ unlink(ERROR_LOG);
+ exit(EXIT_FAILURE);
+ }
+
+ check_str_list(&expected, fp);
+
+ fclose(fp);
+ unlink(ERROR_LOG);
+}
+
+void indent_level_error()
+{
+ struct flb_cf *cf;
+ FILE *fp = NULL;
+ char *expected_strs[] = {"invalid", "indent", "level"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ unlink(ERROR_LOG);
+
+ fp = freopen(ERROR_LOG, "w+", stderr);
+ if (!TEST_CHECK(fp != NULL)) {
+ TEST_MSG("freopen failed. errno=%d path=%s", errno, ERROR_LOG);
+ exit(EXIT_FAILURE);
+ }
+
+ cf = flb_cf_create();
+ if (!TEST_CHECK(cf != NULL)) {
+ TEST_MSG("flb_cf_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ cf = flb_cf_fluentbit_create(cf, FLB_002, NULL, 0);
+ TEST_CHECK(cf == NULL);
+ fflush(fp);
+ fclose(fp);
+
+ fp = fopen(ERROR_LOG, "r");
+ if (!TEST_CHECK(fp != NULL)) {
+ TEST_MSG("fopen failed. errno=%d path=%s", errno, ERROR_LOG);
+ if (cf != NULL) {
+ flb_cf_destroy(cf);
+ }
+ unlink(ERROR_LOG);
+ exit(EXIT_FAILURE);
+ }
+
+ check_str_list(&expected, fp);
+ if (cf != NULL) {
+ flb_cf_destroy(cf);
+ }
+ fclose(fp);
+ unlink(ERROR_LOG);
+}
+
+void recursion()
+{
+ struct flb_cf *cf;
+
+ cf = flb_cf_create();
+ if (!TEST_CHECK(cf != NULL)) {
+ TEST_MSG("flb_cf_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ cf = flb_cf_fluentbit_create(cf, FLB_003, NULL, 0);
+
+ /* No SIGSEGV means success */
+}
+
+
+/*
+ * https://github.com/fluent/fluent-bit/issues/6281
+ *
+ * Fluent-bit loads issue6281.conf that loads issue6281_input/output.conf.
+ *
+ * config dir -+-- issue6281.conf
+ * |
+ * +-- issue6281_input.conf
+ * |
+ * +-- issue6281_output.conf
+ */
+void not_current_dir_files()
+{
+ struct flb_cf *cf;
+
+ cf = flb_cf_create();
+ if (!TEST_CHECK(cf != NULL)) {
+ TEST_MSG("flb_cf_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ cf = flb_cf_fluentbit_create(cf, FLB_004, NULL, 0);
+ if (!TEST_CHECK(cf != NULL)) {
+ TEST_MSG("flb_cf_fluentbit_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (cf != NULL) {
+ flb_cf_destroy(cf);
+ }
+}
+
+/* data/config_format/nolimitline.conf */
+void test_nolimit_line()
+{
+ struct flb_cf *cf;
+ struct flb_cf_section *s;
+ struct cfl_list *p_head;
+ struct cfl_kvpair *kv;
+
+ cf = flb_cf_fluentbit_create(NULL, FLB_005, NULL, 0);
+ TEST_CHECK(cf != NULL);
+
+ /* Total number of sections */
+ TEST_CHECK(mk_list_size(&cf->sections) == 3);
+
+ /* SERVICE check */
+ TEST_CHECK(cf->service == NULL);
+
+ /* Meta commands */
+ TEST_CHECK(mk_list_size(&cf->metas) == 0);
+
+ /* Check number sections per list */
+ TEST_CHECK(mk_list_size(&cf->inputs) == 1);
+ TEST_CHECK(mk_list_size(&cf->filters) == 1);
+ TEST_CHECK(mk_list_size(&cf->outputs) == 1);
+
+ /* Check the previous line buffer limit */
+ s = flb_cf_section_get_by_name(cf, "filter");
+ TEST_CHECK(s != NULL);
+ TEST_CHECK(mk_list_size(&s->groups) == 0);
+
+ if (cfl_list_size(&s->properties->list) > 0) {
+ TEST_CHECK(cfl_list_size(&s->properties->list) == 4);
+ cfl_list_foreach(p_head, &s->properties->list) {
+ kv = cfl_list_entry(p_head, struct cfl_kvpair, _head);
+ if (strcmp(kv->key, "code") == 0) {
+ TEST_CHECK(cfl_sds_len(kv->val->data.as_string) > FLB_DEFAULT_CF_BUF_SIZE);
+ }
+ }
+ }
+
+ printf("\n");
+ flb_cf_dump(cf);
+
+ flb_cf_destroy(cf);
+}
+
+static inline int check_snake_case(char *input, char *output)
+{
+ int len;
+ int ret;
+ flb_sds_t out;
+ struct flb_cf *cf;
+
+
+ cf = flb_cf_create();
+ flb_cf_set_origin_format(cf, FLB_CF_CLASSIC);
+
+ len = strlen(input);
+ out = flb_cf_key_translate(cf, input, len);
+
+ ret = strcmp(out, output);
+
+ flb_sds_destroy(out);
+
+ flb_cf_destroy(cf);
+
+ if (ret == 0) {
+ return FLB_TRUE;
+ }
+
+ return FLB_FALSE;
+}
+
+static void test_snake_case_key()
+{
+ /* normal conversion */
+ TEST_CHECK(check_snake_case("a", "a") == FLB_TRUE);
+ TEST_CHECK(check_snake_case("aB", "ab") == FLB_TRUE);
+ TEST_CHECK(check_snake_case("aBc", "abc") == FLB_TRUE);
+ TEST_CHECK(check_snake_case("interval_Sec", "interval_sec") == FLB_TRUE);
+}
+
+TEST_LIST = {
+ { "basic" , test_basic},
+ { "missing_value_issue5880" , missing_value},
+ { "indent_level_error" , indent_level_error},
+ { "recursion" , recursion},
+ { "not_current_dir_files", not_current_dir_files},
+ { "no_limit_line", test_nolimit_line},
+ { "snake_case_key", test_snake_case_key},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/config_format_yaml.c b/fluent-bit/tests/internal/config_format_yaml.c
new file mode 100644
index 00000000..3135022b
--- /dev/null
+++ b/fluent-bit/tests/internal/config_format_yaml.c
@@ -0,0 +1,324 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_kv.h>
+#include <fluent-bit/flb_str.h>
+#include <fluent-bit/flb_config_format.h>
+
+#include <cfl/cfl.h>
+#include <cfl/cfl_list.h>
+
+#include "flb_tests_internal.h"
+
+#define FLB_TESTS_CONF_PATH FLB_TESTS_DATA_PATH "/data/config_format/yaml"
+#define FLB_000 FLB_TESTS_CONF_PATH "/fluent-bit.yaml"
+#define FLB_001 FLB_TESTS_CONF_PATH "/issue_7559.yaml"
+
+/*
+ * Configurations to test:
+ * * basic single input to single output
+ * * basic single input to single output with a filter
+ * * includes
+ * * slist
+ * * conf parsers
+ * * yaml parsers
+ * * customs
+ * * service
+ * * env
+ */
+
+/* data/config_format/fluent-bit.yaml */
+static void test_basic()
+{
+ struct mk_list *head;
+ struct flb_cf *cf;
+ struct flb_cf_section *s;
+ struct flb_cf_group *g;
+ struct cfl_variant *v;
+ int idx = 0;
+
+ cf = flb_cf_yaml_create(NULL, FLB_000, NULL, 0);
+ TEST_CHECK(cf != NULL);
+ if (!cf) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Total number of sections */
+ TEST_CHECK(mk_list_size(&cf->sections) == 9);
+
+ /* SERVICE check */
+ TEST_CHECK(cf->service != NULL);
+ if (cf->service) {
+ TEST_CHECK(cfl_list_size(&cf->service->properties->list) == 3);
+ }
+
+ /* Check number sections per list */
+ TEST_CHECK(mk_list_size(&cf->parsers) == 0);
+ TEST_CHECK(mk_list_size(&cf->multiline_parsers) == 0);
+ TEST_CHECK(mk_list_size(&cf->customs) == 1);
+ TEST_CHECK(mk_list_size(&cf->inputs) == 3);
+ TEST_CHECK(mk_list_size(&cf->filters) == 1);
+ TEST_CHECK(mk_list_size(&cf->outputs) == 2);
+ TEST_CHECK(mk_list_size(&cf->others) == 1);
+
+ /* check inputs */
+ idx = 0;
+ mk_list_foreach(head, &cf->inputs) {
+ s = mk_list_entry(head, struct flb_cf_section, _head_section);
+ switch (idx) {
+ case 0:
+ v = flb_cf_section_property_get(cf, s, "name");
+ TEST_CHECK(v->type == CFL_VARIANT_STRING);
+ TEST_CHECK(strcmp(v->data.as_string, "dummy") == 0);
+ break;
+ case 1:
+ v = flb_cf_section_property_get(cf, s, "name");
+ TEST_CHECK(v->type == CFL_VARIANT_STRING);
+ TEST_CHECK(strcmp(v->data.as_string, "tail") == 0);
+ v = flb_cf_section_property_get(cf, s, "path");
+ TEST_CHECK(v->type == CFL_VARIANT_STRING);
+ TEST_CHECK(strcmp(v->data.as_string, "./test.log") == 0);
+ break;
+ case 2:
+ v = flb_cf_section_property_get(cf, s, "name");
+ TEST_CHECK(v->type == CFL_VARIANT_STRING);
+ TEST_CHECK(strcmp(v->data.as_string, "tail") == 0);
+ v = flb_cf_section_property_get(cf, s, "path");
+ TEST_CHECK(v->type == CFL_VARIANT_STRING);
+ TEST_CHECK(strcmp(v->data.as_string, "./test.log") == 0);
+ break;
+ }
+ idx++;
+ }
+
+ /* check outputs */
+ idx = 0;
+ mk_list_foreach(head, &cf->outputs) {
+ s = mk_list_entry(head, struct flb_cf_section, _head_section);
+ switch (idx) {
+ case 0:
+ v = flb_cf_section_property_get(cf, s, "name");
+ TEST_CHECK(v->type == CFL_VARIANT_STRING);
+ TEST_CHECK(strcmp(v->data.as_string, "stdout") == 0);
+ break;
+ case 1:
+ v = flb_cf_section_property_get(cf, s, "name");
+ TEST_CHECK(v->type == CFL_VARIANT_STRING);
+ TEST_CHECK(strcmp(v->data.as_string, "stdout") == 0);
+ break;
+ }
+ idx++;
+ }
+
+ /* check filters */
+ idx = 0;
+ mk_list_foreach(head, &cf->filters) {
+ s = mk_list_entry(head, struct flb_cf_section, _head_section);
+ switch (idx) {
+ case 0:
+ v = flb_cf_section_property_get(cf, s, "name");
+ TEST_CHECK(v->type == CFL_VARIANT_STRING);
+ TEST_CHECK(strcmp(v->data.as_string, "record_modifier") == 0);
+ break;
+ }
+ idx++;
+ }
+
+ /* groups */
+ s = flb_cf_section_get_by_name(cf, "input");
+ TEST_CHECK(s != NULL);
+ TEST_CHECK(mk_list_size(&s->groups) == 1);
+
+ mk_list_foreach(head, &s->groups) {
+ g = mk_list_entry(head, struct flb_cf_group, _head);
+ TEST_CHECK(cfl_list_size(&g->properties->list) == 2);
+ }
+
+ printf("\n");
+ flb_cf_dump(cf);
+ flb_cf_destroy(cf);
+}
+
+/* https://github.com/fluent/fluent-bit/issues/7559 */
+static void test_customs_section()
+{
+ struct flb_cf *cf;
+ struct flb_cf_section *s;
+
+ cf = flb_cf_yaml_create(NULL, FLB_001, NULL, 0);
+ TEST_CHECK(cf != NULL);
+ if (!cf) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Total number of sections */
+ if(!TEST_CHECK(mk_list_size(&cf->sections) == 3)) {
+ TEST_MSG("Section number error. Got=%d expect=3", mk_list_size(&cf->sections));
+ }
+
+ s = flb_cf_section_get_by_name(cf, "customs");
+ TEST_CHECK(s != NULL);
+ if (!TEST_CHECK(s->type == FLB_CF_CUSTOM)) {
+ TEST_MSG("Section type error. Got=%d expect=%d", s->type, FLB_CF_CUSTOM);
+ }
+
+ flb_cf_dump(cf);
+ flb_cf_destroy(cf);
+}
+
+static void test_slist_even()
+{
+ struct flb_cf *cf;
+ struct flb_cf_section *s;
+ struct cfl_variant *v;
+ struct mk_list *head;
+
+ cf = flb_cf_yaml_create(NULL, FLB_TESTS_CONF_PATH "/pipelines/slist/even.yaml", NULL, 0);
+ TEST_CHECK(cf != NULL);
+ if (!cf) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Total number of inputs */
+ if(!TEST_CHECK(mk_list_size(&cf->inputs) == 1)) {
+ TEST_MSG("Section number error. Got=%d expect=1", mk_list_size(&cf->inputs));
+ }
+
+ mk_list_foreach(head, &cf->inputs) {
+ s = mk_list_entry(head, struct flb_cf_section, _head_section);
+ TEST_CHECK(s != NULL);
+
+ v = flb_cf_section_property_get(cf, s, "success_header");
+ TEST_CHECK(v->type == CFL_VARIANT_ARRAY);
+ if (!TEST_CHECK(v->data.as_array->entry_count == 2)) {
+ TEST_MSG("Section number error. Got=%lud expect=2", v->data.as_array->entry_count);
+ }
+ }
+
+ flb_cf_dump(cf);
+ flb_cf_destroy(cf);
+}
+
+static void test_slist_odd()
+{
+ struct flb_cf *cf;
+ struct flb_cf_section *s;
+ struct cfl_variant *v;
+ struct mk_list *head;
+
+ cf = flb_cf_yaml_create(NULL, FLB_TESTS_CONF_PATH "/pipelines/slist/odd.yaml", NULL, 0);
+ TEST_CHECK(cf != NULL);
+ if (!cf) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Total number of inputs */
+ if(!TEST_CHECK(mk_list_size(&cf->inputs) == 1)) {
+ TEST_MSG("Section number error. Got=%d expect=1", mk_list_size(&cf->inputs));
+ }
+
+ mk_list_foreach(head, &cf->inputs) {
+ s = mk_list_entry(head, struct flb_cf_section, _head_section);
+ TEST_CHECK(s != NULL);
+
+ v = flb_cf_section_property_get(cf, s, "success_header");
+ TEST_CHECK(v->type == CFL_VARIANT_ARRAY);
+ if (!TEST_CHECK(v->data.as_array->entry_count == 3)) {
+ TEST_MSG("Section number error. Got=%lud expect=3", v->data.as_array->entry_count);
+ }
+ }
+
+ flb_cf_dump(cf);
+ flb_cf_destroy(cf);
+}
+
+
+static void test_parser_conf()
+{
+ struct flb_cf *cf;
+ struct flb_config *config;
+ int ret;
+ int cnt;
+
+ cf = flb_cf_yaml_create(NULL, FLB_TESTS_CONF_PATH "/parsers/parsers-conf.yaml", NULL, 0);
+ TEST_CHECK(cf != NULL);
+ if (!cf) {
+ exit(EXIT_FAILURE);
+ }
+
+ config = flb_config_init();
+ TEST_CHECK(config != NULL);
+ config->conf_path = flb_strdup(FLB_TESTS_CONF_PATH "/parsers/");
+
+ // count the parsers registered automatically by fluent-bit
+ cnt = mk_list_size(&config->parsers);
+ // load the parsers from the configuration
+ ret = flb_config_load_config_format(config, cf);
+ if (ret != 0) {
+ exit(EXIT_FAILURE);;
+ }
+
+ /* Total number of inputs */
+ if(!TEST_CHECK(mk_list_size(&config->parsers) == cnt+1)) {
+ TEST_MSG("Section number error. Got=%d expect=%d",
+ mk_list_size(&config->parsers),
+ cnt+1);
+ }
+
+ flb_cf_dump(cf);
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+}
+
+static inline int check_camel_to_snake(char *input, char *output)
+{
+ int len;
+ int ret = -1;
+ flb_sds_t out;
+ struct flb_cf *cf;
+
+ cf = flb_cf_create();
+ flb_cf_set_origin_format(cf, FLB_CF_YAML);
+
+ len = strlen(input);
+ out = flb_cf_key_translate(cf, input, len);
+
+ ret = strcmp(out, output);
+ flb_sds_destroy(out);
+
+ flb_cf_destroy(cf);
+
+ if (ret == 0) {
+ return FLB_TRUE;
+ }
+
+ return FLB_FALSE;
+}
+
+
+static void test_camel_case_key()
+{
+ /* normal conversion */
+ TEST_CHECK(check_camel_to_snake("a", "a") == FLB_TRUE);
+ TEST_CHECK(check_camel_to_snake("aB", "a_b") == FLB_TRUE);
+ TEST_CHECK(check_camel_to_snake("aBc", "a_bc") == FLB_TRUE);
+ TEST_CHECK(check_camel_to_snake("aBcA", "a_bc_a") == FLB_TRUE);
+ TEST_CHECK(check_camel_to_snake("aBCD", "a_b_c_d") == FLB_TRUE);
+ TEST_CHECK(check_camel_to_snake("intervalSec", "interval_sec") == FLB_TRUE);
+
+ /* unsupported conversion, we force lowercase in Yaml */
+ TEST_CHECK(check_camel_to_snake("AA", "AA") == FLB_TRUE);
+ TEST_CHECK(check_camel_to_snake("Interval_Sec", "Interval_Sec") == FLB_TRUE);
+
+}
+
+TEST_LIST = {
+ { "basic" , test_basic},
+ { "customs section", test_customs_section},
+ { "slist odd", test_slist_odd},
+ { "slist even", test_slist_even},
+ { "parsers file conf", test_parser_conf},
+ { "camel_case_key", test_camel_case_key},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/config_map.c b/fluent-bit/tests/internal/config_map.c
new file mode 100644
index 00000000..b709d016
--- /dev/null
+++ b/fluent-bit/tests/internal/config_map.c
@@ -0,0 +1,386 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_kv.h>
+#include <fluent-bit/flb_slist.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_config.h>
+#include <fluent-bit/flb_config_map.h>
+
+#include "flb_tests_internal.h"
+
+struct context {
+ /* Single values */
+ int num_int;
+ size_t size;
+ time_t time;
+ char boolean;
+ double num_double;
+ flb_sds_t string;
+ struct mk_list *list1;
+ struct mk_list *list2;
+
+ /* Multiple entries */
+ struct mk_list *mult_num_int;
+ struct mk_list *mult_boolean;
+ struct mk_list *mult_num_double;
+ struct mk_list *mult_string;
+ struct mk_list *mult_list1;
+ struct mk_list *mult_list2;
+};
+
+struct flb_config_map config_map[] = {
+ {
+ FLB_CONFIG_MAP_BOOL,
+ "boolean",
+ "true",
+ 0, FLB_TRUE, offsetof(struct context, boolean),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_INT,
+ "num_int",
+ "123",
+ 0, FLB_TRUE, offsetof(struct context, num_int),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_DOUBLE,
+ "num_double", "0.12345",
+ 0, FLB_TRUE, offsetof(struct context, num_double),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_STR,
+ "string",
+ "test",
+ 0, FLB_TRUE, offsetof(struct context, string),
+ NULL
+ },
+
+ /* SIZE */
+ {
+ FLB_CONFIG_MAP_SIZE,
+ "test_size",
+ "2M",
+ 0, FLB_TRUE, offsetof(struct context, size),
+ NULL
+ },
+
+ /* TIME */
+ {
+ FLB_CONFIG_MAP_TIME,
+ "test_time",
+ "2H",
+ 0, FLB_TRUE, offsetof(struct context, time),
+ NULL
+ },
+
+ /* CSLIST */
+ {
+ FLB_CONFIG_MAP_CLIST,
+ "test_clist",
+ "a, b, c ,d,e , f, g,h,i,jk , lm , n o,pqr,, , ,stuv,xyz",
+ 0, FLB_TRUE, offsetof(struct context, list1),
+ NULL
+ },
+
+ /* SLIST */
+ {
+ FLB_CONFIG_MAP_SLIST_4,
+ "test_slist",
+ "a b c de f ghi jk l m n o pqr stuv xyz",
+ 0, FLB_TRUE, offsetof(struct context, list2),
+ NULL
+ },
+
+ /* EOF */
+ {0}
+};
+
+struct flb_config_map config_map_mult[] = {
+ {
+ FLB_CONFIG_MAP_BOOL,
+ "no_mult",
+ "true",
+ 0, FLB_TRUE, 1,
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_BOOL,
+ "mult_boolean",
+ NULL,
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_boolean),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_INT,
+ "mult_num_int",
+ "123",
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_num_int),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_DOUBLE,
+ "mult_num_double", "0.12345",
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_num_double),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_STR,
+ "mult_string",
+ "test",
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_string),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_CLIST,
+ "mult_clist",
+ "a, b, c ,d,e , f, g,h,i,jk , lm , n o,pqr,, , ,stuv,xyz",
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_list1),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_SLIST_4,
+ "mult_slist",
+ "a b c de f ghi jk l m n o pqr stuv xyz",
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_list2),
+ NULL
+ },
+
+ /* EOF */
+ {0}
+};
+
+void test_helper()
+{
+ int ret;
+ struct context ctx;
+ struct mk_list *map;
+ struct mk_list prop;
+ struct flb_config *config;
+
+ config = flb_config_init();
+ if (!config) {
+ exit(1);
+ }
+
+ memset(&ctx, '\0', sizeof(struct context));
+
+ /* Create invalid property */
+ flb_kv_init(&prop);
+ flb_kv_item_create(&prop, "bad", "property");
+
+ map = flb_config_map_create(config, config_map);
+ TEST_CHECK(map != NULL);
+
+ ret = flb_config_map_properties_check("test", &prop, map);
+ TEST_CHECK(ret == -1);
+
+ flb_config_map_destroy(map);
+ flb_kv_release(&prop);
+
+ flb_config_exit(config);
+}
+
+void test_create()
+{
+ int ret;
+ struct context ctx;
+ struct mk_list *map;
+ struct mk_list properties;
+ struct flb_slist_entry *e;
+ struct flb_config *config;
+
+ config = flb_config_init();
+ if (!config) {
+ exit(1);
+ }
+
+ memset(&ctx, '\0', sizeof(struct context));
+ mk_list_init(&properties);
+
+ map = flb_config_map_create(config, config_map);
+ TEST_CHECK(map != NULL);
+
+ /* Populate default values only */
+ ret = flb_config_map_set(&properties, map, &ctx);
+ TEST_CHECK(ret == 0);
+
+ TEST_CHECK(ctx.boolean == 1);
+ TEST_CHECK(ctx.num_int == 123);
+ TEST_CHECK(ctx.num_double == 0.12345);
+ TEST_CHECK(ctx.size == 2000000);
+ TEST_CHECK(ctx.time == 7200);
+ TEST_CHECK(strcmp(ctx.string, "test") == 0);
+ TEST_CHECK(flb_sds_len(ctx.string) == 4);
+ TEST_CHECK(mk_list_size(ctx.list1) == 15);
+ TEST_CHECK(mk_list_size(ctx.list2) == 5);
+
+ e = mk_list_entry_last(ctx.list2, struct flb_slist_entry, _head);
+ TEST_CHECK(strcmp(e->str, "f ghi jk l m n o pqr stuv xyz") == 0);
+ flb_config_map_destroy(map);
+
+ flb_config_exit(config);
+}
+
+void test_override_defaults()
+{
+ int ret;
+ struct context ctx;
+ struct mk_list *map;
+ struct mk_list properties;
+ struct flb_slist_entry *e;
+ struct flb_config *config;
+
+ config = flb_config_init();
+ if (!config) {
+ exit(1);
+ }
+
+ memset(&ctx, '\0', sizeof(struct context));
+ mk_list_init(&properties);
+
+ map = flb_config_map_create(config, config_map);
+ TEST_CHECK(map != NULL);
+
+ /* Create a properties list that will override default values */
+ flb_kv_item_create(&properties, "boolean", "false");
+ flb_kv_item_create(&properties, "num_int", "321");
+ flb_kv_item_create(&properties, "num_double", "0.54321");
+ flb_kv_item_create(&properties, "string", "no test");
+ flb_kv_item_create(&properties, "test_time", "1H");
+ flb_kv_item_create(&properties, "test_size", "1M");
+ flb_kv_item_create(&properties, "test_clist", "abc, def, ghi ,,,,jkl ");
+ flb_kv_item_create(&properties, "test_slist", "abc def ghi jkl m n o");
+
+ /* Populate default values only */
+ ret = flb_config_map_set(&properties, map, &ctx);
+ TEST_CHECK(ret == 0);
+
+ TEST_CHECK(ctx.boolean == 0);
+ TEST_CHECK(ctx.num_int == 321);
+ TEST_CHECK(ctx.num_double == 0.54321);
+ TEST_CHECK(ctx.size == 1000000);
+ TEST_CHECK(ctx.time == 3600);
+ TEST_CHECK(strcmp(ctx.string, "no test") == 0);
+ TEST_CHECK(flb_sds_len(ctx.string) == 7);
+ TEST_CHECK(mk_list_size(ctx.list1) == 4);
+ TEST_CHECK(mk_list_size(ctx.list2) == 5);
+
+ e = mk_list_entry_last(ctx.list2, struct flb_slist_entry, _head);
+ TEST_CHECK(strcmp(e->str, "m n o") == 0);
+
+ flb_kv_release(&properties);
+ flb_config_map_destroy(map);
+
+ flb_config_exit(config);
+}
+
+/* Check that single property raise an error if are set multiple times (dups) */
+void test_no_multiple()
+{
+ int ret;
+ struct context ctx;
+ struct mk_list *map;
+ struct mk_list prop;
+ struct flb_config *config;
+
+ config = flb_config_init();
+ if (!config) {
+ exit(1);
+ }
+
+ memset(&ctx, '\0', sizeof(struct context));
+
+ /* Assign the property multiple times */
+ flb_kv_init(&prop);
+ flb_kv_item_create(&prop, "no_mult", "true");
+ flb_kv_item_create(&prop, "no_mult", "false");
+
+ map = flb_config_map_create(config, config_map_mult);
+ TEST_CHECK(map != NULL);
+
+ ret = flb_config_map_properties_check("test", &prop, map);
+ TEST_CHECK(ret == -1);
+
+ flb_config_map_destroy(map);
+ flb_kv_release(&prop);
+
+ flb_config_exit(config);
+}
+
+void test_multiple()
+{
+ int ret;
+ int i;
+ int total;
+ struct context ctx;
+ struct mk_list *map;
+ struct mk_list prop;
+ struct mk_list *head;
+ struct flb_config_map_val *mv;
+ struct flb_config *config;
+
+ config = flb_config_init();
+ if (!config) {
+ exit(1);
+ }
+
+ memset(&ctx, '\0', sizeof(struct context));
+
+ /* Create invalid property */
+ flb_kv_init(&prop);
+ flb_kv_item_create(&prop, "mult_boolean", "true");
+ flb_kv_item_create(&prop, "mult_boolean", "false");
+
+ flb_kv_item_create(&prop, "mult_clist", "a, b, c");
+ flb_kv_item_create(&prop, "mult_clist", "d, e, f");
+ flb_kv_item_create(&prop, "mult_clist", "g, h, i");
+
+ flb_kv_item_create(&prop, "mult_slist", "d e f g");
+
+ map = flb_config_map_create(config, config_map_mult);
+ TEST_CHECK(map != NULL);
+
+ ret = flb_config_map_properties_check("test", &prop, map);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_config_map_set(&prop, map, &ctx);
+ TEST_CHECK(ret == 0);
+
+ i = 0;
+ flb_config_map_foreach(head, mv, ctx.mult_boolean) {
+ if (i == 0) {
+ TEST_CHECK(mv->val.boolean == FLB_TRUE);
+ }
+ else {
+ TEST_CHECK(mv->val.boolean == FLB_FALSE);
+ }
+ i++;
+ }
+
+ total = 0;
+ flb_config_map_foreach(head, mv, ctx.mult_list1) {
+ total++;
+ }
+ TEST_CHECK(total == 4);
+
+ total = 0;
+ flb_config_map_foreach(head, mv, ctx.mult_list2) {
+ total++;
+ }
+
+ flb_config_map_destroy(map);
+ flb_kv_release(&prop);
+ flb_config_exit(config);
+}
+
+TEST_LIST = {
+ { "helper" , test_helper},
+ { "create" , test_create},
+ { "override_defaults", test_override_defaults},
+ { "no_multiple" , test_no_multiple},
+ { "multiple" , test_multiple},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/crypto.c b/fluent-bit/tests/internal/crypto.c
new file mode 100644
index 00000000..e8c9bb43
--- /dev/null
+++ b/fluent-bit/tests/internal/crypto.c
@@ -0,0 +1,202 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_hash.h>
+#include <fluent-bit/flb_crypto.h>
+
+#include "flb_tests_internal.h"
+
+#define PRIVATE_KEY (unsigned char *) \
+ "-----BEGIN RSA PRIVATE KEY-----\n" \
+ "MIICXgIBAAKBgQDUkO22PyhYHJlXYQomNXEeX7ChfdqyY1ukUMsPQPbmjgsDZ/tG\n" \
+ "rvNtJVtwuOjGi+DGTG+fSFdgoGDbypOXOC5sX2Luwen+Ixvqay1V3pm16cOVOHf8\n" \
+ "XYAMLqTh1Aq3CykQeuTLKQPGG5rL6utFsgzQEgVpSZdLi2W3RfcJGS22EQIDAQAB\n" \
+ "AoGAPMuWsWEu8MR9NviSJotyZvWHVyjfu9WfCEfzS9GQzDAkBj1fKMAw7y6YEI1S\n" \
+ "RjcLequx4SSXmRNFoJc3zzBKVj77vf60vahoaq11My9pMLDSENK/JKW+VpueKYrT\n" \
+ "5Z9C6y9dB7NKXd8YANDApYNc4+a4l01WFNxjBXJveDo6+IECQQD0iT1ne96LIPFO\n" \
+ "j82SGlDkc1w7ZOZKl5kvRwM1VfXmDzdpFhEDndDnFa7Dth3t0QOQVVkbOUnNvQ+Z\n" \
+ "XEVr6l2FAkEA3ogBoIns72p8sRqE2Uavum53M7SxRJTN/Fn5mBN6aNX2if4M2k6r\n" \
+ "Uwhyld6k0PwAB1zVNUlTi0pyR7BcnrAGHQJBAO81NTD+1hLJVeQg/do3Dfx78LRV\n" \
+ "HoXHSF0cHUJWZWX4ap7MrDYachkrd7sRcrOJq+/L3Y+o+c5dbF38ChjnuTUCQQCg\n" \
+ "3ZDPjOVK7Z/WJ2WB7Cd8jf590lGTUj7V/fUAipQi1Qm0F4MTDWusSp8K2DgtGv6q\n" \
+ "U+GM88UBHIAgcs2BqZ3BAkEAjKEOgXNjeYabdkVrQMvJMAJF52vBpSksrV1jqV1F\n" \
+ "AH/sGR3C9faYNzFltPnQcE0USluDQRS/7UHNtn1VEBb34A==\n" \
+ "-----END RSA PRIVATE KEY-----\n"
+
+#define PRIVATE_KEY_LENGTH (strlen((char *) PRIVATE_KEY))
+
+#define PUBLIC_KEY (unsigned char *) \
+ "-----BEGIN PUBLIC KEY-----\n" \
+ "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDUkO22PyhYHJlXYQomNXEeX7Ch\n" \
+ "fdqyY1ukUMsPQPbmjgsDZ/tGrvNtJVtwuOjGi+DGTG+fSFdgoGDbypOXOC5sX2Lu\n" \
+ "wen+Ixvqay1V3pm16cOVOHf8XYAMLqTh1Aq3CykQeuTLKQPGG5rL6utFsgzQEgVp\n" \
+ "SZdLi2W3RfcJGS22EQIDAQAB\n" \
+ "-----END PUBLIC KEY-----\n"
+
+#define PUBLIC_KEY_LENGTH (strlen((char *) PUBLIC_KEY))
+
+#define INPUT_DATA (unsigned char *) "This was encrypted by fluent-bits RSA wrapper!"
+
+#define INPUT_DATA_LENGTH (strlen((char *) INPUT_DATA))
+
+#define SIGNATURE_INPUT INPUT_DATA
+#define SIGNATURE_INPUT_LENGTH INPUT_DATA_LENGTH
+
+#define SIGNATURE_DIGEST_TYPE FLB_HASH_SHA256
+#define SIGNATURE_PADDING_TYPE FLB_CRYPTO_PADDING_PKCS1
+#define SIGNATURE_OUTPUT ((unsigned char []) { \
+ 0x73, 0x03, 0x78, 0x0b, 0x61, 0x2a, 0x3b, 0x94, 0x5e, 0x26, 0x77, 0x65, \
+ 0x96, 0x74, 0x96, 0xbb, 0x6b, 0x1b, 0xdf, 0x8b, 0xfa, 0xa2, 0x57, 0x77, \
+ 0xc3, 0x39, 0xaf, 0x21, 0x56, 0x43, 0x87, 0xfe, 0xfb, 0x90, 0xa1, 0x19, \
+ 0xf4, 0xc3, 0xe1, 0x74, 0xdb, 0x6b, 0x6a, 0x89, 0xeb, 0x01, 0x4e, 0xbc, \
+ 0xf7, 0xe6, 0x0b, 0x7c, 0x1b, 0xa1, 0x6a, 0x7b, 0x55, 0x24, 0x1f, 0xbc, \
+ 0x78, 0x20, 0x11, 0x76, 0xf5, 0x02, 0x16, 0x29, 0xb4, 0x17, 0xff, 0x29, \
+ 0x43, 0x89, 0x4a, 0x7d, 0x23, 0x0d, 0x63, 0x59, 0x76, 0x75, 0xd2, 0x9d, \
+ 0x2c, 0x9f, 0x56, 0x6d, 0x27, 0x53, 0xeb, 0xa3, 0xa7, 0x90, 0x56, 0x4d, \
+ 0x88, 0xe5, 0x4e, 0x55, 0xca, 0x36, 0x58, 0x6f, 0x16, 0x1a, 0xb9, 0x1c, \
+ 0x4a, 0x8b, 0x0c, 0x30, 0x41, 0x19, 0x93, 0x23, 0x47, 0xcc, 0x41, 0x6c, \
+ 0x9a, 0x15, 0x9e, 0xec, 0x22, 0xac, 0x4a, 0xb9 \
+ })
+
+#define SIGNATURE_OUTPUT_LENGTH (sizeof(ENCRYPTED_DATA))
+
+#define ENCRYPTION_PADDING_TYPE FLB_CRYPTO_PADDING_PKCS1
+#define ENCRYPTION_INPUT INPUT_DATA
+#define ENCRYPTED_DATA ((unsigned char []) { \
+ 0x0b, 0xd3, 0x91, 0x15, 0xb4, 0xed, 0xcd, 0x6c, 0xf1, 0x5c, 0x88, 0x15, \
+ 0xde, 0x9d, 0x02, 0x7a, 0x0c, 0x13, 0x93, 0xdc, 0x98, 0x7c, 0x7c, 0xa0, \
+ 0x70, 0x80, 0xff, 0x88, 0x68, 0xb6, 0x17, 0x10, 0xfd, 0x02, 0xaa, 0x96, \
+ 0x54, 0x75, 0x83, 0x51, 0x9e, 0xe3, 0x72, 0x58, 0xd3, 0x01, 0x79, 0x61, \
+ 0xfa, 0x17, 0x18, 0x48, 0xa8, 0xcb, 0xe9, 0x54, 0x5d, 0x87, 0x83, 0x86, \
+ 0x5f, 0x1b, 0xf2, 0x01, 0x7f, 0x98, 0x93, 0xa1, 0x6e, 0x2d, 0x23, 0x2c, \
+ 0x8b, 0xc9, 0x36, 0xad, 0xfc, 0xdb, 0x9d, 0xa0, 0xd2, 0x17, 0xa1, 0x9d, \
+ 0x2e, 0x25, 0xee, 0x54, 0xf5, 0xe3, 0xa6, 0xdb, 0x98, 0x7a, 0x09, 0xef, \
+ 0x43, 0xcc, 0x7e, 0x44, 0x53, 0x7e, 0x4a, 0x8b, 0x14, 0x9b, 0x42, 0x67, \
+ 0xa2, 0x9a, 0x51, 0xdb, 0xf4, 0xfc, 0x93, 0xe7, 0xe1, 0xda, 0x28, 0xad, \
+ 0x97, 0x7e, 0xd4, 0xc0, 0xe2, 0x4e, 0xfa, 0xeb \
+ })
+
+#define ENCRYPTED_DATA_LENGTH (sizeof(ENCRYPTED_DATA))
+
+
+/* This test encrypts and decrypts a buffer with a pre-generated 1024 bit RSA
+ * key and then ensures the results match.
+ */
+
+static void test_rsa_simple_encrypt()
+{
+ unsigned char decrypted_data_buffer[1024 * 10];
+ size_t decrypted_data_buffer_size;
+ unsigned char encrypted_data_buffer[1024 * 10];
+ size_t encrypted_data_buffer_size;
+ int result;
+
+ encrypted_data_buffer_size = sizeof(encrypted_data_buffer);
+ decrypted_data_buffer_size = sizeof(decrypted_data_buffer);
+
+ result = flb_crypto_encrypt_simple(ENCRYPTION_PADDING_TYPE,
+ PUBLIC_KEY, PUBLIC_KEY_LENGTH,
+ INPUT_DATA, INPUT_DATA_LENGTH,
+ encrypted_data_buffer,
+ &encrypted_data_buffer_size);
+
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+
+ if (result == FLB_CRYPTO_SUCCESS) {
+ result = flb_crypto_decrypt_simple(ENCRYPTION_PADDING_TYPE,
+ PRIVATE_KEY,
+ PRIVATE_KEY_LENGTH,
+ encrypted_data_buffer,
+ encrypted_data_buffer_size,
+ decrypted_data_buffer,
+ &decrypted_data_buffer_size);
+
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+ TEST_CHECK(decrypted_data_buffer_size == INPUT_DATA_LENGTH);
+
+ if (result == FLB_CRYPTO_SUCCESS) {
+ TEST_CHECK(memcmp(decrypted_data_buffer, INPUT_DATA, INPUT_DATA_LENGTH) == 0);
+ }
+ }
+}
+
+/* This test decrypts a buffer that was encrpyted using openssls rsa utility
+ * with a pre-generated 1024 bit RSA key and then ensures the results match.
+ */
+
+static void test_rsa_simple_decrypt()
+{
+ unsigned char decrypted_data_buffer[1024 * 10];
+ size_t decrypted_data_buffer_size;
+ int result;
+
+ decrypted_data_buffer_size = sizeof(decrypted_data_buffer);
+
+ result = flb_crypto_decrypt_simple(ENCRYPTION_PADDING_TYPE,
+ PRIVATE_KEY,
+ PRIVATE_KEY_LENGTH,
+ ENCRYPTED_DATA,
+ ENCRYPTED_DATA_LENGTH,
+ decrypted_data_buffer,
+ &decrypted_data_buffer_size);
+
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+ TEST_CHECK(decrypted_data_buffer_size == INPUT_DATA_LENGTH);
+
+ if (result == FLB_CRYPTO_SUCCESS) {
+ TEST_CHECK(memcmp(decrypted_data_buffer, INPUT_DATA, INPUT_DATA_LENGTH) == 0);
+ }
+}
+
+
+/* This test signs a message using a pre-generated 1024 bit RSA key and
+ * compares the signature with one that has been pre-generated using
+ * openssls pkeyutil.
+ */
+
+static void test_rsa_simple_sign()
+{
+ unsigned char signature_buffer[1024];
+ size_t signature_buffer_size;
+ unsigned char digest_buffer[32];
+ int result;
+
+ result = flb_hash_simple(SIGNATURE_DIGEST_TYPE,
+ SIGNATURE_INPUT,
+ SIGNATURE_INPUT_LENGTH,
+ digest_buffer,
+ sizeof(digest_buffer));
+
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+
+ if (result == FLB_CRYPTO_SUCCESS) {
+ signature_buffer_size = sizeof(signature_buffer);
+
+ result = flb_crypto_sign_simple(FLB_CRYPTO_PRIVATE_KEY,
+ SIGNATURE_PADDING_TYPE,
+ SIGNATURE_DIGEST_TYPE,
+ PRIVATE_KEY,
+ PRIVATE_KEY_LENGTH,
+ digest_buffer,
+ sizeof(digest_buffer),
+ signature_buffer,
+ &signature_buffer_size);
+
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+ TEST_CHECK(signature_buffer_size == SIGNATURE_OUTPUT_LENGTH);
+
+ if (result == FLB_CRYPTO_SUCCESS) {
+ TEST_CHECK(memcmp(signature_buffer,
+ SIGNATURE_OUTPUT,
+ SIGNATURE_OUTPUT_LENGTH) == 0);
+ }
+ }
+}
+
+TEST_LIST = {
+ { "test_rsa_simple_encrypt", test_rsa_simple_encrypt },
+ { "test_rsa_simple_decrypt", test_rsa_simple_decrypt },
+ { "test_rsa_simple_sign", test_rsa_simple_sign },
+
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/csv.c b/fluent-bit/tests/internal/csv.c
new file mode 100644
index 00000000..ad4503eb
--- /dev/null
+++ b/fluent-bit/tests/internal/csv.c
@@ -0,0 +1,167 @@
+#include <fluent-bit/flb_csv.h>
+#include <fluent-bit/flb_sds.h>
+#include <string.h>
+
+#include "flb_tests_internal.h"
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+#define row_count 3
+#define col_count 4
+
+char data[] =
+ "r0c1,,,\r"
+ ",r1c2,\"\",\"r1three\nlines\nfield\"\r\n"
+ "r2c1,r2c2,\"\"\"r2c\n3\n\",\"r2three\nlines\nfield\"\"\"\n";
+
+char *parse_expected[row_count][col_count] = {
+ { "r0c1", "", "", "" },
+ { "", "r1c2", "", "r1three\nlines\nfield" },
+ { "r2c1", "r2c2", "\"r2c\n3\n", "r2three\nlines\nfield\"" },
+};
+
+char *split_expected[row_count] = {
+ "r0c1,,,\r",
+ ",r1c2,\"\",\"r1three\nlines\nfield\"\r\n",
+ "r2c1,r2c2,\"\"\"r2c\n3\n\",\"r2three\nlines\nfield\"\"\"\n",
+};
+
+struct parse_state {
+ char **parsed;
+ size_t current_row;
+ size_t current_col;
+};
+
+static void field_parsed(void *data, const char *field, size_t field_len)
+{
+ struct parse_state *state = data;
+ size_t col = state->current_col;
+ size_t row = state->current_row;
+ char *fdup = strndup(field, field_len);
+ state->parsed[row * col_count + col] = fdup;
+ state->current_col++;
+}
+
+static char **parse_data_step(int step)
+{
+ int i;
+ size_t len = sizeof(data);
+ struct parse_state state;
+ char *bufptr;
+ struct flb_csv_state csv_state;
+ size_t field_count;
+ state.parsed = malloc(sizeof *state.parsed * row_count * col_count);
+ state.current_row = 0;
+ state.current_col = 0;
+ flb_csv_init(&csv_state, field_parsed, &state);
+ bufptr = data;
+ for (i = 0; i < sizeof(data);) {
+ int ret;
+ size_t bl = MIN(len - i, step);
+ ret = flb_csv_parse_record(&csv_state, &bufptr, &bl, &field_count);
+ i = bufptr - data;
+ if (ret == FLB_CSV_SUCCESS) {
+ TEST_CHECK(field_count == 4);
+ state.current_row++;
+ state.current_col = 0;
+ }
+ else if (ret == FLB_CSV_EOF) {
+ continue;
+ }
+ else {
+ abort();
+ };
+ }
+ flb_csv_destroy(&csv_state);
+ return state.parsed;
+}
+
+static void check_parse_result(char **result)
+{
+ int row, col;
+
+ for (row = 0; row < row_count; row++) {
+ for (col = 0; col < col_count; col++) {
+ char *res = result[row * col_count + col];
+ TEST_CHECK(strcmp(res, parse_expected[row][col]) == 0);
+ TEST_MSG("Mismatch on row %d, col %d", row, col);
+ TEST_MSG("Expected: %s", parse_expected[row][col]);
+ TEST_MSG("Result: %s", result[row * col_count + col]);
+ free(res);
+ }
+ }
+
+ free(result);
+}
+
+static void check_split_result(char **result)
+{
+ int row;
+
+ for (row = 0; row < row_count; row++) {
+ char *res = result[row];
+ TEST_CHECK(strcmp(res, split_expected[row]) == 0);
+ TEST_MSG("Mismatch on row %d", row);
+ TEST_MSG("Expected: %s", split_expected[row]);
+ TEST_MSG("Result: %s", result[row]);
+ free(res);
+ }
+
+ free(result);
+}
+
+static char **split_rows_step(int step)
+{
+ int i;
+ size_t len = sizeof(data);
+ char *bufptr;
+ char *row_start;
+ size_t current_row = 0;
+ char **result = malloc(sizeof *result * row_count);
+ struct flb_csv_state csv_state;
+ flb_csv_init(&csv_state, NULL, NULL);
+ bufptr = data;
+ row_start = data;
+ size_t field_count;
+ for (i = 0; i < len;) {
+ int ret;
+ size_t bl = MIN(len - i, step);
+ ret = flb_csv_parse_record(&csv_state, &bufptr, &bl, &field_count);
+ i = bufptr - data;
+ if (ret == FLB_CSV_SUCCESS) {
+ TEST_CHECK(field_count == 4);
+ result[current_row] = strndup(row_start, bufptr - row_start);
+ row_start = bufptr;
+ current_row++;
+ }
+ else if (ret == FLB_CSV_EOF) {
+ continue;
+ } else {
+ abort();
+ };
+ }
+ flb_csv_destroy(&csv_state);
+ return result;
+}
+
+static void test_basic()
+{
+ int step;
+ for (step = 1; step <= sizeof(data); step++) {
+ check_parse_result(parse_data_step(step));
+ }
+}
+
+static void test_split_lines()
+{
+ int step;
+ for (step = 1; step <= sizeof(data); step++) {
+ check_split_result(split_rows_step(step));
+ }
+}
+
+TEST_LIST = {
+ { "basic" , test_basic},
+ { "split_lines" , test_split_lines},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/data/avro/json_single_map_001.json b/fluent-bit/tests/internal/data/avro/json_single_map_001.json
new file mode 100644
index 00000000..6833b392
--- /dev/null
+++ b/fluent-bit/tests/internal/data/avro/json_single_map_001.json
@@ -0,0 +1,5 @@
+{"key001": 123456789,
+ "key002": 0.999887766,
+ "key003": "abcdefghijk",
+ "key004": [{"a": 1, "b": 2}, {"c": 3, "d": 4}]
+}
diff --git a/fluent-bit/tests/internal/data/avro/live-sample.json b/fluent-bit/tests/internal/data/avro/live-sample.json
new file mode 100644
index 00000000..ba6eb8ac
--- /dev/null
+++ b/fluent-bit/tests/internal/data/avro/live-sample.json
@@ -0,0 +1,25 @@
+{
+ "log": "2020-08-21T15:49:48.154291375ZstderrFhdfsExists:invokeMethod((Lorg/apache/hadoop/fs/Path;)Z)error:2020-08-21T15:49:48.154296328ZstderrForg.apache.hadoop.ipc.RemoteException(org.apache.hadoop.security..SecretManager$Invalid):(HDFS_DELEGATION_273964959forweeb)can'tbefoundincache2020-08-21T15:49:48.154300089ZstderrFatorg.apache.hadoop.ipc.Client.call(Client.java:1475)2020-08-21T15:49:48.154303281ZstderrFatorg.apache.hadoop.ipc.Client.call(Client.java:1412)2020-08-21T15:49:48.154307503ZstderrFatorg.apache.hadoop.ipc.ProtobufRpcEngine$Invoker.invoke(ProtobufRpcEngine.java:229)2020-08-21T15:49:48.154311268ZstderrFatcom.sun.proxy.$Proxy9.getFileInfo(UnknownSource)2020-08-21T15:49:48.154314514ZstderrFatorg.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB.getFileInfo(ClientNamenodeProtocolTranslatorPB.java:771)2020-08-21T15:49:48.154317916ZstderrFatsun.reflect.GeneratedMethodAccessor2.invoke(UnknownSource)2020-08-21T15:49:48.154328852ZstderrFatsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)2020-08-21T15:49:48.154332363ZstderrFatjava.lang.reflect.Method.invoke(Method.java:498)2020-08-21T15:49:48.154335464ZstderrFatorg.apache.hadoop.io.retry.RetryInvocationHandler.invokeMethod(RetryInvocationHandler.java:191)2020-08-21T15:49:48.154338276ZstderrFatorg.apache.hadoop.io.retry.RetryInvocationHandler.invoke(RetryInvocationHandler.java:102)2020-08-21T15:49:48.15434115ZstderrFatcom.sun.proxy.$Proxy10.getFileInfo(UnknownSource)2020-08-21T15:49:48.154344323ZstderrFatorg.apache.hadoop.hdfs.DFSClient.getFileInfo(DFSClient.java:2108)2020-08-21T15:49:48.154347224ZstderrFatorg.apache.hadoop.hdfs.DistributedFileSystem$22.doCall(DistributedFileSystem.java:1305)2020-08-21T15:49:48.154350059ZstderrFatorg.apache.hadoop.hdfs.DistributedFileSystem$22.doCall(DistributedFileSystem.java:1301)2020-08-21T15:49:48.154352819ZstderrFatorg.apache.hadoop.fs.FileSystemLinkResolver.resolve(FileSystemLinkResolver.java:81)2020-08-21T15:49:48.154355939ZstderrFatorg.apache.hadoop.hdfs.DistributedFileSystem.getFileStatus(DistributedFileSystem.java:1301)2020-08-21T15:49:48.154358773ZstderrFatorg.apache.hadoop.fs.FileSystem.exists(FileSystem.java:1424)",
+ "capture": "2020-08-21T15:49:53.16823268ZstderrF20/08/2115:49:53WARNipc.Client:Exceptionencounteredwhileconnectingtotheserver:org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.security..SecretManager$Invalid):(HDFS_DELEGATION_273964959forweeb)can'tbefoundincache",
+ "kubernetes": {
+ "pod_name": "rrrr-bert-completion-tb1-6786c9c8-wj25m",
+ "namespace_name": "k8s-fgg",
+ "pod_id": "50bfc67d-cd3c-410d-9369-8bda8f33b1c7",
+ "labels": {
+ "app": "customized-tb1",
+ "pod-template-hash": "6786c9c8"
+ },
+ "annotations": {
+ "doAs": "weeb",
+ "iddecorator.dkdk.username": "rrrr",
+ "kubernetes.io/limit-ranger": "LimitRangerpluginset:cpu,memoryrequestforcontainertensorboard;cpu,memorylimitforcontainertensorboard;cpu,memoryrequestforinitcontainerfetcher;cpu,memorylimitforinitcontainerfetcher",
+ "kubernetes.io/psp": "katib-nfs-provisioner",
+ "podpreset.admission.kubernetes.io/podpreset-kube-master": "60792473"
+ },
+ "host": "wedddd.dkdk.qqqq.com",
+ "container_name": "tb1",
+ "docker_id": "50bfc67d-cd3c-410d-9369-8bda8f33b1c7",
+ "container_hash": "qqqq.corp.qqqq.com/ai/centos/tf1.15.0-py3.7-horovod@sha256:68b6885f6d1d3fd87ce425a2b2aa687440b9578740d60996912a816ae67be85e",
+ "container_image": "qqqq.corp.qqqq.com/ai/centos/tf1.15.0-py3.7-horovod:1.2"
+ }
+ }
diff --git a/fluent-bit/tests/internal/data/avro/multiline.json b/fluent-bit/tests/internal/data/avro/multiline.json
new file mode 100644
index 00000000..8d0173e8
--- /dev/null
+++ b/fluent-bit/tests/internal/data/avro/multiline.json
@@ -0,0 +1,27 @@
+[
+ {
+ "date": 1597871185.922652,
+ "log": "2020-08-19T21:06:24.337499838Z stderr F hdfsExists: invokeMethod((Lorg/apache/hadoop/fs/Path;)Z) error:\n2020-08-19T21:06:24.33754077Z stderr F org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.security..SecretManager$Invalid): (HDFS_DELEGATION_ 273964959 for weeb) can't be found in cache\n2020-08-19T21:06:24.337544598Z stderr F \tat org.apache.hadoop.ipc.Client.call(Client.java:1475)\n2020-08-19T21:06:24.337547418Z stderr F \tat org.apache.hadoop.ipc.Client.call(Client.java:1412)\n2020-08-19T21:06:24.337550469Z stderr F \tat org.apache.hadoop.ipc.ProtobufRpcEngine$Invoker.invoke(ProtobufRpcEngine.java:229)\n2020-08-19T21:06:24.337553603Z stderr F \tat com.sun.proxy.$Proxy9.getFileInfo(Unknown Source)\n2020-08-19T21:06:24.337556408Z stderr F \tat org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB.getFileInfo(ClientNamenodeProtocolTranslatorPB.java:771)\n2020-08-19T21:06:24.337559359Z stderr F \tat sun.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)\n2020-08-19T21:06:24.337562715Z stderr F \tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n2020-08-19T21:06:24.337565488Z stderr F \tat java.lang.reflect.Method.invoke(Method.java:498)\n2020-08-19T21:06:24.337574315Z stderr F \tat org.apache.hadoop.io.retry.RetryInvocationHandler.invokeMethod(RetryInvocationHandler.java:191)\n2020-08-19T21:06:24.337577049Z stderr F \tat org.apache.hadoop.io.retry.RetryInvocationHandler.invoke(RetryInvocationHandler.java:102)\n2020-08-19T21:06:24.33757971Z stderr F \tat com.sun.proxy.$Proxy10.getFileInfo(Unknown Source)\n2020-08-19T21:06:24.337582469Z stderr F \tat org.apache.hadoop.hdfs.DFSClient.getFileInfo(DFSClient.java:2108)\n2020-08-19T21:06:24.337585132Z stderr F \tat org.apache.hadoop.hdfs.DistributedFileSystem$22.doCall(DistributedFileSystem.java:1305)\n2020-08-19T21:06:24.337587799Z stderr F \tat org.apache.hadoop.hdfs.DistributedFileSystem$22.doCall(DistributedFileSystem.java:1301)\n2020-08-19T21:06:24.337590342Z stderr F \tat org.apache.hadoop.fs.FileSystemLinkResolver.resolve(FileSystemLinkResolver.java:81)\n2020-08-19T21:06:24.337592897Z stderr F \tat org.apache.hadoop.hdfs.DistributedFileSystem.getFileStatus(DistributedFileSystem.java:1301)\n2020-08-19T21:06:24.337595578Z stderr F \tat org.apache.hadoop.fs.FileSystem.exists(FileSystem.java:1424)",
+ "kubernetes": {
+ "pod_name": "rrrr-bert-completion-tb1-6786c9c8-wj25m",
+ "namespace_name": "k8s-fgg",
+ "pod_id": "50bfc67d-cd3c-410d-9369-8bda8f33b1c7",
+ "labels": {
+ "app": "customized-tb1",
+ "pod-template-hash": "6786c9c8"
+ },
+ "annotations": {
+ "doAs": "weeb",
+ "iddecorator.dkdk.username": "rrrr",
+ "kubernetes.io/limit-ranger": "LimitRanger",
+ "kubernetes.io/psp": "katib-nfs-provisioner",
+ "podpreset.admission.kubernetes.io/podpreset-kube-master": "60792473"
+ },
+ "host": "wedddd.dkdk.qqqq.com",
+ "container_name": "tb1",
+ "docker_id": "50bfc67d-cd3c-410d-9369-8bda8f33b1c7",
+ "container_hash": "qqqq.corp.qqqq.com/ai/centos/tf1.15.0-py3.7-horovod@sha256:68b6885f6d1d3fd87ce425a2b2aa687440b9578740d60996912a816ae67be85e",
+ "container_image": "qqqq.corp.qqqq.com/ai/centos/tf1.15.0-py3.7-horovod:1.2"
+ }
+ }
+]
diff --git a/fluent-bit/tests/internal/data/aws_credentials/credential_process/aws-credential-process b/fluent-bit/tests/internal/data/aws_credentials/credential_process/aws-credential-process
new file mode 100755
index 00000000..45d90650
--- /dev/null
+++ b/fluent-bit/tests/internal/data/aws_credentials/credential_process/aws-credential-process
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+printf '{"Version":1,"AccessKeyId":"%s","SecretAccessKey":"%s"' "${1}" "${_AWS_SECRET_ACCESS_KEY}"
+if [ -n "${_AWS_SESSION_TOKEN}" ]; then
+ printf ',"SessionToken":"%s"' "${_AWS_SESSION_TOKEN}"
+fi
+if [ -n "${_AWS_EXPIRATION}" ]; then
+ printf ',"Expiration":"%s"' "$(/bin/date -d "${_AWS_EXPIRATION}" --utc '+%Y-%m-%dT%H:%M:%SZ')"
+fi
+printf '}'
+
+if [ -n "${_AWS_EXIT_CODE}" ]; then
+ exit "${_AWS_EXIT_CODE}"
+fi
diff --git a/fluent-bit/tests/internal/data/aws_credentials/shared_config.ini b/fluent-bit/tests/internal/data/aws_credentials/shared_config.ini
new file mode 100644
index 00000000..451b23ba
--- /dev/null
+++ b/fluent-bit/tests/internal/data/aws_credentials/shared_config.ini
@@ -0,0 +1,9 @@
+[default]
+region = us-east-1
+credential_process = aws-credential-process default
+output = json
+
+[profile nondefault]
+region = us-east-1
+credential_process = aws-credential-process nondefault
+output = json
diff --git a/fluent-bit/tests/internal/data/aws_credentials/shared_credentials_file.ini b/fluent-bit/tests/internal/data/aws_credentials/shared_credentials_file.ini
new file mode 100644
index 00000000..be9520a4
--- /dev/null
+++ b/fluent-bit/tests/internal/data/aws_credentials/shared_credentials_file.ini
@@ -0,0 +1,28 @@
+[default]
+aws_access_key_id = ASIASDMPIJWXJAXT3O3T
+aws_secret_access_key = EAUBpd/APPT4Nfi4DWY3gnt5TU/4T49laqS5zh8W
+aws_session_token = IQoJb3JpZ2luX2VjEOD//////////wEaCNVzLWVhc3QtMSJHMEUCIKCn7v/EDowMZvJnciSJbxA7rIV4p1K6pOUvcLHM+9EzNgIgeiYbfA47DGSqoEZS3yrRWGN8Fr4Q/bK7ANRgv09Hth8q1gEIWRABGgwxNDQ3MTg3MTE0NzAiDGSqzyXiicOZp63afiqzAUyWOljOn5HaIxRfpQ5pTf+o4roJ2KPlHn+XHEKJZKien4Ydm7zeVi7SbPLKocjmjYJd31PrlbJ43C6AyrhmY57qaD7Zz4N3N0V6mekzvlAeARXsa4deflsbemqkp1WVsBLkO6qUuk+N04+MxIVXAxkW9RSPRTVjxeS2m5Yobygto58WLFE8gacRoNd4lCK4JUmEdiaxJEQQO7leZ3v1XxQr6QBS8P/GmcJYcQTxlA6AFQxIMJKGwfAFOuMB2cEc8cF2Htiqf3LVGMk/6bYKkW7fHUtrnttp28jgWtbbLtFbX/zIdlqwm73Ryp7lI+xkM4XNIT+6ZKa4Xw0/Zw3xLzlk3jic6QWPAcffwR6kOunoTOWJzPskK/RZ4Cd+GyGarxG27Cz6xolAzAsDpdGQwV7kCCUPi6/VHjefwKEk9HjZfejC5WuCS173qFrU9kNb4IrYhnK+wmRzzJfgpWUwerdiJKBz95j1iW9rP1a8p1xLR3EXUMN3LIW0+gP8sFjg5iiqDkaS/tUXWZndM2QdJLcrxwAutFchc0nqJHYTijw=
+
+[nondefault]
+aws_access_key_id = akid
+aws_secret_access_key = skid
+
+[custom]
+aws_access_key_id=custom_access_key_id
+aws_secret_access_key=custom_secret_access_key
+
+#some comment line
+sadfjasdlkfajskldfjasd some garbage = not_actually_valid_but=parser_should_handle_it
+
+[headerwithnokeys]
+
+[nospace]
+aws_access_key_id=akidnospace
+aws_secret_access_key=skidnospace
+aws_session_token=tokennospace
+
+[weirdwhitespace]
+aws_access_key_id= akidweird
+aws_secret_access_key= skidweird
+
+aws_session_token=tokenweird///token==
diff --git a/fluent-bit/tests/internal/data/aws_credentials/shared_credentials_file_nodefault.ini b/fluent-bit/tests/internal/data/aws_credentials/shared_credentials_file_nodefault.ini
new file mode 100644
index 00000000..532d17dd
--- /dev/null
+++ b/fluent-bit/tests/internal/data/aws_credentials/shared_credentials_file_nodefault.ini
@@ -0,0 +1,12 @@
+[nondefault]
+aws_access_key_id = akid
+aws_secret_access_key = skid
+
+[nospace]
+aws_access_key_id=akidnospace
+aws_secret_access_key=skidnospace
+aws_session_token=tokennospace
+
+[weirdwhitespace]
+aws_access_key_id= akidweird
+aws_secret_access_key= skidweird
diff --git a/fluent-bit/tests/internal/data/aws_credentials/web_identity_token_file.txt b/fluent-bit/tests/internal/data/aws_credentials/web_identity_token_file.txt
new file mode 100644
index 00000000..cb43252b
--- /dev/null
+++ b/fluent-bit/tests/internal/data/aws_credentials/web_identity_token_file.txt
@@ -0,0 +1 @@
+this-is-a-fake-jwt \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/config_format/classic/fluent-bit.conf b/fluent-bit/tests/internal/data/config_format/classic/fluent-bit.conf
new file mode 100644
index 00000000..9acaa675
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/classic/fluent-bit.conf
@@ -0,0 +1,35 @@
+@SET a=1
+@SET b=2
+@INCLUDE service.conf
+
+[PARSER]
+ name test_api
+
+[MULTILINE_PARSER]
+ name abc
+
+[CUSTOM]
+ name calyptia
+
+[INPUT]
+ name tail
+ path /var/log/containers/*.log
+
+ [GROUP 1]
+ key1 aa
+ key2 bb
+
+ [GROUP 2]
+ key3 cc
+ key4 dd
+
+[FILTER]
+ name stdout
+ match *
+
+[OUTPUT]
+ name null
+ match *
+
+[UNKNOWN]
+ name blah
diff --git a/fluent-bit/tests/internal/data/config_format/classic/indent_level_error.conf b/fluent-bit/tests/internal/data/config_format/classic/indent_level_error.conf
new file mode 100644
index 00000000..6aabb6c8
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/classic/indent_level_error.conf
@@ -0,0 +1,6 @@
+[INPUT]
+ Name dummy
+
+[OUTPUT]
+ Name stdout
+ Match *
diff --git a/fluent-bit/tests/internal/data/config_format/classic/issue6281.conf b/fluent-bit/tests/internal/data/config_format/classic/issue6281.conf
new file mode 100644
index 00000000..372be20d
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/classic/issue6281.conf
@@ -0,0 +1,2 @@
+@INCLUDE issue6281_input.conf
+@INCLUDE issue6281_output.conf \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/config_format/classic/issue6281_input.conf b/fluent-bit/tests/internal/data/config_format/classic/issue6281_input.conf
new file mode 100644
index 00000000..1125e31c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/classic/issue6281_input.conf
@@ -0,0 +1,2 @@
+[INPUT]
+ Name dummy \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/config_format/classic/issue6281_output.conf b/fluent-bit/tests/internal/data/config_format/classic/issue6281_output.conf
new file mode 100644
index 00000000..6ac53a12
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/classic/issue6281_output.conf
@@ -0,0 +1,2 @@
+[OUTPUT]
+ Name stdout \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/config_format/classic/issue_5880.conf b/fluent-bit/tests/internal/data/config_format/classic/issue_5880.conf
new file mode 100644
index 00000000..57bfa81d
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/classic/issue_5880.conf
@@ -0,0 +1,14 @@
+[INPUT]
+ Name dummy
+ Tag dummy
+
+[FILTER]
+ Name modify
+ Match *
+ ADD foo bar
+ ADD
+
+[OUTPUT]
+ Name stdout
+ Match *
+ Format
diff --git a/fluent-bit/tests/internal/data/config_format/classic/nolimitline.conf b/fluent-bit/tests/internal/data/config_format/classic/nolimitline.conf
new file mode 100644
index 00000000..e7fff842
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/classic/nolimitline.conf
@@ -0,0 +1,11 @@
+[INPUT]
+ name dummy
+
+[FILTER]
+ Name lua
+ Match *
+ code local str = 'abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd'; function cb_filter(tag, ts, record) record.str = str; return 1, ts, record end
+ call cb_filter
+
+[OUTPUT]
+ name stdout
diff --git a/fluent-bit/tests/internal/data/config_format/classic/recursion.conf b/fluent-bit/tests/internal/data/config_format/classic/recursion.conf
new file mode 100644
index 00000000..10fb25d8
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/classic/recursion.conf
@@ -0,0 +1 @@
+@INCLUDE rec* \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/config_format/classic/service.conf b/fluent-bit/tests/internal/data/config_format/classic/service.conf
new file mode 100644
index 00000000..15d1730c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/classic/service.conf
@@ -0,0 +1,4 @@
+[SERVICE]
+ flush 1
+ log_level info
+ http_server on
diff --git a/fluent-bit/tests/internal/data/config_format/yaml/fluent-bit.yaml b/fluent-bit/tests/internal/data/config_format/yaml/fluent-bit.yaml
new file mode 100644
index 00000000..49894552
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/yaml/fluent-bit.yaml
@@ -0,0 +1,29 @@
+env:
+ flush_interval: 1
+
+includes:
+ - service.yaml
+
+customs:
+ - name: ${observability}
+ api_key: zyJUb2tlbklEItoiY2ZlMTcx
+
+pipeline:
+ inputs:
+ - name: tail
+ path: ./test.log
+ parser: json
+ read_from_head: true
+ - name: tail
+ path: ./test.log
+ parser: json
+ read_from_head: true
+
+ filters:
+ - name: record_modifier
+ match: "*"
+ record: powered_by calyptia
+
+ outputs:
+ - name: stdout
+ match: "*"
diff --git a/fluent-bit/tests/internal/data/config_format/yaml/issue_7559.yaml b/fluent-bit/tests/internal/data/config_format/yaml/issue_7559.yaml
new file mode 100644
index 00000000..00927cae
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/yaml/issue_7559.yaml
@@ -0,0 +1,14 @@
+customs:
+ - name: calyptia
+
+pipeline:
+ inputs:
+ - name: fluentbit_metrics
+ scrape_interval: 30
+ scrape_on_start: true
+ tag: _calyptia_cloud
+service:
+ HTTP_Listen: 0.0.0.0
+ HTTP_PORT: 2020
+ HTTP_Server: 'On'
+ Log_Level: debug
diff --git a/fluent-bit/tests/internal/data/config_format/yaml/parsers/parsers-conf.yaml b/fluent-bit/tests/internal/data/config_format/yaml/parsers/parsers-conf.yaml
new file mode 100644
index 00000000..6421a8a1
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/yaml/parsers/parsers-conf.yaml
@@ -0,0 +1,3 @@
+---
+service:
+ parsers_file: parsers.conf
diff --git a/fluent-bit/tests/internal/data/config_format/yaml/parsers/parsers.conf b/fluent-bit/tests/internal/data/config_format/yaml/parsers/parsers.conf
new file mode 100644
index 00000000..9f3b6b33
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/yaml/parsers/parsers.conf
@@ -0,0 +1,6 @@
+[PARSER]
+ Name docker
+ Format json
+ Time_Key time
+ Time_Format %Y-%m-%dT%H:%M:%S.%L
+ Time_Keep On
diff --git a/fluent-bit/tests/internal/data/config_format/yaml/pipelines/slist/even.yaml b/fluent-bit/tests/internal/data/config_format/yaml/pipelines/slist/even.yaml
new file mode 100644
index 00000000..5d5b7c46
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/yaml/pipelines/slist/even.yaml
@@ -0,0 +1,7 @@
+---
+pipeline:
+ inputs:
+ - name: http
+ success_header:
+ - foo bar
+ - bar foo
diff --git a/fluent-bit/tests/internal/data/config_format/yaml/pipelines/slist/odd.yaml b/fluent-bit/tests/internal/data/config_format/yaml/pipelines/slist/odd.yaml
new file mode 100644
index 00000000..a7d2058c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/yaml/pipelines/slist/odd.yaml
@@ -0,0 +1,8 @@
+---
+pipeline:
+ inputs:
+ - name: http
+ success_header:
+ - foo bar
+ - bar foo
+ - foobar barfoo
diff --git a/fluent-bit/tests/internal/data/config_format/yaml/service.yaml b/fluent-bit/tests/internal/data/config_format/yaml/service.yaml
new file mode 100644
index 00000000..712d455b
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/yaml/service.yaml
@@ -0,0 +1,5 @@
+env:
+ observability: calyptia
+
+includes:
+ - test/nested.yaml
diff --git a/fluent-bit/tests/internal/data/config_format/yaml/test.yaml b/fluent-bit/tests/internal/data/config_format/yaml/test.yaml
new file mode 100644
index 00000000..26a3f9f7
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/yaml/test.yaml
@@ -0,0 +1,31 @@
+env:
+ flush_interval: 1
+ my_api_key: abcdefghijk
+
+#includes:
+ #- dummy_pipeline.yaml
+ #- opensearch_pipeline.yaml
+
+service:
+ flush_interval: ${flush_interval}
+ log_level: info
+
+#customs:
+# calyptia:
+# api_key: ${my_api_key}
+
+pipeline:
+ inputs:
+ tail:
+ path: ./test.log
+ parser: json
+ read_from_head: true
+
+ filters:
+ record_modifier:
+ match: "*"
+ record: powered_by calyptia
+
+ outputs:
+ stdout:
+ match: "*"
diff --git a/fluent-bit/tests/internal/data/config_format/yaml/test/dummy_pipeline.yaml b/fluent-bit/tests/internal/data/config_format/yaml/test/dummy_pipeline.yaml
new file mode 100644
index 00000000..770ab99a
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/yaml/test/dummy_pipeline.yaml
@@ -0,0 +1,13 @@
+pipeline:
+ inputs:
+ - name: dummy
+ tag: success
+ group1:
+ a: b
+ c: d
+ outputs:
+ - name: stdout
+ match: "*"
+
+unknown:
+ a: b
diff --git a/fluent-bit/tests/internal/data/config_format/yaml/test/nested.yaml b/fluent-bit/tests/internal/data/config_format/yaml/test/nested.yaml
new file mode 100644
index 00000000..f4707649
--- /dev/null
+++ b/fluent-bit/tests/internal/data/config_format/yaml/test/nested.yaml
@@ -0,0 +1,7 @@
+service:
+ flush: ${flush_interval}
+ log_level: debug
+ http_server: on
+
+includes:
+ - dummy_pipeline.yaml
diff --git a/fluent-bit/tests/internal/data/file/empty_file.txt b/fluent-bit/tests/internal/data/file/empty_file.txt
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/fluent-bit/tests/internal/data/file/empty_file.txt
diff --git a/fluent-bit/tests/internal/data/file/text_file.txt b/fluent-bit/tests/internal/data/file/text_file.txt
new file mode 100644
index 00000000..8c8e5543
--- /dev/null
+++ b/fluent-bit/tests/internal/data/file/text_file.txt
@@ -0,0 +1,5 @@
+Some text file
+
+line 3
+
+line 5
diff --git a/fluent-bit/tests/internal/data/input_chunk/log/a_thousand_plus_one_bytes.log b/fluent-bit/tests/internal/data/input_chunk/log/a_thousand_plus_one_bytes.log
new file mode 100644
index 00000000..ce6ec220
--- /dev/null
+++ b/fluent-bit/tests/internal/data/input_chunk/log/a_thousand_plus_one_bytes.log
@@ -0,0 +1,2 @@
+{"log":"zmHw8IQgSHIR9f4QkIYYZGVBoFhozS6r0EWwuJhTslZeDvYte8r4HwfwYt0clr3gENB9Tpc0vSdbxn7D3xmUFjXlBFgI01cKTcJh4XUUMkNPpcxidVQpSkvxzLiJXHbOiVc0C6AmiLbTP4XSgUiMWS0p42ZkH373JVYIeqa850MVCJUR0Zkx3Uhwq01zOR4eXTKa8UU5t31RV9ghuT2BnR3Aipt0vZk2eBydBLtDonnSHjVFYMwyq2uPmpIGi9V2MbczoTL7sPiFtiRkPHauosLkMtaN1jQCqeQnJ9db8LuTXSRbtqdFh0p7pdpUG29vFgSsbYhS3Yto1gLiqBZSnuLl9mgMYICRQ42OhGvhQE7SByAp7VMpUaA42H3xWH7daNVgHkHFr16LeMQNcGgZ3pZGpfxMd8Ugs5xNIDzrrArtAqaKxAc3BMGLP4d35ipGGIhDQymWhnkJXauM6fSljSmnuqj6kEavdRQ2HJ9WaH108OfKcaIYV5CgjZWqW2ZGSz4NQ9tutKURAntQ3GjvEdaVa3ShCE1mNEWTArch7NN6K3LC8F5mbKiIP79QZ12VyvVcEr98XQAgORykZh4EMBSNwKxEiHDL6BmcFiBkS4DLUBKmlVebRSrAvwoY8cgL17YQXcZHsMOUDLBw4jPxzy5b2A9IFIj10f6QH0D4H2TsSi7wl70EmxjT6UfYhY0SZvzjCl8209QPHtON5KCknB3CJlpGhr9SCvhC7hbguS1IowuvFeTcMgiuy2MGJtl4wKS2gZxFe9C6kLo0krCLF14xhG1b9kBwGD1sFOuDUJ3hxGRpHLPbHQZ99QltqS4LhUuzbzivd5JjxAVGu1gSqAzd5EYU8XaQjPX4FxUUJjbj4CW06lFjAX0sFhfQVkdJ04kMKDLGjMcYaPJwyFBXrQZWR49jD83Xgtp3PU3nR7qlrIYi0OCA1IGgtZrDTjeJXoO9ZK8NcZJ1F3h9hZqN5ba"}
+
diff --git a/fluent-bit/tests/internal/data/input_chunk/log/test_buffer_drop_chunks.h b/fluent-bit/tests/internal/data/input_chunk/log/test_buffer_drop_chunks.h
new file mode 100644
index 00000000..f1c89786
--- /dev/null
+++ b/fluent-bit/tests/internal/data/input_chunk/log/test_buffer_drop_chunks.h
@@ -0,0 +1,27 @@
+#define TEST_BUFFER_DROP_CHUNKS "[" \
+ "1598279645," \
+ "{" \
+ "\"key_0\": false," \
+ "\"key_1\": false," \
+ "\"key_2\": false," \
+ "\"key_3\": false," \
+ "\"key_4\": false," \
+ "\"key_5\": false," \
+ "\"key_6\": false," \
+ "\"key_7\": false," \
+ "\"key_8\": false," \
+ "\"key_9\": false," \
+ "\"key_10\": false," \
+ "\"key_11\": false," \
+ "\"key_12\": false," \
+ "\"key_13\": false," \
+ "\"key_14\": false," \
+ "\"key_15\": false," \
+ "\"key_16\": false," \
+ "\"key_17\": false," \
+ "\"key_18\": false," \
+ "\"key_19\": false," \
+ "\"key_20\": false," \
+ "\"END_KEY\": \"JSON_END\"" \
+ "}]"
+
diff --git a/fluent-bit/tests/internal/data/input_chunk/log/test_buffer_valid.log b/fluent-bit/tests/internal/data/input_chunk/log/test_buffer_valid.log
new file mode 100644
index 00000000..a5a05ee4
--- /dev/null
+++ b/fluent-bit/tests/internal/data/input_chunk/log/test_buffer_valid.log
@@ -0,0 +1,3 @@
+{"log":"Single log\n"}
+{"log":"Second log\n"}
+{"log":"Third log\n"}
diff --git a/fluent-bit/tests/internal/data/input_chunk/out/a_thousand_plus_one_bytes.out b/fluent-bit/tests/internal/data/input_chunk/out/a_thousand_plus_one_bytes.out
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/fluent-bit/tests/internal/data/input_chunk/out/a_thousand_plus_one_bytes.out
diff --git a/fluent-bit/tests/internal/data/input_chunk/out/test_buffer_valid.out b/fluent-bit/tests/internal/data/input_chunk/out/test_buffer_valid.out
new file mode 100644
index 00000000..a5a05ee4
--- /dev/null
+++ b/fluent-bit/tests/internal/data/input_chunk/out/test_buffer_valid.out
@@ -0,0 +1,3 @@
+{"log":"Single log\n"}
+{"log":"Second log\n"}
+{"log":"Third log\n"}
diff --git a/fluent-bit/tests/internal/data/input_chunk/parser.conf b/fluent-bit/tests/internal/data/input_chunk/parser.conf
new file mode 100644
index 00000000..28bde475
--- /dev/null
+++ b/fluent-bit/tests/internal/data/input_chunk/parser.conf
@@ -0,0 +1,10 @@
+[PARSER]
+ Name docker
+ Format json
+ Time_Key time
+ Time_Format %Y-%m-%dT%H:%M:%S.%L%z
+
+[PARSER]
+ Name docker_multiline
+ Format regex
+ Regex (?<log>^{"log":"\d{4}-\d{2}-\d{2}.*)
diff --git a/fluent-bit/tests/internal/data/mp/apache_10k.mp b/fluent-bit/tests/internal/data/mp/apache_10k.mp
new file mode 100644
index 00000000..d3e55e74
--- /dev/null
+++ b/fluent-bit/tests/internal/data/mp/apache_10k.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/pack/README.md b/fluent-bit/tests/internal/data/pack/README.md
new file mode 100644
index 00000000..49b00c1b
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/README.md
@@ -0,0 +1,3 @@
+Data samples on this directory are generated by Python scripts taken as source the .txt files.
+
+Note that utf-8 files with Unicode characters > 0xffff generate an invalid JSON representation 'FOR THE TEST' purposes.
diff --git a/fluent-bit/tests/internal/data/pack/bug342.json b/fluent-bit/tests/internal/data/pack/bug342.json
new file mode 100644
index 00000000..1f9e5544
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/bug342.json
@@ -0,0 +1,240 @@
+{"num": 1}
+{"num": 2}
+{"num": 3}
+{"num": 4}
+{"num": 5}
+{"num": 6}
+{"num": 7}
+{"num": 8}
+{"num": 9}
+{"num": 10}
+{"num": 11}
+{"num": 12}
+{"num": 13}
+{"num": 14}
+{"num": 15}
+{"num": 16}
+{"num": 17}
+{"num": 18}
+{"num": 19}
+{"num": 20}
+{"num": 21}
+{"num": 22}
+{"num": 23}
+{"num": 24}
+{"num": 25}
+{"num": 26}
+{"num": 27}
+{"num": 28}
+{"num": 29}
+{"num": 30}
+{"num": 31}
+{"num": 32}
+{"num": 33}
+{"num": 34}
+{"num": 35}
+{"num": 36}
+{"num": 37}
+{"num": 38}
+{"num": 39}
+{"num": 40}
+{"num": 41}
+{"num": 42}
+{"num": 43}
+{"num": 44}
+{"num": 45}
+{"num": 46}
+{"num": 47}
+{"num": 48}
+{"num": 49}
+{"num": 50}
+{"num": 51}
+{"num": 52}
+{"num": 53}
+{"num": 54}
+{"num": 55}
+{"num": 56}
+{"num": 57}
+{"num": 58}
+{"num": 59}
+{"num": 60}
+{"num": 61}
+{"num": 62}
+{"num": 63}
+{"num": 64}
+{"num": 65}
+{"num": 66}
+{"num": 67}
+{"num": 68}
+{"num": 69}
+{"num": 70}
+{"num": 71}
+{"num": 72}
+{"num": 73}
+{"num": 74}
+{"num": 75}
+{"num": 76}
+{"num": 77}
+{"num": 78}
+{"num": 79}
+{"num": 80}
+{"num": 81}
+{"num": 82}
+{"num": 83}
+{"num": 84}
+{"num": 85}
+{"num": 86}
+{"num": 87}
+{"num": 88}
+{"num": 89}
+{"num": 90}
+{"num": 91}
+{"num": 92}
+{"num": 93}
+{"num": 94}
+{"num": 95}
+{"num": 96}
+{"num": 97}
+{"num": 98}
+{"num": 99}
+{"num": 100}
+{"num": 101}
+{"num": 102}
+{"num": 103}
+{"num": 104}
+{"num": 105}
+{"num": 106}
+{"num": 107}
+{"num": 108}
+{"num": 109}
+{"num": 110}
+{"num": 111}
+{"num": 112}
+{"num": 113}
+{"num": 114}
+{"num": 115}
+{"num": 116}
+{"num": 117}
+{"num": 118}
+{"num": 119}
+{"num": 120}
+{"num": 121}
+{"num": 122}
+{"num": 123}
+{"num": 124}
+{"num": 125}
+{"num": 126}
+{"num": 127}
+{"num": 128}
+{"num": 129}
+{"num": 130}
+{"num": 131}
+{"num": 132}
+{"num": 133}
+{"num": 134}
+{"num": 135}
+{"num": 136}
+{"num": 137}
+{"num": 138}
+{"num": 139}
+{"num": 140}
+{"num": 141}
+{"num": 142}
+{"num": 143}
+{"num": 144}
+{"num": 145}
+{"num": 146}
+{"num": 147}
+{"num": 148}
+{"num": 149}
+{"num": 150}
+{"num": 151}
+{"num": 152}
+{"num": 153}
+{"num": 154}
+{"num": 155}
+{"num": 156}
+{"num": 157}
+{"num": 158}
+{"num": 159}
+{"num": 160}
+{"num": 161}
+{"num": 162}
+{"num": 163}
+{"num": 164}
+{"num": 165}
+{"num": 166}
+{"num": 167}
+{"num": 168}
+{"num": 169}
+{"num": 170}
+{"num": 171}
+{"num": 172}
+{"num": 173}
+{"num": 174}
+{"num": 175}
+{"num": 176}
+{"num": 177}
+{"num": 178}
+{"num": 179}
+{"num": 180}
+{"num": 181}
+{"num": 182}
+{"num": 183}
+{"num": 184}
+{"num": 185}
+{"num": 186}
+{"num": 187}
+{"num": 188}
+{"num": 189}
+{"num": 190}
+{"num": 191}
+{"num": 192}
+{"num": 193}
+{"num": 194}
+{"num": 195}
+{"num": 196}
+{"num": 197}
+{"num": 198}
+{"num": 199}
+{"num": 200}
+{"num": 201}
+{"num": 202}
+{"num": 203}
+{"num": 204}
+{"num": 205}
+{"num": 206}
+{"num": 207}
+{"num": 208}
+{"num": 209}
+{"num": 210}
+{"num": 211}
+{"num": 212}
+{"num": 213}
+{"num": 214}
+{"num": 215}
+{"num": 216}
+{"num": 217}
+{"num": 218}
+{"num": 219}
+{"num": 220}
+{"num": 221}
+{"num": 222}
+{"num": 223}
+{"num": 224}
+{"num": 225}
+{"num": 226}
+{"num": 227}
+{"num": 228}
+{"num": 229}
+{"num": 230}
+{"num": 231}
+{"num": 232}
+{"num": 233}
+{"num": 234}
+{"num": 235}
+{"num": 236}
+{"num": 237}
+{"num": 238}
+{"num": 239}
+{"num": 240}
diff --git a/fluent-bit/tests/internal/data/pack/dup_keys_in.json b/fluent-bit/tests/internal/data/pack/dup_keys_in.json
new file mode 100644
index 00000000..bdfb9d7c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/dup_keys_in.json
@@ -0,0 +1 @@
+[1601487301, {"date": 872835240, "map": {"sub1": false, "sub2": "aaa", "sub3": "bbb", "sub1": null, "sub1": true}, "key1": 12345, "key2": 444, "date": 1059113640, "key1": 333}]
diff --git a/fluent-bit/tests/internal/data/pack/dup_keys_out.json b/fluent-bit/tests/internal/data/pack/dup_keys_out.json
new file mode 100644
index 00000000..5bb557eb
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/dup_keys_out.json
@@ -0,0 +1 @@
+{"map":{"sub2":"aaa","sub3":"bbb","sub1":true},"key2":444,"date":1059113640,"key1":333}
diff --git a/fluent-bit/tests/internal/data/pack/json_single_map_001.json b/fluent-bit/tests/internal/data/pack/json_single_map_001.json
new file mode 100644
index 00000000..6833b392
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/json_single_map_001.json
@@ -0,0 +1,5 @@
+{"key001": 123456789,
+ "key002": 0.999887766,
+ "key003": "abcdefghijk",
+ "key004": [{"a": 1, "b": 2}, {"c": 3, "d": 4}]
+}
diff --git a/fluent-bit/tests/internal/data/pack/json_single_map_002.json b/fluent-bit/tests/internal/data/pack/json_single_map_002.json
new file mode 100644
index 00000000..d5a3709e
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/json_single_map_002.json
@@ -0,0 +1,4 @@
+{"AAA": 123456789,
+ "BBB": 0.999887766,
+ "CCC": ["aa", "bb", "cc", [1, 2, 3]]
+}
diff --git a/fluent-bit/tests/internal/data/pack/mixed.py b/fluent-bit/tests/internal/data/pack/mixed.py
new file mode 100644
index 00000000..7ad0a614
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/mixed.py
@@ -0,0 +1,31 @@
+# Fluent Bit / Pack mixed samples to JSON
+# =======================================
+# This script generate the JSON formatted strings for the mixed_ABC.txt samples.
+
+import os
+import json
+import msgpack
+
+def gen_json(f):
+ raw = open(f, 'r')
+ data = raw.read()
+ raw.close()
+
+ out_mp = f[:-4] + ".mp"
+ out_json = f[:-4] + ".json"
+
+ # Write messagepack
+ fmp = open(out_mp, 'w')
+ fmp.write(msgpack.packb(data))
+ fmp.close()
+
+ fjson = open(out_json, 'w')
+ fjson.write(json.dumps(data))
+ fjson.close()
+
+for fn in os.listdir('.'):
+ if not os.path.isfile(fn):
+ continue
+
+ if fn.startswith('mixed_') and fn.endswith('.txt'):
+ gen_json(fn)
diff --git a/fluent-bit/tests/internal/data/pack/mixed_001.json b/fluent-bit/tests/internal/data/pack/mixed_001.json
new file mode 100644
index 00000000..10f7faad
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/mixed_001.json
@@ -0,0 +1 @@
+"mixed_001 => \u001b[1;34mI\u001b[0m test\n" \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/mixed_001.mp b/fluent-bit/tests/internal/data/pack/mixed_001.mp
new file mode 100644
index 00000000..587dc63f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/mixed_001.mp
@@ -0,0 +1 @@
+¿mixed_001 => I test
diff --git a/fluent-bit/tests/internal/data/pack/mixed_001.txt b/fluent-bit/tests/internal/data/pack/mixed_001.txt
new file mode 100644
index 00000000..d217bff8
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/mixed_001.txt
@@ -0,0 +1 @@
+mixed_001 => I test
diff --git a/fluent-bit/tests/internal/data/pack/mixed_002.json b/fluent-bit/tests/internal/data/pack/mixed_002.json
new file mode 100644
index 00000000..e32314b0
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/mixed_002.json
@@ -0,0 +1 @@
+"mixed_002 =>\n\n áéíóú\n\n\n'\n\\t\n" \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/mixed_002.mp b/fluent-bit/tests/internal/data/pack/mixed_002.mp
new file mode 100644
index 00000000..1bf97553
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/mixed_002.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/pack/mixed_002.txt b/fluent-bit/tests/internal/data/pack/mixed_002.txt
new file mode 100644
index 00000000..a85912bb
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/mixed_002.txt
@@ -0,0 +1,7 @@
+mixed_002 =>
+
+ áéíóú
+
+
+'
+\t
diff --git a/fluent-bit/tests/internal/data/pack/mixed_003.json b/fluent-bit/tests/internal/data/pack/mixed_003.json
new file mode 100644
index 00000000..167c89b8
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/mixed_003.json
@@ -0,0 +1 @@
+"á\n" \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/mixed_003.mp b/fluent-bit/tests/internal/data/pack/mixed_003.mp
new file mode 100644
index 00000000..760f5e18
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/mixed_003.mp
@@ -0,0 +1 @@
+£Ã¡
diff --git a/fluent-bit/tests/internal/data/pack/mixed_003.txt b/fluent-bit/tests/internal/data/pack/mixed_003.txt
new file mode 100644
index 00000000..072d5937
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/mixed_003.txt
@@ -0,0 +1 @@
+á
diff --git a/fluent-bit/tests/internal/data/pack/utf8_bell.json b/fluent-bit/tests/internal/data/pack/utf8_bell.json
new file mode 100644
index 00000000..ced4da0c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_bell.json
@@ -0,0 +1 @@
+"🔔" \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_bell.mp b/fluent-bit/tests/internal/data/pack/utf8_bell.mp
new file mode 100644
index 00000000..ea4ce5c0
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_bell.mp
@@ -0,0 +1 @@
+¤ðŸ”” \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_bell.txt b/fluent-bit/tests/internal/data/pack/utf8_bell.txt
new file mode 100644
index 00000000..89b9de00
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_bell.txt
@@ -0,0 +1 @@
+🔔 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_copyright.json b/fluent-bit/tests/internal/data/pack/utf8_copyright.json
new file mode 100644
index 00000000..4d52a66f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_copyright.json
@@ -0,0 +1 @@
+"©" \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_copyright.mp b/fluent-bit/tests/internal/data/pack/utf8_copyright.mp
new file mode 100644
index 00000000..6bd6a103
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_copyright.mp
@@ -0,0 +1 @@
+¢Â© \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_copyright.txt b/fluent-bit/tests/internal/data/pack/utf8_copyright.txt
new file mode 100644
index 00000000..5f8778c5
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_copyright.txt
@@ -0,0 +1 @@
+© \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_gen.py b/fluent-bit/tests/internal/data/pack/utf8_gen.py
new file mode 100644
index 00000000..606e8cc2
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_gen.py
@@ -0,0 +1,32 @@
+# Fluent Bit / Pack utf-8 samples to JSON
+# =======================================
+
+import os
+import json
+import msgpack
+
+def gen_json(f):
+
+ print f
+
+ with io.open(f, 'rb') as raw:
+ data = raw.read()
+
+ out_mp = f[:-4] + ".mp"
+ out_json = f[:-4] + ".json"
+
+ # Write messagepack
+ fmp = open(out_mp, 'w')
+ fmp.write(msgpack.packb(data))
+ fmp.close()
+
+ fjson = open(out_json, 'w')
+ fjson.write(json.dumps(data).encode('utf8'))
+ fjson.close()
+
+for fn in os.listdir('.'):
+ if not os.path.isfile(fn):
+ continue
+
+ if fn.startswith('utf8_') and fn.endswith('.txt'):
+ gen_json(fn)
diff --git a/fluent-bit/tests/internal/data/pack/utf8_hokke.json b/fluent-bit/tests/internal/data/pack/utf8_hokke.json
new file mode 100644
index 00000000..d93624bf
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_hokke.json
@@ -0,0 +1 @@
+"𩸽" \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_hokke.mp b/fluent-bit/tests/internal/data/pack/utf8_hokke.mp
new file mode 100644
index 00000000..704885a8
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_hokke.mp
@@ -0,0 +1 @@
+¤ð©¸½ \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_hokke.txt b/fluent-bit/tests/internal/data/pack/utf8_hokke.txt
new file mode 100644
index 00000000..f1d17d76
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_hokke.txt
@@ -0,0 +1 @@
+𩸽 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_relaxed.json b/fluent-bit/tests/internal/data/pack/utf8_relaxed.json
new file mode 100644
index 00000000..4526bf40
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_relaxed.json
@@ -0,0 +1 @@
+"☺" \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_relaxed.mp b/fluent-bit/tests/internal/data/pack/utf8_relaxed.mp
new file mode 100644
index 00000000..1fcce51e
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_relaxed.mp
@@ -0,0 +1 @@
+£â˜º \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/pack/utf8_relaxed.txt b/fluent-bit/tests/internal/data/pack/utf8_relaxed.txt
new file mode 100644
index 00000000..e2890a9c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/pack/utf8_relaxed.txt
@@ -0,0 +1 @@
+☺ \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/parser/json.conf b/fluent-bit/tests/internal/data/parser/json.conf
new file mode 100644
index 00000000..198bbf3c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/parser/json.conf
@@ -0,0 +1,200 @@
+# Parser: no_year
+# ===============
+# the given format don't contain the Year, this is a common
+# case on old Syslog implementations.
+#
+[PARSER]
+ Name no_year
+ Format json
+ Time_Key time
+ Time_Format %b %d %H:%M:%S
+ Time_Keep On
+
+# Parser: no_year_N
+# =================
+# Just for compatibility, check a string with no year but including Nanoseconds.
+#
+[PARSER]
+ Name no_year_N
+ Format json
+ Time_Key time
+ Time_Format %b %d %H:%M:%S.%L
+ Time_Keep On
+
+# Parser: no_year_NC
+# =================
+# Just for compatibility, check a string with no year but including Nanoseconds with comma as fractional separator.
+#
+[PARSER]
+ Name no_year_NC
+ Format json
+ Time_Key time
+ Time_Format %b %d %H:%M:%S,%L
+ Time_Keep On
+
+# Parser: no_year_TZ
+# =================
+# Time string with no year and including timezone
+#
+[PARSER]
+ Name no_year_TZ
+ Format json
+ Time_Key time
+ Time_Format %b %d %H:%M:%S %z
+ Time_Keep On
+
+# Parser: no_year_N_TZ
+# ====================
+# Time string with no year, nanoseconds and timezone
+#
+[PARSER]
+ Name no_year_N_TZ
+ Format json
+ Time_Key time
+ Time_Format %b %d %H:%M:%S.%L %z
+ Time_Keep On
+
+
+# Parser: no_year_NC_TZ
+# ====================
+# Time string with no year, nanoseconds and timezone with comma as fractional separator.
+#
+[PARSER]
+ Name no_year_NC_TZ
+ Format json
+ Time_Key time
+ Time_Format %b %d %H:%M:%S,%L %z
+ Time_Keep On
+
+
+# Parser: default_UTC
+# ===================
+# Time string with timezone in UTC
+#
+[PARSER]
+ Name default_UTC
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S
+ Time_Keep On
+
+# Parser: default_UTC_Z
+# =====================
+# Time string with timezone in UTC and ending Z
+#
+[PARSER]
+ Name default_UTC_Z
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%SZ
+ Time_Keep On
+
+# Parser: default_UTC_N_Z
+# =======================
+# Time string with timezone in UTC, nanoseconds and ending Z
+#
+[PARSER]
+ Name default_UTC_N_Z
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S.%LZ
+ Time_Keep On
+
+# Parser: default_UTC_NC_Z
+# =======================
+# Time string with timezone in UTC, nanoseconds with comma as fractional separator and ending Z
+#
+[PARSER]
+ Name default_UTC_NC_Z
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S,%LZ
+ Time_Keep On
+
+# Parser: generic_TZ
+# ==================
+# Generic date with timezone
+#
+[PARSER]
+ Name generic_TZ
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S %z
+ Time_Keep On
+
+# Parser: generic
+# ===============
+# Generic date
+#
+[PARSER]
+ Name generic
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S
+ Time_Keep On
+
+# Parser: generic_N
+# ===============
+# Generic date with nanoseconds
+#
+[PARSER]
+ Name generic_N
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S.%L
+ Time_Keep On
+
+# Parser: generic_NC
+# ===============
+# Generic date with nanoseconds with comma as fractional separator
+#
+[PARSER]
+ Name generic_NC
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S,%L
+ Time_Keep On
+
+# Parser: generic_N_TZ
+# ====================
+# Generic date with nanoseconds and timezone
+#
+[PARSER]
+ Name generic_N_TZ
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S.%L %z
+ Time_Keep On
+
+# Parser: generic_NC_TZ
+# ====================
+# Generic date with nanoseconds with comma as fractional separator and timezone
+#
+[PARSER]
+ Name generic_NC_TZ
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S,%L %z
+ Time_Keep On
+
+# Parser: generic_NL_TZ
+# ====================
+# Generic date with nanoseconds with colon as fractional separator and timezone
+#
+[PARSER]
+ Name generic_NL_TZ
+ Format json
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S:%L %z
+ Time_Keep On
+
+# Parser: apache_error
+# ====================
+# Apache error log time format
+#
+[PARSER]
+ Name apache_error
+ Format json
+ Time_Key time
+ Time_Format %a %b %d %H:%M:%S.%L %Y
+ Time_Keep On
diff --git a/fluent-bit/tests/internal/data/parser/regex.conf b/fluent-bit/tests/internal/data/parser/regex.conf
new file mode 100644
index 00000000..5b340dc3
--- /dev/null
+++ b/fluent-bit/tests/internal/data/parser/regex.conf
@@ -0,0 +1,234 @@
+# Parser: no_year
+# ===============
+# the given format don't contain the Year, this is a common
+# case on old Syslog implementations.
+#
+[PARSER]
+ Name no_year
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %b %d %H:%M:%S
+ Time_Keep On
+
+# Parser: no_year_N
+# =================
+# Just for compatibility, check a string with no year but including Nanoseconds.
+#
+[PARSER]
+ Name no_year_N
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %b %d %H:%M:%S.%L
+ Time_Keep On
+
+# Parser: no_year_NC
+# =================
+# Just for compatibility, check a string with no year but including Nanoseconds with comma as fractional separator.
+#
+[PARSER]
+ Name no_year_NC
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %b %d %H:%M:%S,%L
+ Time_Keep On
+
+# Parser: no_year_TZ
+# =================
+# Time string with no year and including timezone
+#
+[PARSER]
+ Name no_year_TZ
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %b %d %H:%M:%S %z
+ Time_Keep On
+
+# Parser: no_year_N_TZ
+# ====================
+# Time string with no year, nanoseconds and timezone
+#
+[PARSER]
+ Name no_year_N_TZ
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %b %d %H:%M:%S.%L %z
+ Time_Keep On
+
+
+# Parser: no_year_NC_TZ
+# ====================
+# Time string with no year, nanoseconds and timezone with comma as fractional separator.
+#
+[PARSER]
+ Name no_year_NC_TZ
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %b %d %H:%M:%S,%L %z
+ Time_Keep On
+
+
+# Parser: default_UTC
+# ===================
+# Time string with timezone in UTC
+#
+[PARSER]
+ Name default_UTC
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S
+ Time_Keep On
+
+# Parser: default_UTC_Z
+# =====================
+# Time string with timezone in UTC and ending Z
+#
+[PARSER]
+ Name default_UTC_Z
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%SZ
+ Time_Keep On
+
+# Parser: default_UTC_N_Z
+# =======================
+# Time string with timezone in UTC, nanoseconds and ending Z
+#
+[PARSER]
+ Name default_UTC_N_Z
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S.%LZ
+ Time_Keep On
+
+# Parser: default_UTC_NC_Z
+# =======================
+# Time string with timezone in UTC, nanoseconds with comma as fractional separator and ending Z
+#
+[PARSER]
+ Name default_UTC_NC_Z
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S,%LZ
+ Time_Keep On
+
+# Parser: generic_TZ
+# ==================
+# Generic date with timezone
+#
+[PARSER]
+ Name generic_TZ
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S %z
+ Time_Keep On
+
+# Parser: generic
+# ===============
+# Generic date
+#
+[PARSER]
+ Name generic
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S
+ Time_Keep On
+
+# Parser: generic_N
+# ===============
+# Generic date with nanoseconds
+#
+[PARSER]
+ Name generic_N
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S.%L
+ Time_Keep On
+
+# Parser: generic_NC
+# ===============
+# Generic date with nanoseconds with comma as fractional separator
+#
+[PARSER]
+ Name generic_NC
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S,%L
+ Time_Keep On
+
+# Parser: generic_N_TZ
+# ====================
+# Generic date with nanoseconds and timezone
+#
+[PARSER]
+ Name generic_N_TZ
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S.%L %z
+ Time_Keep On
+
+# Parser: generic_NC_TZ
+# ====================
+# Generic date with nanoseconds with comma as fractional separator and timezone
+#
+[PARSER]
+ Name generic_NC_TZ
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S,%L %z
+ Time_Keep On
+
+# Parser: generic_NL_TZ
+# ====================
+# Generic date with nanoseconds with colon as fractional separator and timezone
+#
+[PARSER]
+ Name generic_NL_TZ
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %m/%d/%Y %H:%M:%S:%L %z
+ Time_Keep On
+
+
+# Parser: apache_error
+# ====================
+# Apache error log time format
+#
+[PARSER]
+ Name apache_error
+ Format regex
+ Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$
+ Time_Key time
+ Time_Format %a %b %d %H:%M:%S.%L %Y
+ Time_Keep On
+
+
+
+# Parser: mysql_quoted_stuff
+# ====================
+# Apache error log time format
+#
+[PARSER]
+ Name mysql_quoted_stuff
+ Format regex
+ Regex ^(?<time>.*?),(?<key001>.*)$
+ Time_Key time
+ Time_Format %Y-%M-%S %H:%M:%S
+ Time_Keep On
+ Decode_Field_As mysql_quoted key001
diff --git a/fluent-bit/tests/internal/data/reload/fluent-bit.conf b/fluent-bit/tests/internal/data/reload/fluent-bit.conf
new file mode 100644
index 00000000..4db44f32
--- /dev/null
+++ b/fluent-bit/tests/internal/data/reload/fluent-bit.conf
@@ -0,0 +1,15 @@
+[SERVICE]
+ Flush 1
+ Daemon Off
+ Log_Level error
+ HTTP_Server On
+ HTTP_Listen 0.0.0.0
+ HTTP_Port 2020
+
+[INPUT]
+ Name dummy
+ Tag dummy.locals
+
+[OUTPUT]
+ Name stdout
+ Match *
diff --git a/fluent-bit/tests/internal/data/reload/yaml/processor.yaml b/fluent-bit/tests/internal/data/reload/yaml/processor.yaml
new file mode 100644
index 00000000..3021ee7e
--- /dev/null
+++ b/fluent-bit/tests/internal/data/reload/yaml/processor.yaml
@@ -0,0 +1,41 @@
+service:
+ log_level: info
+ http_server: on
+ http_listen: 0.0.0.0
+ http_port: 2021
+
+
+pipeline:
+ inputs:
+ - name: random
+ tag: test-tag
+ interval_sec: 1
+
+ processors:
+ logs:
+ - name: modify
+ add: hostname monox
+
+ - name: lua
+ call: append_tag
+ code: |
+ function append_tag(tag, timestamp, record)
+ new_record = record
+ new_record["tag"] = tag
+ return 1, timestamp, new_record
+ end
+
+ outputs:
+ - name: stdout
+ match: '*'
+
+ processors:
+ logs:
+ - name: lua
+ call: add_field
+ code: |
+ function add_field(tag, timestamp, record)
+ new_record = record
+ new_record["output"] = "new data"
+ return 1, timestamp, new_record
+ end
diff --git a/fluent-bit/tests/internal/data/s3_local_buffer/.gitkeep b/fluent-bit/tests/internal/data/s3_local_buffer/.gitkeep
new file mode 100644
index 00000000..786a4bd9
--- /dev/null
+++ b/fluent-bit/tests/internal/data/s3_local_buffer/.gitkeep
@@ -0,0 +1 @@
+# git won't keep an empty dir, hence this file \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/LICENSE b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/LICENSE
new file mode 100644
index 00000000..d6456956
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/NOTICE b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/NOTICE
new file mode 100644
index 00000000..a0821aef
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/NOTICE
@@ -0,0 +1,2 @@
+AWS Signature Version 4 Test Suite
+Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.authz
new file mode 100644
index 00000000..ade3ec75
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c9d5ea9f3f72853aea855b47ea873832890dbdd183b4468f858259531a5138ea \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.creq
new file mode 100644
index 00000000..fa8f49a1
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.creq
@@ -0,0 +1,9 @@
+GET
+/
+
+host:example.amazonaws.com
+my-header1:value2,value2,value1
+x-amz-date:20150830T123600Z
+
+host;my-header1;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.req
new file mode 100644
index 00000000..08a0364c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.req
@@ -0,0 +1,6 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1:value2
+My-Header1:value2
+My-Header1:value1
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.sreq
new file mode 100644
index 00000000..f0166e18
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.sreq
@@ -0,0 +1,7 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1:value2
+My-Header1:value2
+My-Header1:value1
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c9d5ea9f3f72853aea855b47ea873832890dbdd183b4468f858259531a5138ea \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.sts
new file mode 100644
index 00000000..48a135ec
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+dc7f04a3abfde8d472b0ab1a418b741b7c67174dad1551b4117b15527fbe966c \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.authz
new file mode 100644
index 00000000..e2717bf6
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=ba17b383a53190154eb5fa66a1b836cc297cc0a3d70a5d00705980573d8ff790 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.creq
new file mode 100644
index 00000000..721a39ff
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.creq
@@ -0,0 +1,9 @@
+GET
+/
+
+host:example.amazonaws.com
+my-header1:value1,value2,value3
+x-amz-date:20150830T123600Z
+
+host;my-header1;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.req
new file mode 100644
index 00000000..7caa6acc
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.req
@@ -0,0 +1,6 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1:value1
+ value2
+ value3
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.sreq
new file mode 100644
index 00000000..56955d9a
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.sreq
@@ -0,0 +1,7 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1:value1
+ value2
+ value3
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=ba17b383a53190154eb5fa66a1b836cc297cc0a3d70a5d00705980573d8ff790 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.sts
new file mode 100644
index 00000000..0a3350ab
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+b7b6cbfd8a0430b78891e986784da2630c8a135a8595cec25b26ea94f926ee55 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.authz
new file mode 100644
index 00000000..c0409ab2
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=08c7e5a9acfcfeb3ab6b2185e75ce8b1deb5e634ec47601a50643f830c755c01 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.creq
new file mode 100644
index 00000000..e336bc94
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.creq
@@ -0,0 +1,9 @@
+GET
+/
+
+host:example.amazonaws.com
+my-header1:value4,value1,value3,value2
+x-amz-date:20150830T123600Z
+
+host;my-header1;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.req
new file mode 100644
index 00000000..f7bd9e66
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.req
@@ -0,0 +1,7 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1:value4
+My-Header1:value1
+My-Header1:value3
+My-Header1:value2
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.sreq
new file mode 100644
index 00000000..79e16a95
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.sreq
@@ -0,0 +1,8 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1:value4
+My-Header1:value1
+My-Header1:value3
+My-Header1:value2
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=08c7e5a9acfcfeb3ab6b2185e75ce8b1deb5e634ec47601a50643f830c755c01 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.sts
new file mode 100644
index 00000000..711a8d4d
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+31ce73cd3f3d9f66977ad3dd957dc47af14df92fcd8509f59b349e9137c58b86 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.authz
new file mode 100644
index 00000000..4874ac0b
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;my-header2;x-amz-date, Signature=acc3ed3afb60bb290fc8d2dd0098b9911fcaa05412b367055dee359757a9c736 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.creq
new file mode 100644
index 00000000..a59087c9
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.creq
@@ -0,0 +1,10 @@
+GET
+/
+
+host:example.amazonaws.com
+my-header1:value1
+my-header2:"a b c"
+x-amz-date:20150830T123600Z
+
+host;my-header1;my-header2;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.req
new file mode 100644
index 00000000..901f36c3
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.req
@@ -0,0 +1,5 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1: value1
+My-Header2: "a b c"
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.sreq
new file mode 100644
index 00000000..98224c9b
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.sreq
@@ -0,0 +1,6 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1: value1
+My-Header2: "a b c"
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;my-header2;x-amz-date, Signature=acc3ed3afb60bb290fc8d2dd0098b9911fcaa05412b367055dee359757a9c736 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.sts
new file mode 100644
index 00000000..a0b15cc7
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+a726db9b0df21c14f559d0a978e563112acb1b9e05476f0a6a1c7d68f28605c7 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.authz
new file mode 100644
index 00000000..2943ec89
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=07ef7494c76fa4850883e2b006601f940f8a34d404d0cfa977f52a65bbf5f24f \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.creq
new file mode 100644
index 00000000..8af54df2
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.creq
@@ -0,0 +1,8 @@
+GET
+/-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.req
new file mode 100644
index 00000000..da760cdb
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.req
@@ -0,0 +1,3 @@
+GET /-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sreq
new file mode 100644
index 00000000..8001b3d6
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sreq
@@ -0,0 +1,4 @@
+GET /-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=07ef7494c76fa4850883e2b006601f940f8a34d404d0cfa977f52a65bbf5f24f \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sts
new file mode 100644
index 00000000..e9dc5414
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+6a968768eefaa713e2a6b16b589a8ea192661f098f37349f4e2c0082757446f9 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.authz
new file mode 100644
index 00000000..738b3fbd
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=8318018e0b0f223aa2bbf98705b62bb787dc9c0e678f255a891fd03141be5d85 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.creq
new file mode 100644
index 00000000..5d4b9f61
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.creq
@@ -0,0 +1,8 @@
+GET
+/%E1%88%B4
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.req
new file mode 100644
index 00000000..da4808d0
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.req
@@ -0,0 +1,3 @@
+GET /ሴ HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.sreq
new file mode 100644
index 00000000..94eadb6d
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.sreq
@@ -0,0 +1,4 @@
+GET /ሴ HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=8318018e0b0f223aa2bbf98705b62bb787dc9c0e678f255a891fd03141be5d85 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.sts
new file mode 100644
index 00000000..5edc8f45
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-utf8/get-utf8.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+2a0a97d02205e45ce2e994789806b19270cfbbb0921b278ccf58f5249ac42102 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.authz
new file mode 100644
index 00000000..65b5c7ce
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=a67d582fa61cc504c4bae71f336f98b97f1ea3c7a6bfe1b6e45aec72011b9aeb \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.creq
new file mode 100644
index 00000000..c6cdceda
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.creq
@@ -0,0 +1,8 @@
+GET
+/
+Param1=value1
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.req
new file mode 100644
index 00000000..970d0a05
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.req
@@ -0,0 +1,3 @@
+GET /?Param1=value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sreq
new file mode 100644
index 00000000..f0815913
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sreq
@@ -0,0 +1,4 @@
+GET /?Param1=value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=a67d582fa61cc504c4bae71f336f98b97f1ea3c7a6bfe1b6e45aec72011b9aeb \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sts
new file mode 100644
index 00000000..c4ed216c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+1e24db194ed7d0eec2de28d7369675a243488e08526e8c1c73571282f7c517ab \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.authz
new file mode 100644
index 00000000..c781fe66
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=b97d918cfa904a5beff61c982a1b6f458b799221646efd99d3219ec94cdf2500 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.creq
new file mode 100644
index 00000000..8ae02cd6
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.creq
@@ -0,0 +1,8 @@
+GET
+/
+Param1=value1&Param2=value2
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.req
new file mode 100644
index 00000000..8a56f15f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.req
@@ -0,0 +1,3 @@
+GET /?Param2=value2&Param1=value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sreq
new file mode 100644
index 00000000..aa3162d8
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sreq
@@ -0,0 +1,4 @@
+GET /?Param2=value2&Param1=value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=b97d918cfa904a5beff61c982a1b6f458b799221646efd99d3219ec94cdf2500 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sts
new file mode 100644
index 00000000..f773de59
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+816cd5b414d056048ba4f7c5386d6e0533120fb1fcfa93762cf0fc39e2cf19e0 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.authz
new file mode 100644
index 00000000..812cd3fd
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=eedbc4e291e521cf13422ffca22be7d2eb8146eecf653089df300a15b2382bd1 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.creq
new file mode 100644
index 00000000..36c3cdfa
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.creq
@@ -0,0 +1,8 @@
+GET
+/
+Param1=Value1&Param1=value2
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.req
new file mode 100644
index 00000000..375a4965
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.req
@@ -0,0 +1,3 @@
+GET /?Param1=value2&Param1=Value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.sreq
new file mode 100644
index 00000000..bc8e6520
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.sreq
@@ -0,0 +1,4 @@
+GET /?Param1=value2&Param1=Value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=eedbc4e291e521cf13422ffca22be7d2eb8146eecf653089df300a15b2382bd1 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.sts
new file mode 100644
index 00000000..fd43a414
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+704b4cef673542d84cdff252633f065e8daeba5f168b77116f8b1bcaf3d38f89 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.authz
new file mode 100644
index 00000000..b8ad91f6
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5772eed61e12b33fae39ee5e7012498b51d56abc0abb7c60486157bd471c4694 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.creq
new file mode 100644
index 00000000..26898ebe
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.creq
@@ -0,0 +1,8 @@
+GET
+/
+Param1=value1&Param1=value2
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.req
new file mode 100644
index 00000000..9255bee0
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.req
@@ -0,0 +1,3 @@
+GET /?Param1=value2&Param1=value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.sreq
new file mode 100644
index 00000000..4793e218
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.sreq
@@ -0,0 +1,4 @@
+GET /?Param1=value2&Param1=value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5772eed61e12b33fae39ee5e7012498b51d56abc0abb7c60486157bd471c4694 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.sts
new file mode 100644
index 00000000..90e66b8d
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+c968629d70850097a2d8781c9bf7edcb988b04cac14cca9be4acc3595f884606 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.authz
new file mode 100644
index 00000000..a44ca5be
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9c3e54bfcdf0b19771a7f523ee5669cdf59bc7cc0884027167c21bb143a40197 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.creq
new file mode 100644
index 00000000..5249be3b
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.creq
@@ -0,0 +1,8 @@
+GET
+/
+-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.req
new file mode 100644
index 00000000..d2833b32
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.req
@@ -0,0 +1,3 @@
+GET /?-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sreq
new file mode 100644
index 00000000..ba1ef402
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sreq
@@ -0,0 +1,4 @@
+GET /?-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9c3e54bfcdf0b19771a7f523ee5669cdf59bc7cc0884027167c21bb143a40197 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sts
new file mode 100644
index 00000000..24a97d20
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+c30d4703d9f799439be92736156d47ccfb2d879ddf56f5befa6d1d6aab979177 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.authz
new file mode 100644
index 00000000..551c0271
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.creq
new file mode 100644
index 00000000..ed91561f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.creq
@@ -0,0 +1,8 @@
+GET
+/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.req
new file mode 100644
index 00000000..0f7a9bfa
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.req
@@ -0,0 +1,3 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sreq
new file mode 100644
index 00000000..d739b01f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sreq
@@ -0,0 +1,4 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sts
new file mode 100644
index 00000000..b187649c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.authz
new file mode 100644
index 00000000..e016c3da
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=2cdec8eed098649ff3a119c94853b13c643bcf08f8b0a1d91e12c9027818dd04 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.creq
new file mode 100644
index 00000000..a835c9e4
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.creq
@@ -0,0 +1,8 @@
+GET
+/
+%E1%88%B4=bar
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.req
new file mode 100644
index 00000000..cc2757e1
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.req
@@ -0,0 +1,3 @@
+GET /?ሴ=bar HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.sreq
new file mode 100644
index 00000000..7baf4c82
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.sreq
@@ -0,0 +1,4 @@
+GET /?ሴ=bar HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=2cdec8eed098649ff3a119c94853b13c643bcf08f8b0a1d91e12c9027818dd04 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.sts
new file mode 100644
index 00000000..51ee71b7
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+eb30c5bed55734080471a834cc727ae56beb50e5f39d1bff6d0d38cb192a7073 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.authz
new file mode 100644
index 00000000..551c0271
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.creq
new file mode 100644
index 00000000..ed91561f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.creq
@@ -0,0 +1,8 @@
+GET
+/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.req
new file mode 100644
index 00000000..0f7a9bfa
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.req
@@ -0,0 +1,3 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sreq
new file mode 100644
index 00000000..d739b01f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sreq
@@ -0,0 +1,4 @@
+GET / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sts
new file mode 100644
index 00000000..b187649c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.authz
new file mode 100644
index 00000000..551c0271
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.creq
new file mode 100644
index 00000000..ed91561f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.creq
@@ -0,0 +1,8 @@
+GET
+/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.req
new file mode 100644
index 00000000..cfd4e8b7
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.req
@@ -0,0 +1,3 @@
+GET /example1/example2/../.. HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.sreq
new file mode 100644
index 00000000..cbdebe2c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.sreq
@@ -0,0 +1,4 @@
+GET /example1/example2/../.. HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.sts
new file mode 100644
index 00000000..b187649c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.authz
new file mode 100644
index 00000000..551c0271
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.creq
new file mode 100644
index 00000000..ed91561f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.creq
@@ -0,0 +1,8 @@
+GET
+/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.req
new file mode 100644
index 00000000..9d6d7ca2
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.req
@@ -0,0 +1,3 @@
+GET /example/.. HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sreq
new file mode 100644
index 00000000..4f59e7d2
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sreq
@@ -0,0 +1,4 @@
+GET /example/.. HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sts
new file mode 100644
index 00000000..b187649c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.authz
new file mode 100644
index 00000000..551c0271
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.creq
new file mode 100644
index 00000000..ed91561f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.creq
@@ -0,0 +1,8 @@
+GET
+/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.req
new file mode 100644
index 00000000..f3537b70
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.req
@@ -0,0 +1,3 @@
+GET /./ HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sreq
new file mode 100644
index 00000000..23a2b41c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sreq
@@ -0,0 +1,4 @@
+GET /./ HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sts
new file mode 100644
index 00000000..b187649c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.authz
new file mode 100644
index 00000000..b76ca1e2
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=ef75d96142cf21edca26f06005da7988e4f8dc83a165a80865db7089db637ec5 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.creq
new file mode 100644
index 00000000..915c57f2
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.creq
@@ -0,0 +1,8 @@
+GET
+/example
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.req
new file mode 100644
index 00000000..3c910717
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.req
@@ -0,0 +1,3 @@
+GET /./example HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sreq
new file mode 100644
index 00000000..80966096
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sreq
@@ -0,0 +1,4 @@
+GET /./example HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=ef75d96142cf21edca26f06005da7988e4f8dc83a165a80865db7089db637ec5 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sts
new file mode 100644
index 00000000..7429923e
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+214d50c111a8edc4819da6a636336472c916b5240f51e9a51b5c3305180cf702 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.authz
new file mode 100644
index 00000000..551c0271
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.creq
new file mode 100644
index 00000000..ed91561f
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.creq
@@ -0,0 +1,8 @@
+GET
+/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.req
new file mode 100644
index 00000000..ede8e3c8
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.req
@@ -0,0 +1,3 @@
+GET // HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sreq
new file mode 100644
index 00000000..cde31b43
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sreq
@@ -0,0 +1,4 @@
+GET // HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sts
new file mode 100644
index 00000000..b187649c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.authz
new file mode 100644
index 00000000..307c1051
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9a624bd73a37c9a373b5312afbebe7a714a789de108f0bdfe846570885f57e84 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.creq
new file mode 100644
index 00000000..2bdaf747
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.creq
@@ -0,0 +1,8 @@
+GET
+/example/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.req
new file mode 100644
index 00000000..a4307ce4
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.req
@@ -0,0 +1,3 @@
+GET //example// HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sreq
new file mode 100644
index 00000000..c84a80d5
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sreq
@@ -0,0 +1,4 @@
+GET //example// HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9a624bd73a37c9a373b5312afbebe7a714a789de108f0bdfe846570885f57e84 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sts
new file mode 100644
index 00000000..95d1fc25
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+cb96b4ac96d501f7c5c15bc6d67b3035061cfced4af6585ad927f7e6c985c015 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.authz
new file mode 100644
index 00000000..832d8a50
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=652487583200325589f1fba4c7e578f72c47cb61beeca81406b39ddec1366741 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.creq
new file mode 100644
index 00000000..124a7096
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.creq
@@ -0,0 +1,8 @@
+GET
+/example%20space/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.req
new file mode 100644
index 00000000..b7d5e8bb
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.req
@@ -0,0 +1,3 @@
+GET /example space/ HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sreq
new file mode 100644
index 00000000..eefa20c4
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sreq
@@ -0,0 +1,4 @@
+GET /example space/ HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=652487583200325589f1fba4c7e578f72c47cb61beeca81406b39ddec1366741 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sts
new file mode 100644
index 00000000..a633f0c0
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+63ee75631ed7234ae61b5f736dfc7754cdccfedbff4b5128a915706ee9390d86 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/normalize-path.txt b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/normalize-path.txt
new file mode 100644
index 00000000..c2fcb272
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/normalize-path/normalize-path.txt
@@ -0,0 +1,3 @@
+A note about signing requests to Amazon S3:
+
+In exception to this, you do not normalize URI paths for requests to Amazon S3. For example, if you have a bucket with an object named my-object//example//photo.user, use that path. Normalizing the path to my-object/example/photo.user will cause the request to fail. For more information, see Task 1: Create a Canonical Request in the Amazon Simple Storage Service API Reference: http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html#canonical-request \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.authz
new file mode 100644
index 00000000..89e572e6
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.creq
new file mode 100644
index 00000000..5c3a9434
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.creq
@@ -0,0 +1,8 @@
+POST
+/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.req
new file mode 100644
index 00000000..3dc41790
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.req
@@ -0,0 +1,3 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.sreq
new file mode 100644
index 00000000..a5ada0d9
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.sreq
@@ -0,0 +1,4 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.sts
new file mode 100644
index 00000000..a6367039
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+553f88c9e4d10fc9e109e2aeb65f030801b70c2f6468faca261d401ae622fc87 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.authz
new file mode 100644
index 00000000..a62589ff
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c5410059b04c1ee005303aed430f6e6645f61f4dc9e1461ec8f8916fdf18852c \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.creq
new file mode 100644
index 00000000..ebe943e8
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.creq
@@ -0,0 +1,9 @@
+POST
+/
+
+host:example.amazonaws.com
+my-header1:value1
+x-amz-date:20150830T123600Z
+
+host;my-header1;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.req
new file mode 100644
index 00000000..0253f194
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.req
@@ -0,0 +1,4 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1:value1
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.sreq
new file mode 100644
index 00000000..b4b78a16
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.sreq
@@ -0,0 +1,5 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1:value1
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c5410059b04c1ee005303aed430f6e6645f61f4dc9e1461ec8f8916fdf18852c \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.sts
new file mode 100644
index 00000000..eb663626
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+9368318c2967cf6de74404b30c65a91e8f6253e0a8659d6d5319f1a812f87d65 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.authz
new file mode 100644
index 00000000..d9e52a37
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=cdbc9802e29d2942e5e10b5bccfdd67c5f22c7c4e8ae67b53629efa58b974b7d \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.creq
new file mode 100644
index 00000000..af824c88
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.creq
@@ -0,0 +1,9 @@
+POST
+/
+
+host:example.amazonaws.com
+my-header1:VALUE1
+x-amz-date:20150830T123600Z
+
+host;my-header1;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.req
new file mode 100644
index 00000000..3f9987af
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.req
@@ -0,0 +1,4 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1:VALUE1
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.sreq
new file mode 100644
index 00000000..99c3210c
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.sreq
@@ -0,0 +1,5 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+My-Header1:VALUE1
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=cdbc9802e29d2942e5e10b5bccfdd67c5f22c7c4e8ae67b53629efa58b974b7d \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.sts
new file mode 100644
index 00000000..40062c79
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+d51ced243e649e3de6ef63afbbdcbca03131a21a7103a1583706a64618606a93 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.authz
new file mode 100644
index 00000000..89e572e6
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.creq
new file mode 100644
index 00000000..5c3a9434
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.creq
@@ -0,0 +1,8 @@
+POST
+/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.req
new file mode 100644
index 00000000..3dc41790
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.req
@@ -0,0 +1,3 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.sreq
new file mode 100644
index 00000000..291ed075
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.sreq
@@ -0,0 +1,5 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+X-Amz-Security-Token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA==
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.sts
new file mode 100644
index 00000000..a6367039
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+553f88c9e4d10fc9e109e2aeb65f030801b70c2f6468faca261d401ae622fc87 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.authz
new file mode 100644
index 00000000..64aa046d
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=85d96828115b5dc0cfc3bd16ad9e210dd772bbebba041836c64533a82be05ead \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.creq
new file mode 100644
index 00000000..1d5a462e
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.creq
@@ -0,0 +1,9 @@
+POST
+/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+x-amz-security-token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA==
+
+host;x-amz-date;x-amz-security-token
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.req
new file mode 100644
index 00000000..9d917755
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.req
@@ -0,0 +1,4 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+X-Amz-Security-Token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.sreq
new file mode 100644
index 00000000..37b2f041
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.sreq
@@ -0,0 +1,5 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+X-Amz-Security-Token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA==
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=85d96828115b5dc0cfc3bd16ad9e210dd772bbebba041836c64533a82be05ead \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.sts
new file mode 100644
index 00000000..bc39ccfc
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+c237e1b440d4c63c32ca95b5b99481081cb7b13c7e40434868e71567c1a882f6 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/readme.txt b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/readme.txt
new file mode 100644
index 00000000..cc342823
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-sts-token/readme.txt
@@ -0,0 +1,15 @@
+A note about using temporary security credentials:
+
+You can use temporary security credentials provided by the AWS Security Token Service (AWS STS) to sign a request. The process is the same as using long-term credentials but requires an additional HTTP header or query string parameter for the security token. The name of the header or query string parameter is X-Amz-Security-Token, and the value is the session token (the string that you received from AWS STS when you obtained temporary security credentials).
+
+When you add X-Amz-Security-Token, some services require that you include this parameter in the canonical (signed) request. For other services, you add this parameter at the end, after you calculate the signature. For details see the API reference documentation for that service.
+
+The test suite has 2 examples:
+
+post-sts-header-before - The X-Amz-Security-Token header is part of the canonical request.
+
+post-sts-header-after - The X-Amz-Security-Token header is added to the request after you calculate the signature.
+
+The test suite uses this example value for X-Amz-Security-Token:
+
+AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.authz
new file mode 100644
index 00000000..44280cd7
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.creq
new file mode 100644
index 00000000..f5058d43
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.creq
@@ -0,0 +1,8 @@
+POST
+/
+Param1=value1
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.req
new file mode 100644
index 00000000..9157bc74
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.req
@@ -0,0 +1,3 @@
+POST /?Param1=value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sreq
new file mode 100644
index 00000000..82af1505
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sreq
@@ -0,0 +1,4 @@
+POST /?Param1=value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sts
new file mode 100644
index 00000000..ca7cc661
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+9d659678c1756bb3113e2ce898845a0a79dbbc57b740555917687f1b3340fbbd \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.authz
new file mode 100644
index 00000000..44280cd7
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.creq
new file mode 100644
index 00000000..f5058d43
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.creq
@@ -0,0 +1,8 @@
+POST
+/
+Param1=value1
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.req
new file mode 100644
index 00000000..9157bc74
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.req
@@ -0,0 +1,3 @@
+POST /?Param1=value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sreq
new file mode 100644
index 00000000..82af1505
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sreq
@@ -0,0 +1,4 @@
+POST /?Param1=value1 HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sts
new file mode 100644
index 00000000..ca7cc661
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+9d659678c1756bb3113e2ce898845a0a79dbbc57b740555917687f1b3340fbbd \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.authz
new file mode 100644
index 00000000..89e572e6
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.creq
new file mode 100644
index 00000000..5c3a9434
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.creq
@@ -0,0 +1,8 @@
+POST
+/
+
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.req
new file mode 100644
index 00000000..3dc41790
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.req
@@ -0,0 +1,3 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sreq
new file mode 100644
index 00000000..a5ada0d9
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sreq
@@ -0,0 +1,4 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sts
new file mode 100644
index 00000000..a6367039
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+553f88c9e4d10fc9e109e2aeb65f030801b70c2f6468faca261d401ae622fc87 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.authz
new file mode 100644
index 00000000..df90ca36
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=ad1ac0c5fa1793b9b9d23894903b9ce84044d014533a633091e12c840473b393 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.creq
new file mode 100644
index 00000000..457b0d55
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.creq
@@ -0,0 +1,9 @@
+POST
+/
+Param1=value1
+content-type:application/x-www-form-urlencoded; charset=utf-8
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+content-type;host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.req
new file mode 100644
index 00000000..36015f10
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.req
@@ -0,0 +1,6 @@
+POST / HTTP/1.1
+Content-Type:application/x-www-form-urlencoded; charset=utf-8
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+
+Param1=value1 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sreq
new file mode 100644
index 00000000..b8ecb899
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sreq
@@ -0,0 +1,7 @@
+POST / HTTP/1.1
+Content-Type:application/x-www-form-urlencoded; charset=utf-8
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=ad1ac0c5fa1793b9b9d23894903b9ce84044d014533a633091e12c840473b393
+
+Param1=value1 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sts
new file mode 100644
index 00000000..f7e615ee
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+5a4a15e664c6705d500b13808c1c0e2e83f6a4748f1110a980051440f5a0d9f9 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.authz b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.authz
new file mode 100644
index 00000000..79077d27
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.authz
@@ -0,0 +1 @@
+AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=0c3bc22986c53164a4755b4466c2e17db853888c5ddb98567e700a3ecdb262f6 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.creq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.creq
new file mode 100644
index 00000000..71dc9817
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.creq
@@ -0,0 +1,9 @@
+POST
+/
+
+content-type:application/x-www-form-urlencoded; charset=utf-8
+host:example.amazonaws.com
+x-amz-date:20150830T123600Z
+
+host;x-amz-date
+e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.req b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.req
new file mode 100644
index 00000000..78526171
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.req
@@ -0,0 +1,4 @@
+POST / HTTP/1.1
+Content-Type:application/x-www-form-urlencoded; charset=utf-8
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sreq b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sreq
new file mode 100644
index 00000000..3e2e5718
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sreq
@@ -0,0 +1,4 @@
+POST / HTTP/1.1
+Host:example.amazonaws.com
+X-Amz-Date:20150830T123600Z
+Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=0c3bc22986c53164a4755b4466c2e17db853888c5ddb98567e700a3ecdb262f6 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sts b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sts
new file mode 100644
index 00000000..89ceae8d
--- /dev/null
+++ b/fluent-bit/tests/internal/data/signv4/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sts
@@ -0,0 +1,4 @@
+AWS4-HMAC-SHA256
+20150830T123600Z
+20150830/us-east-1/service/aws4_request
+6a42edc18395dd67cc71407d4307ff29ab1646b08c1a0107edb319e08e2bc584 \ No newline at end of file
diff --git a/fluent-bit/tests/internal/data/stream_processor/gen_msgpack.sh b/fluent-bit/tests/internal/data/stream_processor/gen_msgpack.sh
new file mode 100755
index 00000000..19339318
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/gen_msgpack.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+echo "=== Hit ctrl-c if it runs forever!... ==="
+echo
+
+# Delete old samples file
+rm -rf samples.mp samples-subkeys.mp
+
+# Generate new msgpack file using as a source samples.json
+../../../../build/bin/fluent-bit -R ../../../../conf/parsers.conf \
+ -i tail -t samples -p path=samples.json -p parser=json \
+ -i tail -t samples-subkeys -p path=samples-subkeys.json -p parser=json \
+ -o file -m samples -p format=msgpack -p file=samples.mp \
+ -o file -m samples-subkeys -p format=msgpack -p file=samples-subkeys.mp -f 1 &
+ pid=$!
+ sleep 2
+ kill -9 $pid
+
+ # Generate new msgpack files for hopping window
+ files=$(find "samples-hw/" -type f -name "*.mp" | sort)
+
+ for file in $files
+ do
+ rm -f $file
+ done
+
+ files=$(find "samples-hw/" -type f -name "*.json" | sort)
+
+ for file in $files
+ do
+ # Generate new msgpack files for hopping window
+ echo ""
+ echo "=== Generating MessagePack file for $file ... ==="
+ ../../../../build/bin/fluent-bit -R ../../../../conf/parsers.conf \
+ -i tail -p path=$file -p parser=json \
+ -o file -p format=msgpack -p file=$(echo $file | sed s/json/mp/) -f 1 &
+ pid=$!
+ sleep 2
+ kill -9 $pid
+ done
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/1.json b/fluent-bit/tests/internal/data/stream_processor/samples-hw/1.json
new file mode 100644
index 00000000..45c8aea2
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/1.json
@@ -0,0 +1,4 @@
+{"id": 0, "word1": "fluent" , "word2": "logging" , "bytes": 10 , "bool": true, "usage": 10}
+{"id": 1, "word1": "fluentd" , "word2": "rlz" , "bytes": 10.0 , "bool": true, "usage": 20}
+{"id": 2, "word1": "fluent-bit" , "word3": "rlz" , "bytes": 10 , "bool": true, "usage": 30}
+{"id": 3, "word1": "fluent-logger" , "word3": "" , "bytes": 10 , "bool": true, "usage": 40}
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/1.mp b/fluent-bit/tests/internal/data/stream_processor/samples-hw/1.mp
new file mode 100644
index 00000000..0ea828bd
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/1.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/2.json b/fluent-bit/tests/internal/data/stream_processor/samples-hw/2.json
new file mode 100644
index 00000000..870a3eb4
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/2.json
@@ -0,0 +1,4 @@
+{"id": 4, "word1": "forward" , "word3": "plain" , "bytes": 10 , "bool": true, "usage": 50}
+{"id": 5, "word5": "forward-protocol", "word6": "secure" , "bytes": 10 , "bool": true, "usage": 60}
+{"id": 6, "word1": "stream" , "word3": "processing" , "bytes": 10.20 , "bool": false, "usage": 70}
+{"id": 7, "word1": "edge-rocks" , "word6": "" , "bytes": 10 , "bool": true, "usage": 80}
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/2.mp b/fluent-bit/tests/internal/data/stream_processor/samples-hw/2.mp
new file mode 100644
index 00000000..a45e1c18
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/2.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/3.json b/fluent-bit/tests/internal/data/stream_processor/samples-hw/3.json
new file mode 100644
index 00000000..f1376be7
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/3.json
@@ -0,0 +1,3 @@
+{"id": 8, "word1": "treasure-data" , "word3": "cncf" , "bytes": 10 , "bool": true, "usage": 90}
+{"id": 9, "word1": "arm" , "word3": "linux foundation", "bytes": "10.30", "bool": false, "usage": 100}
+{"id": 10, "word1": "fluent-bit" , "word3": null , "bytes": 10 , "bool": true, "usage": 110}
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/3.mp b/fluent-bit/tests/internal/data/stream_processor/samples-hw/3.mp
new file mode 100644
index 00000000..1b6cf92a
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/3.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/4.json b/fluent-bit/tests/internal/data/stream_processor/samples-hw/4.json
new file mode 100644
index 00000000..72373944
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/4.json
@@ -0,0 +1,4 @@
+{"id": 11, "word1": "fluent" , "word2": "logging" , "bytes": 10 , "bool": true, "usage": 120}
+{"id": 12, "word1": "fluentd" , "word2": "rlz" , "bytes": 10.0 , "bool": true, "usage": 130}
+{"id": 13, "word1": "fluent-bit" , "word3": "rlz" , "bytes": 10 , "bool": true, "usage": 140}
+{"id": 14, "word1": "fluent-logger" , "word3": "" , "bytes": 10 , "bool": true, "usage": 150}
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/4.mp b/fluent-bit/tests/internal/data/stream_processor/samples-hw/4.mp
new file mode 100644
index 00000000..46716103
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/4.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/5.json b/fluent-bit/tests/internal/data/stream_processor/samples-hw/5.json
new file mode 100644
index 00000000..273820f1
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/5.json
@@ -0,0 +1,4 @@
+{"id": 15, "word1": "forward" , "word3": "plain" , "bytes": 10 , "bool": true, "usage": 160}
+{"id": 16, "word5": "forward-protocol", "word6": "secure" , "bytes": 10 , "bool": true, "usage": 170}
+{"id": 17, "word1": "stream" , "word3": "processing" , "bytes": 10.20 , "bool": false, "usage": 180}
+{"id": 18, "word1": "edge-rocks" , "word6": "" , "bytes": 10 , "bool": true, "usage": 190}
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/5.mp b/fluent-bit/tests/internal/data/stream_processor/samples-hw/5.mp
new file mode 100644
index 00000000..de531bd2
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/5.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/6.json b/fluent-bit/tests/internal/data/stream_processor/samples-hw/6.json
new file mode 100644
index 00000000..14de259b
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/6.json
@@ -0,0 +1,3 @@
+{"id": 19, "word1": "treasure-data" , "word3": "cncf" , "bytes": 10 , "bool": true, "usage": 200}
+{"id": 20, "word1": "arm" , "word3": "linux foundation", "bytes": "10.30", "bool": false, "usage": 210}
+{"id": 21, "word1": "fluent-bit" , "word3": null , "bytes": 10 , "bool": true, "usage": 220}
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/6.mp b/fluent-bit/tests/internal/data/stream_processor/samples-hw/6.mp
new file mode 100644
index 00000000..cfe85fc7
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/6.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/7.json b/fluent-bit/tests/internal/data/stream_processor/samples-hw/7.json
new file mode 100644
index 00000000..c30653ee
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/7.json
@@ -0,0 +1,4 @@
+{"id": 22, "word1": "fluent" , "word2": "logging" , "bytes": 10 , "bool": true, "usage": 230}
+{"id": 23, "word1": "fluentd" , "word2": "rlz" , "bytes": 10.0 , "bool": true, "usage": 240}
+{"id": 24, "word1": "fluent-bit" , "word3": "rlz" , "bytes": 10 , "bool": true, "usage": 250}
+{"id": 25, "word1": "fluent-logger" , "word3": "" , "bytes": 10 , "bool": true, "usage": 260}
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-hw/7.mp b/fluent-bit/tests/internal/data/stream_processor/samples-hw/7.mp
new file mode 100644
index 00000000..19f5ccf5
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-hw/7.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-subkeys.json b/fluent-bit/tests/internal/data/stream_processor/samples-subkeys.json
new file mode 100644
index 00000000..ef4c2ba5
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-subkeys.json
@@ -0,0 +1,9 @@
+{"id": 0, "map": {"sub1": {"sub2": {"color": "blue"}}}}
+{"id": 1, "map": {"sub1": {"sub2": {"color": "red"}, "sub4": "circle"}}}
+{"id": 2, "map": {"sub1": {"sub2": 123}}}
+{"id": 3, "map": {"sub1": {"sub2": "123", "stype": "a"}, "mtype": 0}}
+{"id": 4, "map": {"color": "blue"}}
+{"id": 5, "map": {"sub1": {"sub3": "100", "stype": "a", "sub4": "circle"}, "mtype": 0}}
+{"id": 6, "map": {"sub1": {"sub3": "0.50", "stype": "b", "sub4": "rectangle"}, "mtype": 1}}
+{"id": 7, "map": {"sub1": {"sub3": "5.50", "stype": "a", "sub4": "triangle"}, "mtype": 0}}
+{"id": 8, "map": {"sub1": {"sub3": "10.50", "stype": "b", "sub4": "rectangle"}, "mtype": 2}}
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples-subkeys.mp b/fluent-bit/tests/internal/data/stream_processor/samples-subkeys.mp
new file mode 100644
index 00000000..374e16cd
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples-subkeys.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples.json b/fluent-bit/tests/internal/data/stream_processor/samples.json
new file mode 100644
index 00000000..2cfdef6e
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples.json
@@ -0,0 +1,11 @@
+{"id": 0, "word1": "fluent" , "word2": "logging" , "bytes": 10 , "bool": true, "usage": 10}
+{"id": 1, "word1": "fluentd" , "word2": "rlz" , "bytes": 10.0 , "bool": true, "usage": 20}
+{"id": 2, "word1": "fluent-bit" , "word3": "rlz" , "bytes": 10 , "bool": true, "usage": 30}
+{"id": 3, "word1": "fluent-logger" , "word3": "" , "bytes": 10 , "bool": true, "usage": 40}
+{"id": 4, "word1": "forward" , "word3": "plain" , "bytes": 10 , "bool": true, "usage": 50}
+{"id": 5, "word5": "forward-protocol", "word6": "secure" , "bytes": 10 , "bool": true, "usage": 60}
+{"id": 6, "word1": "stream" , "word3": "processing" , "bytes": 10.20 , "bool": false, "usage": 70}
+{"id": 7, "word1": "edge-rocks" , "word6": "" , "bytes": 10 , "bool": true, "usage": 80}
+{"id": 8, "word1": "treasure-data" , "word3": "cncf" , "bytes": 10 , "bool": true, "usage": 90}
+{"id": 9, "word1": "arm" , "word3": "linux foundation", "bytes": "10.30", "bool": false, "usage": 100}
+{"id": 10, "word1": "fluent-bit" , "word3": null , "bytes": 10 , "bool": true, "usage": 110}
diff --git a/fluent-bit/tests/internal/data/stream_processor/samples.mp b/fluent-bit/tests/internal/data/stream_processor/samples.mp
new file mode 100644
index 00000000..8304065d
--- /dev/null
+++ b/fluent-bit/tests/internal/data/stream_processor/samples.mp
Binary files differ
diff --git a/fluent-bit/tests/internal/data/tls/certificate.pem b/fluent-bit/tests/internal/data/tls/certificate.pem
new file mode 100644
index 00000000..b7971987
--- /dev/null
+++ b/fluent-bit/tests/internal/data/tls/certificate.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDlzCCAn+gAwIBAgIUCl24aoQh3HuyObboivsozKKOb3cwDQYJKoZIhvcNAQEL
+BQAwWzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
+GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLbGVvLnZjYXAubWUw
+HhcNMjIwODE3MTM0OTAyWhcNMjMwODE3MTM0OTAyWjBbMQswCQYDVQQGEwJBVTET
+MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
+dHkgTHRkMRQwEgYDVQQDDAtsZW8udmNhcC5tZTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAJ2cbLtGhSeGPAXKitw+PN7U8CkaSGL7BssPFSuqtuVkJ1AH
+puluHq62FHm4UgTyJB/Iundf1kBxiOehGFr9E1fGp6kUety6iBZK3mmmX0NdXY2A
+ObqfvxTgPNVPEj+5Qp0CJW6fEBRhHrFgpf+OdKzoKnjSCbl1EyJ1wFZeSNDTx98I
+h951xEOCobcXj1qXHDd0snRW5QNKTdSnj+3amqrNqV5DrkCyyb5Rq/VQX9LIyGV8
+tOyq50mUKeK+vOu/oNwfkZqgZ5wlY/AUyPMNvd8UKitnWeQMiMmr3ImQRZLkFnoE
+2mi2Xr6ZQhmsweWbTmmUYxr+ej7L5WvBVQEkbd0CAwEAAaNTMFEwHQYDVR0OBBYE
+FGliHqbvEsRN36C2w1cCq5M/gGCRMB8GA1UdIwQYMBaAFGliHqbvEsRN36C2w1cC
+q5M/gGCRMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACWCNohz
+YbnnDzGXMhGlfV5PywH6+gguqTrtzJlfJB/FGHDMybBvEp9gda5ssPZfPdv6aAvo
+hz2XC+uwJqYn76ZcX+LOixsqhoXJ367pT0ggMSUB/UNvGkzMFWuGerxcB5Q7xvsh
+0Ui37Ojok7Ozj9Ofk/zroJB1WG3kPDtwoo+GwNmJaDtXhOutRPxCqp5Dw+dC0N0c
+TlPCBRB17hx49Zjw+h9sv1VmOUtQcplNZgRL9lguv7eXB6y0sSdpDyDLlTBbRMGl
+uVnQ20uKnqqALRfpXDWFL8g01xPNEIfV3KctpJfu1inLWtPuklerl0yeyUh9M1f0
+zCAi0URsGEHMFA0=
+-----END CERTIFICATE-----
diff --git a/fluent-bit/tests/internal/data/tls/private_key.pem b/fluent-bit/tests/internal/data/tls/private_key.pem
new file mode 100644
index 00000000..b6a70539
--- /dev/null
+++ b/fluent-bit/tests/internal/data/tls/private_key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCdnGy7RoUnhjwF
+yorcPjze1PApGkhi+wbLDxUrqrblZCdQB6bpbh6uthR5uFIE8iQfyLp3X9ZAcYjn
+oRha/RNXxqepFHrcuogWSt5ppl9DXV2NgDm6n78U4DzVTxI/uUKdAiVunxAUYR6x
+YKX/jnSs6Cp40gm5dRMidcBWXkjQ08ffCIfedcRDgqG3F49alxw3dLJ0VuUDSk3U
+p4/t2pqqzaleQ65Assm+Uav1UF/SyMhlfLTsqudJlCnivrzrv6DcH5GaoGecJWPw
+FMjzDb3fFCorZ1nkDIjJq9yJkEWS5BZ6BNpotl6+mUIZrMHlm05plGMa/no+y+Vr
+wVUBJG3dAgMBAAECggEANXQ/AEkLkfsZ0lD+RXIqTNzlUttiH4fJpwbHhFbSzvvn
+xWHC/zpk15ZTXXDhCGJjVBBNBX2QeazH5N8jFoDslYF/jX2vqbrturnLswNFHeDF
+gN8zNRNGyDrBBwtZQhl/+SYoMdtqpa7GrRv9UK4s7hOTjASYXbjSM4bCI8i4Y3JX
+5iKYnejTJ4GNXm1S5g4H8QpgXxrxYU7HY1UlMBGni1BSDPb5SxANnFvgw0oypaDG
+4a0KpnUmziBtZo8wIQ7+UeStS4xikIeaT2SQM/ilplPNx0HgPHbTMeTBHQylG1JU
+ztEE04Y1F9UF7cqZnxqQLlKmIqsOepLeDOkRXjDtNQKBgQDQtWYbewCmGuo1+AR7
+wStOXpfhqdu6NxAwX+S06ZdAV7f4duMFb//6uJ/c997qwRlNvRvpPlJBdcT3qziS
+zjvR8uk2YN5N9D+ZN0Yi11vltUYVPeGpoAk4L7+Ruj5RTVqk5iZEZ1RbAv5cRIm0
+D24H4W0Cj/aldDED2pwTb9ukEwKBgQDBUv/s7gUNM3LCkNLc1EDg2adSzoUopxHU
+jIQCitXt5iFpY2Lv2tpaIz4ux4HQGwhmao2MiUYOMit0++/pFaKur66tgRQRsr7F
+uS1fpjqtS51/5uFuc1xcWjVquidZXH9u9iChrYvim6SBkxzFsbu4kXNOj4I562cm
+E2VWH5GETwKBgQCo5ePX4VbJFYbsXeXi8JRHO63V5Uv4Co+DVlcTQOYyH8q1vCBE
+Sjrxf29/tugjOllr29o2i0StzMy1UU7bHyKx6M5qP0In+71sFJshnv6ziltI3Wc9
+ilFrstho6jt8OAle4RGe0bAmZunJaX22xbXZkshRBognpTv1Tnh4ElHBGQKBgHIW
+nVohjXGg7xTLiuUvjaokSI6hugunrOoWksE9VcqziPw83uJV8Y5IRiYtLvq1OVvX
+ffl1+ZXfHa5ID+kqD3uvyhIynrljFxpwkcpkuzQR77zPcDJSeis2QVfey+H8qGe/
+cLp5RJhS6d5eBxjULshZbgbqwhuURKc/wwn0T1gZAoGAXu2wqwBE8ikhpaDVFYZR
+EZe0G1iq03CtabFL0oPiddytV9jJy4Mg457tkCj58uCVkxw/8qjXEnm5OoJAzSlJ
+Goc4wh1N1rngYRbSGG+LxD0DeI/Hj3T5tWezMBLCE7J+hieUuVjOB9ELKip7aTj3
+KLNVudOATWYGXyvzux6ciu0=
+-----END PRIVATE KEY-----
diff --git a/fluent-bit/tests/internal/env.c b/fluent-bit/tests/internal/env.c
new file mode 100644
index 00000000..c0a72f0a
--- /dev/null
+++ b/fluent-bit/tests/internal/env.c
@@ -0,0 +1,91 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_env.h>
+#include <fluent-bit/flb_sds.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "flb_tests_internal.h"
+
+/* https://github.com/fluent/fluent-bit/issues/6313 */
+void test_translate_long_env()
+{
+ struct flb_env *env;
+ flb_sds_t buf = NULL;
+ char *long_env = "ABC_APPLICATION_TEST_TEST_ABC_FLUENT_BIT_SECRET_FLUENTD_HTTP_HOST";
+ char long_env_ra[4096] = {0};
+ char *env_val = "aaaaa";
+ char putenv_arg[4096] = {0};
+ size_t ret_size;
+ int ret;
+
+ ret_size = snprintf(&long_env_ra[0], sizeof(long_env_ra), "${%s}", long_env);
+ if (!TEST_CHECK(ret_size < sizeof(long_env_ra))) {
+ TEST_MSG("long_env_ra size error");
+ exit(1);
+ }
+ ret_size = snprintf(&putenv_arg[0], sizeof(putenv_arg), "%s=%s", long_env, env_val);
+ if (!TEST_CHECK(ret_size < sizeof(long_env_ra))) {
+ TEST_MSG("putenv_arg size error");
+ exit(1);
+ }
+
+ env = flb_env_create();
+ if (!TEST_CHECK(env != NULL)) {
+ TEST_MSG("flb_env_create failed");
+ exit(1);
+ }
+#ifndef FLB_SYSTEM_WINDOWS
+ ret = putenv(&putenv_arg[0]);
+#else
+ ret = _putenv(&putenv_arg[0]);
+#endif
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("setenv failed");
+ flb_env_destroy(env);
+ exit(1);
+ }
+
+ buf = flb_env_var_translate(env, &long_env_ra[0]);
+ if (!TEST_CHECK(buf != NULL)) {
+ TEST_MSG("flb_env_var_translate failed");
+#ifndef FLB_SYSTEM_WINDOWS
+ unsetenv(long_env);
+#endif
+ flb_env_destroy(env);
+ exit(1);
+ }
+
+ if (!TEST_CHECK(strlen(buf) == strlen(env_val) && 0 == strcmp(buf, env_val))) {
+ TEST_MSG("mismatch. Got=%s expect=%s", buf, env_val);
+ }
+ flb_sds_destroy(buf);
+#ifndef FLB_SYSTEM_WINDOWS
+ unsetenv(long_env);
+#endif
+ flb_env_destroy(env);
+}
+
+
+TEST_LIST = {
+ { "translate_long_env" , test_translate_long_env},
+ { NULL, NULL }
+};
diff --git a/fluent-bit/tests/internal/error_reporter.c b/fluent-bit/tests/internal/error_reporter.c
new file mode 100644
index 00000000..9ab84475
--- /dev/null
+++ b/fluent-bit/tests/internal/error_reporter.c
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <monkey/mk_core/mk_list.h>
+
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/aws/flb_aws_error_reporter.h>
+
+#include "flb_tests_internal.h"
+
+const char* file_path = "/tmp/error.log";
+const char* error_message_1 = "[engine] scheduler could not start";
+const char* error_message_2 = "[engine] scheduler could not stop";
+
+
+void test_flb_aws_error_reporter_create() {
+
+ setenv(STATUS_MESSAGE_FILE_PATH_ENV, file_path, 1);
+ struct flb_aws_error_reporter *error_reporter = flb_aws_error_reporter_create();
+ TEST_CHECK((void*) error_reporter != NULL);
+ TEST_CHECK((void*)error_reporter->file_path != NULL);
+ TEST_CHECK(flb_sds_cmp(error_reporter->file_path, file_path, strlen(file_path)) == 0);
+ TEST_CHECK(error_reporter->ttl == STATUS_MESSAGE_TTL_DEFAULT);
+ TEST_CHECK(error_reporter->max_size == STATUS_MESSAGE_MAX_BYTE_LENGTH_DEFAULT);
+ flb_aws_error_reporter_destroy(error_reporter);
+}
+
+void test_flb_aws_error_reporter_write() {
+
+ int size;
+ char error_message_3[1041];
+ for (int i = 0; i < 1040; i++) {
+ error_message_3[i] = 'a';
+ }
+ error_message_3[1040] = '\0';
+
+ setenv(STATUS_MESSAGE_FILE_PATH_ENV, file_path, 1);
+
+ struct flb_aws_error_reporter *error_reporter = flb_aws_error_reporter_create();
+ flb_aws_error_reporter_write(error_reporter, error_message_1);
+ size = mk_list_size(&error_reporter->messages);
+ TEST_CHECK(size == 1);
+ /* message same with the latest one will be combined*/
+ flb_aws_error_reporter_write(error_reporter, error_message_1);
+ size = mk_list_size(&error_reporter->messages);
+ TEST_CHECK(size == 1);
+
+ /* message different of the latest one will be combined*/
+ flb_aws_error_reporter_write(error_reporter, error_message_2);
+ size = mk_list_size(&error_reporter->messages);
+ TEST_CHECK(size == 2);
+
+ /* message larger than max size limit*/
+ flb_aws_error_reporter_write(error_reporter, error_message_3);
+ size = mk_list_size(&error_reporter->messages);
+ TEST_CHECK(size == 1);
+
+ flb_aws_error_reporter_destroy(error_reporter);
+}
+
+void test_flb_aws_error_reporter_clean() {
+
+ setenv(STATUS_MESSAGE_FILE_PATH_ENV, file_path, 1);
+ struct flb_aws_error_reporter *error_reporter = flb_aws_error_reporter_create();
+ flb_aws_error_reporter_write(error_reporter, error_message_1);
+ time_t start = time(NULL);
+ while (time(NULL) - start <= error_reporter->ttl) {
+ flb_aws_error_reporter_clean(error_reporter);
+ }
+ TEST_CHECK(mk_list_size(&error_reporter->messages) == 0);
+
+ flb_aws_error_reporter_destroy(error_reporter);
+}
+
+TEST_LIST = {
+ { "test_flb_aws_error_reporter_create", test_flb_aws_error_reporter_create},
+ {"test_flb_aws_error_reporter_write", test_flb_aws_error_reporter_write},
+ {"test_flb_aws_error_reporter_clean", test_flb_aws_error_reporter_clean},
+ { 0 }
+}; \ No newline at end of file
diff --git a/fluent-bit/tests/internal/file.c b/fluent-bit/tests/internal/file.c
new file mode 100644
index 00000000..974c2668
--- /dev/null
+++ b/fluent-bit/tests/internal/file.c
@@ -0,0 +1,48 @@
+#include <fluent-bit/flb_file.h>
+#include <fluent-bit/flb_sds.h>
+#include <string.h>
+
+#include "flb_tests_internal.h"
+#include "fluent-bit/stream_processor/flb_sp.h"
+
+#define TEXT_FILE FLB_TESTS_DATA_PATH "/data/file/text_file.txt"
+#define EMPTY_FILE FLB_TESTS_DATA_PATH "/data/file/empty_file.txt"
+
+static void check_equals(flb_sds_t result, const char *expected)
+{
+ size_t expected_len = strlen(expected);
+ size_t result_len = flb_sds_len(result);
+ TEST_CHECK(expected_len == result_len);
+ TEST_MSG("Expected length: %zu", expected_len);
+ TEST_MSG("Actual length: %zu", result_len);
+ TEST_CHECK(memcmp(result, expected, expected_len) == 0);
+ TEST_MSG("Expected: %s", expected);
+ TEST_MSG("Actual: %s", result);
+}
+
+static void test_file_read_text_file()
+{
+ flb_sds_t result = flb_file_read(TEXT_FILE);
+ check_equals(result, "Some text file\n\nline 3\n\nline 5\n");
+ flb_sds_destroy(result);
+}
+
+static void test_file_read_empty_file()
+{
+ flb_sds_t result = flb_file_read(EMPTY_FILE);
+ check_equals(result, "");
+ flb_sds_destroy(result);
+}
+
+static void test_file_read_missing()
+{
+ flb_sds_t result = flb_file_read(TEXT_FILE ".missing");
+ TEST_CHECK(result == NULL);
+}
+
+TEST_LIST = {
+ { "file_read_text_file" , test_file_read_text_file},
+ { "file_read_empty_file" , test_file_read_empty_file},
+ { "file_read_missing" , test_file_read_missing},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/flb_event_loop.c b/fluent-bit/tests/internal/flb_event_loop.c
new file mode 100644
index 00000000..669475d7
--- /dev/null
+++ b/fluent-bit/tests/internal/flb_event_loop.c
@@ -0,0 +1,592 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_mp.h>
+#include <msgpack.h>
+
+#include "flb_tests_internal.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fluent-bit/flb_event_loop.h>
+#include <fluent-bit/flb_bucket_queue.h>
+#include <monkey/mk_core/mk_list.h>
+
+#define EVENT_LOOP_TEST_PRIORITIES 7
+#define EVENT_LOOP_MAX_EVENTS 64
+#ifdef _WIN32
+ #define TIME_EPSILON_MS 30
+#elif FLB_SYSTEM_MACOS
+ #define TIME_EPSILON_MS 200
+#else
+ #define TIME_EPSILON_MS 10
+#endif
+
+#define TIMER_COARSE_EPSION_MS 300
+
+struct test_evl_context {
+ struct mk_event_loop *evl;
+ struct flb_bucket_queue *bktq;
+};
+
+/*
+ * The following implements a uniform custom delayed event
+ * primarily to help Windows. It is needed to simulate
+ * another thread or network call writing to fd
+ * activating an event without disrupting the event (libevent)
+ * loop. LibEvent timeouts disrupt the libevent loop
+ * making the testcases non-homogeneous between platforms
+ */
+struct delay_worker_args {
+ int fd; /* pipefd */
+ int sec; /* seconds */
+};
+
+/* Writes to pipe after set delay. Cleans self up after pipe closure */
+void _delay_worker(void *arg) {
+ int ret;
+ uint64_t val = 1;
+ char tmp[100];
+
+ struct delay_worker_args *args = (struct delay_worker_args *) arg;
+ static int idx = 0;
+
+ sprintf(tmp, "delay-timer-%i", ++idx);
+ mk_utils_worker_rename(tmp);
+
+ /* Sleep for the delay period */
+ sleep(args->sec);
+
+ /* Send delayed event a notification */
+ flb_pipe_set_nonblocking(args->fd);
+ ret = flb_pipe_w(args->fd, &val, sizeof(uint64_t)); /* supposedly blocking */
+ if (ret == -1) {
+ flb_error("Delayed event: unable to trigger event via write to pipe");
+ }
+
+ /* Clean up */
+ flb_pipe_close(args->fd);
+ flb_free(args);
+}
+
+void test_timeout_create(struct mk_event_loop *loop,
+ time_t sec, long nsec, void *data)
+{
+ flb_pipefd_t fd[2];
+ int ret;
+ pthread_t tid;
+
+ ret = flb_pipe_create(fd);
+ if (ret == -1) {
+ flb_error("pipe creation failure");
+ return;
+ }
+
+ /*
+ * Spin up another thread to keep
+ * track of our delay (don't let register in event loop (like libevent))
+ */
+ struct delay_worker_args *args = flb_malloc(sizeof(struct delay_worker_args));
+
+ /* Register write end of data pipe */
+ args->fd = fd[1];
+ args->sec = sec;
+ ret = mk_utils_worker_spawn(_delay_worker, args, &tid); /* worker handles freeing up args. */
+
+ /* Convert read end to monkey event */
+ MK_EVENT_NEW(data);
+ mk_event_add(loop, fd[0], MK_EVENT_NOTIFICATION, MK_EVENT_READ, data);
+}
+
+void test_timeout_destroy(struct mk_event_loop *loop, void *data)
+{
+ struct mk_event *event = (struct mk_event *) data;
+ mk_event_del(loop, event);
+ flb_pipe_close(event->fd);
+}
+
+struct test_evl_context *evl_context_create()
+{
+ struct test_evl_context *ctx = flb_malloc(sizeof(struct test_evl_context));
+ ctx->evl = mk_event_loop_create(EVENT_LOOP_MAX_EVENTS);
+ ctx->bktq = flb_bucket_queue_create(EVENT_LOOP_TEST_PRIORITIES);
+ return ctx;
+}
+
+void evl_context_destroy(struct test_evl_context *ctx)
+{
+ flb_bucket_queue_destroy(ctx->bktq);
+ mk_event_loop_destroy(ctx->evl);
+ flb_free(ctx);
+}
+
+void test_simple_timeout_1000ms()
+{
+ struct test_evl_context *ctx;
+
+ struct flb_time start_time;
+ struct flb_time end_time;
+ struct flb_time diff_time;
+ uint64_t elapsed_time_flb;
+ int target;
+
+#ifdef _WIN32
+ WSADATA wsa_data;
+ WSAStartup(0x0201, &wsa_data);
+#endif
+
+ target = 1000;
+ ctx = evl_context_create();
+
+ flb_time_get(&start_time);
+
+ mk_event_wait_2(ctx->evl, target);
+
+ flb_time_get(&end_time);
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+ TEST_CHECK(elapsed_time_flb < target + TIME_EPSILON_MS
+ && elapsed_time_flb > target - TIME_EPSILON_MS);
+ TEST_MSG("Target time failed for mk_wait_2. Expect %d ms. Waited %d ms\n", target,
+ (int) elapsed_time_flb);
+
+ evl_context_destroy(ctx);
+
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+/*
+ * Non-block wait: 0ms, no event
+ * Add timer - 1s (very inexact)
+ * Non-block wait: 0ms, no event
+ * Blocking wait with 2.1s timeout: ~1s (very inexact), 1event
+ * Non-blocking wait: 0ms, 1 event
+ * Remove timer event
+ * Blocking wait with 2.1s timeout: 2.1s, no event
+ */
+void test_non_blocking_and_blocking_timeout()
+{
+ struct test_evl_context *ctx;
+
+ struct mk_event event = {0};
+
+ struct flb_time start_time;
+ struct flb_time end_time;
+ struct flb_time diff_time;
+ uint64_t elapsed_time_flb;
+ int n_events;
+
+ int target;
+ int wait_2_timeout = 2100;
+#ifdef _WIN32
+ WSADATA wsa_data;
+ WSAStartup(0x0201, &wsa_data);
+#endif
+
+ ctx = evl_context_create();
+
+ /* Non blocking wait -- no event */
+ target = 0;
+ flb_time_get(&start_time);
+ n_events = mk_event_wait_2(ctx->evl, 0);
+ flb_time_get(&end_time);
+
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+ TEST_CHECK(elapsed_time_flb <= target + TIME_EPSILON_MS);
+ TEST_MSG("Target time failed for mk_wait_2. Expect %d ms. Waited %d ms\n", target,
+ (int) elapsed_time_flb);
+ TEST_CHECK(n_events == 0);
+
+ /* Add somewhat inexact 1 second timer */
+ target = 1000;
+ event.mask = MK_EVENT_EMPTY;
+ event.status = MK_EVENT_NONE;
+ test_timeout_create(ctx->evl, target / 1000, 0, &event);
+
+ /* Non blocking wait -- one event */
+ target = 0;
+ flb_time_get(&start_time);
+ n_events = mk_event_wait_2(ctx->evl, 0);
+ flb_time_get(&end_time);
+
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+ TEST_CHECK(elapsed_time_flb <= target + TIME_EPSILON_MS);
+ TEST_MSG("Target time failed for mk_wait_2. Expect %d ms. Waited %d ms\n", target,
+ (int) elapsed_time_flb);
+ TEST_CHECK(n_events == 0);
+
+ /* Blocking wait with unused timeout */
+ target = 1000;
+ flb_time_get(&start_time);
+ n_events = mk_event_wait_2(ctx->evl, wait_2_timeout);
+ flb_time_get(&end_time);
+
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+ TEST_CHECK(elapsed_time_flb < target + TIMER_COARSE_EPSION_MS
+ && elapsed_time_flb > 100); /* accommodate for timer inaccuracy */
+ TEST_MSG("Target time failed for mk_wait_2. Expect %d ms. Waited %d ms\n", target,
+ (int) elapsed_time_flb);
+ TEST_CHECK(n_events == 1);
+
+ /* Non blocking wait -- one event */
+ target = 0;
+ flb_time_get(&start_time);
+ n_events = mk_event_wait_2(ctx->evl, 0);
+ flb_time_get(&end_time);
+
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+ TEST_CHECK(elapsed_time_flb <= target + TIME_EPSILON_MS);
+ TEST_MSG("Target time failed for mk_wait_2. Expect %d ms. Waited %d ms\n", target,
+ (int) elapsed_time_flb);
+ TEST_CHECK(n_events == 1);
+
+ /* Remove triggered 1s timer event */
+ test_timeout_destroy(ctx->evl, &event);
+
+ /* Blocking wait, used timeout */
+ target = wait_2_timeout;
+ flb_time_get(&start_time);
+ n_events = mk_event_wait_2(ctx->evl, wait_2_timeout);
+ flb_time_get(&end_time);
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+ TEST_CHECK(elapsed_time_flb < target + TIME_EPSILON_MS
+ && elapsed_time_flb > target - TIME_EPSILON_MS);
+ TEST_MSG("Target time failed for mk_wait_2. Expect %d ms. Waited %d ms\n", target,
+ (int) elapsed_time_flb);
+ TEST_CHECK(n_events == 0);
+
+ evl_context_destroy(ctx);
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+/*
+ * Add 1s timer
+ * Infinite wait: 1 event, < 1s + epsilon
+ * Remove timer
+ */
+void test_infinite_wait()
+{
+ struct test_evl_context *ctx;
+
+ struct mk_event event = {0};
+
+ struct flb_time start_time;
+ struct flb_time end_time;
+ struct flb_time diff_time;
+ uint64_t elapsed_time_flb;
+ int n_events;
+
+ int target;
+#ifdef _WIN32
+ WSADATA wsa_data;
+ WSAStartup(0x0201, &wsa_data);
+#endif
+
+ ctx = evl_context_create();
+
+ /* Add somewhat inexact 1 second timer */
+ target = 1000;
+ test_timeout_create(ctx->evl, target / 1000, 0, &event);
+
+ /* Infinite wait -- 1 event */
+ target = 1000;
+ flb_time_get(&start_time);
+ n_events = mk_event_wait(ctx->evl);
+ flb_time_get(&end_time);
+
+ flb_time_diff(&end_time, &start_time, &diff_time);
+ elapsed_time_flb = flb_time_to_nanosec(&diff_time) / 1000000;
+ TEST_CHECK(elapsed_time_flb < target + TIMER_COARSE_EPSION_MS
+ && elapsed_time_flb > 100); /* expect timer to be inexact */
+ TEST_MSG("Target time failed for mk_wait_2. Expect %d ms. Waited %d ms\n", target,
+ (int) elapsed_time_flb);
+ TEST_CHECK(n_events == 1);
+
+ /* Remove triggered 1s timer event */
+ test_timeout_destroy(ctx->evl, &event);
+
+ evl_context_destroy(ctx);
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+void synchronize_tests()
+{
+ test_non_blocking_and_blocking_timeout();
+ test_infinite_wait();
+}
+
+/*
+ * Add non-delayed and delayed timers of varying priority to priority event loop, and
+ * verify timers are processed by order of priority and order of activation. Delete also
+ * checked by deleting events in several cases and confirming deleted events are not
+ * processed.
+ *
+ * Method:
+ * Add n_timers / 2 non-delayed timers
+ * delete 1/4th of the non-delayed timers
+ * Wait for non_delayed timers to activate
+ * delete 1/4th of the non-delayed timers
+ * Process events with mk_event_loop deleting each processed event
+ * Add n_timers / 2 non-delayed timers
+ * Add n_timers / 2 2s delayed timers
+ * Wait for non_delayed timers to activate
+ * Start looping though processing the events
+ * on the first iteration, (after initial events are tracked)
+ * Delete 1/2 of the non-delayed timers
+ * Wait for the delayed timers to activate
+ * Check that deleted events are not processed
+ * Check that non-delayed timers which are tracked first are processed before
+ * non-delayed events.
+ *
+ * Summary:
+ * Track priorities and confirm that all added events were processed
+ * Verify non-delayed timers are triggered before delayed timers
+ * Confirm delete properly deletes events before ready, after ready,
+ * and after tracked by event loop.
+ */
+void event_loop_stress_priority_add_delete()
+{
+ struct test_evl_context *ctx;
+
+ const int n_timers = EVENT_LOOP_MAX_EVENTS;
+ struct mk_event events[EVENT_LOOP_MAX_EVENTS] = {0};
+ struct mk_event *event_cronology[EVENT_LOOP_TEST_PRIORITIES] = {0}; /* event loop priority fifo */
+ int priority_cronology = 0;
+
+ struct mk_event *event;
+ int immediate_timers[EVENT_LOOP_TEST_PRIORITIES] = {0}; /* by priority */
+ int delayed_timers[EVENT_LOOP_TEST_PRIORITIES] = {0}; /* by priority */
+
+ int immediate_timers_triggered[EVENT_LOOP_TEST_PRIORITIES] = {0};
+ int delayed_timers_triggered[EVENT_LOOP_TEST_PRIORITIES] = {0};
+
+ int priority;
+ int n_events;
+ int target;
+
+ int i;
+ int j;
+ int ret = 0;
+ int immediate_timer_count;
+#ifdef _WIN32
+ WSADATA wsa_data;
+ WSAStartup(0x0201, &wsa_data);
+#endif
+
+ ctx = evl_context_create();
+ srand(20);
+
+ /* Add timers with no delay */
+ for (i = 0; i < n_timers / 2; ++i) {
+ priority = rand() % EVENT_LOOP_TEST_PRIORITIES;
+ target = 0;
+ memset(&events[i], 0, sizeof(struct mk_event));
+ test_timeout_create(ctx->evl, 0, 0, &events[i]);
+ events[i].priority = priority;
+ ++immediate_timers[priority];
+ }
+
+ usleep(400000); /* sleep 400 milliseconds for the 0delay events to register */
+
+ /* Remove the first n/8 events */
+ for (i = 0; i < n_timers / 8; ++i) {
+ test_timeout_destroy(ctx->evl, &events[i]);
+ --immediate_timers[(int) events[i].priority];
+ }
+
+ /* Wait on the no delay timers */
+ n_events = mk_event_wait(ctx->evl);
+ TEST_CHECK(n_events == n_timers / 2 - n_timers / 8);
+ TEST_MSG("Expected %i ready events from the no delay timers. Recieved %i",
+ n_timers / 2 - n_timers / 8, ret);
+
+ /* Remove the first n/8 events */
+ for (i = n_timers / 8; i < n_timers / 4; ++i) {
+ test_timeout_destroy(ctx->evl, &events[i]);
+ --immediate_timers[(int) events[i].priority];
+ }
+
+ i = 0;
+ do { /* variable closure */
+ flb_event_priority_live_foreach(event, ctx->bktq, ctx->evl, n_timers) {
+ /* check priority cronology */
+ TEST_CHECK(event->priority >= priority_cronology);
+ TEST_MSG("Priority event loop processed events out of order.");
+ priority_cronology = event->priority;
+
+ /* check none of the deleted records appear */
+ TEST_CHECK(event >= &events[n_timers / 4]);
+ TEST_MSG("Deleted event appeared in priority event loop.");
+
+ /* update records */
+
+ /* delete event */
+ test_timeout_destroy(ctx->evl, event);
+
+ /* update records */
+ test_timeout_destroy(ctx->evl, event);
+ if (event < &events[n_timers/2]) {
+ /* immediate timer */
+ --immediate_timers[(int) event->priority];
+ ++immediate_timers_triggered[(int) event->priority];
+ }
+ else {
+ /* delayed timer */
+ --delayed_timers[(int) event->priority];
+ ++delayed_timers_triggered[(int) event->priority];
+ }
+ ++i;
+ }
+ } while (0);
+ TEST_CHECK(i == n_timers / 4);
+ TEST_MSG("Not all no-wait timers activated");
+
+ /* verify number of immediate timers triggered */
+ for (i = 0; i < EVENT_LOOP_TEST_PRIORITIES; ++i) {
+ TEST_CHECK(immediate_timers[i] == 0);
+ TEST_MSG("Priority event register and triggered mismatch for priority %i. "
+ "Remaining: %i out of: %i", i, immediate_timers[i],
+ immediate_timers_triggered[i]);
+ }
+
+ /* Re-add timers with no delay */
+ for (i = 0; i < n_timers / 2; ++i) {
+ priority = rand() % EVENT_LOOP_TEST_PRIORITIES;
+ target = 0;
+ memset(&events[i], 0, sizeof(struct mk_event));
+ test_timeout_create(ctx->evl, target, 0, &events[i]);
+ events[i].priority = priority;
+ ++immediate_timers[priority];
+ }
+
+ usleep(400000); /* sleep 200 milliseconds for the 0delay events to register */
+
+ /* Add timers with delay */
+ for (i = n_timers / 2; i < n_timers; ++i) {
+ priority = rand() % EVENT_LOOP_TEST_PRIORITIES;
+ target = 2; /* 2 second delay */
+ memset(&events[i], 0, sizeof(struct mk_event));
+ test_timeout_create(ctx->evl, target, 0, &events[i]);
+ events[i].priority = priority;
+ ++delayed_timers[priority];
+ }
+
+ /* Wait on the timers */
+ n_events = mk_event_wait(ctx->evl);
+ TEST_CHECK(n_events == n_timers / 2);
+ TEST_MSG("Expected %i ready events from the no delay timers. Recieved %i", n_timers / 2, ret);
+ j = 0;
+ priority_cronology = 0;
+ do { /* variable closure */
+ flb_event_priority_live_foreach(event, ctx->bktq, ctx->evl, n_timers) {
+
+ /* first round, delete half of all 0delay timers */
+ if (j == 0) {
+
+ /* this tests propper removal from bucket queue */
+ for (i = 0; i < n_timers/4; ++i) {
+ if (&events[i] == event) {
+ continue;
+ }
+ test_timeout_destroy(ctx->evl, &events[i]);
+ --immediate_timers[(int) events[i].priority];
+ }
+
+ /* check priority cronology */
+ TEST_CHECK(event->priority >= priority_cronology);
+ priority_cronology = event->priority;
+
+ /* delete actual event */
+ test_timeout_destroy(ctx->evl, event);
+ --immediate_timers[(int) event->priority];
+ TEST_CHECK(event < &events[n_timers/2]);
+ TEST_MSG("Processed delayed timer first. Should process immediate timer first");
+
+ /* delay for the delayed timers to register */
+ usleep(2500000); /* 2.5 seconds */
+ ++j;
+ continue;
+ }
+
+ /* validate fifo nature. inspect cross from no-timeout to timeout event */
+ /* check event fifo cronology */
+ /* (priority A) [immediate timer] -> [delay timer]: check all immediate timers processed */
+ if (event_cronology[(int) event->priority] < &events[n_timers / 2]
+ && event >= &events[n_timers / 2]) {
+ /* verify that all of the immediate_timers in priority have been removed */
+ immediate_timer_count = 0;
+ for (i = 0; i < EVENT_LOOP_TEST_PRIORITIES; ++i) {
+ immediate_timer_count += immediate_timers[i];
+ }
+ TEST_CHECK(immediate_timers[(int) event->priority] == 0);
+ TEST_MSG("immediate timer events are not all processed before delayed timer events for priority %i", event->priority);
+ }
+ /* check for non fifo behavior */
+ /* (priority A) [delay timer] -> [immediate timer]: disallow */
+ if (!TEST_CHECK(!(event_cronology[(int) event->priority] >= &events[n_timers / 2]
+ && event < &events[n_timers / 2]))) {
+ TEST_MSG("Non fifo behavior within priority. Delayed event processed before immediate event.");
+ }
+ event_cronology[(int) event->priority] = event;
+
+ /* check priority cronology */
+ TEST_CHECK(event->priority >= priority_cronology);
+ TEST_MSG("Priority event loop processed events out of order.");
+ priority_cronology = event->priority;
+
+ /* verify none of the deleted timers are processed */
+ TEST_CHECK(event >= &events[n_timers / 4]);
+ TEST_MSG("Processed a deleted timer. Delete performed after event is registered in the event loop bucket queue.");
+
+ /* update records */
+ test_timeout_destroy(ctx->evl, event);
+ if (event < &events[n_timers/2]) {
+ /* immediate timer */
+ --immediate_timers[(int) event->priority];
+ }
+ else {
+ /* delayed timer */
+ --delayed_timers[(int) event->priority];
+ }
+ ++j;
+ }
+ } while (0);
+
+ /* validate all timers processed */
+ for (i = 0; i < EVENT_LOOP_TEST_PRIORITIES; ++i) {
+ TEST_CHECK(immediate_timers[i] == 0);
+ TEST_MSG("Not all immediate timers processed");
+ }
+ for (i = 0; i < EVENT_LOOP_TEST_PRIORITIES; ++i) {
+ TEST_CHECK(delayed_timers[i] == 0);
+ TEST_MSG("Not all delayed timers processed");
+ }
+
+ evl_context_destroy(ctx);
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+TEST_LIST = {
+ {"test_simple_timeout_1000ms", test_simple_timeout_1000ms},
+ {"test_non_blocking_and_blocking_timeout", test_non_blocking_and_blocking_timeout},
+ {"test_infinite_wait", test_infinite_wait},
+ {"event_loop_stress_priority_add_delete", event_loop_stress_priority_add_delete},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/flb_tests_internal.h.in b/fluent-bit/tests/internal/flb_tests_internal.h.in
new file mode 100644
index 00000000..82a8cd8c
--- /dev/null
+++ b/fluent-bit/tests/internal/flb_tests_internal.h.in
@@ -0,0 +1,43 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2020 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FLB_TEST_INTERNAL_H
+#define FLB_TEST_INTERNAL_H
+
+#ifdef FLB_TEST_INIT_NOP
+static inline void init_nop() {};
+#define TEST_INIT { init_nop(); }
+#else
+#include "../include/flb_tests_initialize_tls.h"
+#define TEST_INIT { flb_test_env_config_init(); }
+#endif
+
+#ifdef FLB_TEST_FINI_NOP
+static inline void fini_nop() {};
+#define TEST_FINI { fini_nop(); }
+#else
+#include "../include/flb_tests_initialize_tls.h"
+#define TEST_FINI { flb_test_env_config_destroy(); }
+#endif
+
+#include "../lib/acutest/acutest.h"
+#define FLB_TESTS_DATA_PATH "@FLB_TESTS_DATA_PATH@"
+
+#endif
diff --git a/fluent-bit/tests/internal/flb_time.c b/fluent-bit/tests/internal/flb_time.c
new file mode 100644
index 00000000..a4e2980b
--- /dev/null
+++ b/fluent-bit/tests/internal/flb_time.c
@@ -0,0 +1,335 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_pack.h>
+#include <mpack/mpack.h>
+#include <msgpack.h>
+#include <msgpack/timestamp.h>
+#include "flb_tests_internal.h"
+
+#define SEC_32BIT 1647061992 /* 0x622c2be8 */
+#define NSEC_32BIT 123000000 /* 123ms 0x0754d4c0 */
+#define D_SEC 1647061992.123;
+const char eventtime[8] = {0x62, 0x2c, 0x2b, 0xe8, 0x07, 0x54, 0xd4, 0xc0 };
+
+void test_to_nanosec()
+{
+ uint64_t expect = 123000000456;
+ uint64_t ret;
+ struct flb_time tm;
+
+ flb_time_set(&tm, 123, 456);
+
+ ret = flb_time_to_nanosec(&tm);
+ if (!TEST_CHECK(ret == expect)) {
+ TEST_MSG("given =%" PRIu64, ret);
+ TEST_MSG("expect =%" PRIu64, expect);
+ }
+}
+
+/* https://github.com/fluent/fluent-bit/issues/5215 */
+void test_append_to_mpack_v1() {
+ mpack_writer_t writer;
+ char *data;
+ size_t size;
+ struct flb_time tm;
+ int ret;
+
+ msgpack_zone mempool;
+ msgpack_object ret_obj;
+ size_t off = 0;
+
+ flb_time_set(&tm, 123, 456);
+ mpack_writer_init_growable(&writer, &data, &size);
+
+ ret = flb_time_append_to_mpack(&writer, &tm, FLB_TIME_ETFMT_V1_FIXEXT);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_time_append_to_mpack failed");
+ mpack_writer_destroy(&writer);
+ flb_free(data);
+ exit(EXIT_FAILURE);
+ }
+ mpack_writer_destroy(&writer);
+
+ msgpack_zone_init(&mempool, 1024);
+ ret = msgpack_unpack(data, size, &off, &mempool, &ret_obj);
+ if (!TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS)) {
+ TEST_MSG("unpack failed ret = %d", ret);
+ msgpack_zone_destroy(&mempool);
+ flb_free(data);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(ret_obj.type == MSGPACK_OBJECT_EXT)) {
+ TEST_MSG("data type is not ext. type=%d", ret_obj.type);
+ msgpack_zone_destroy(&mempool);
+ flb_free(data);
+ exit(EXIT_FAILURE);
+ }
+ if (!TEST_CHECK(ret_obj.via.ext.type == 0)) {
+ TEST_MSG("ext type is not 0. ext type=%d", ret_obj.via.ext.type);
+ msgpack_zone_destroy(&mempool);
+ flb_free(data);
+ exit(EXIT_FAILURE);
+ }
+ msgpack_zone_destroy(&mempool);
+ flb_free(data);
+}
+
+void test_msgpack_to_time_int()
+{
+ struct flb_time tm;
+ int64_t expect = SEC_32BIT;
+ int ret;
+
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+ msgpack_unpacked result;
+
+ msgpack_object tm_obj;
+
+ /* create int object*/
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+ msgpack_pack_int(&mp_pck, expect);
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, mp_sbuf.data, mp_sbuf.size, NULL);
+
+ tm_obj = result.data;
+ ret = flb_time_msgpack_to_time(&tm, &tm_obj);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_time_msgpack_to_time failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(tm.tm.tv_sec == expect && tm.tm.tv_nsec == 0)) {
+ TEST_MSG("got %ld.%ld, expect %ld.%d", tm.tm.tv_sec, tm.tm.tv_nsec, expect, 0);
+ }
+
+ msgpack_sbuffer_destroy(&mp_sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void test_msgpack_to_time_double()
+{
+ struct flb_time tm;
+ double d_time = D_SEC;
+ int64_t expect_sec = SEC_32BIT;
+ int64_t expect_nsec = NSEC_32BIT;
+
+ int ret;
+
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+ msgpack_unpacked result;
+
+ msgpack_object tm_obj;
+
+ /* create int object*/
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+ msgpack_pack_double(&mp_pck, d_time);
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, mp_sbuf.data, mp_sbuf.size, NULL);
+
+ tm_obj = result.data;
+ ret = flb_time_msgpack_to_time(&tm, &tm_obj);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_time_msgpack_to_time failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(tm.tm.tv_sec == expect_sec &&
+ llabs(tm.tm.tv_nsec - expect_nsec ) < 10000 /* 10us*/)) {
+ TEST_MSG("got %ld.%ld, expect %ld.%ld", tm.tm.tv_sec, tm.tm.tv_nsec, expect_sec, expect_nsec);
+ }
+
+ msgpack_sbuffer_destroy(&mp_sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void test_msgpack_to_time_eventtime()
+{
+ struct flb_time tm;
+ int64_t expect_sec = SEC_32BIT;
+ int64_t expect_nsec = NSEC_32BIT;
+ char ext_data[8] = {0};
+ int ret;
+
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+ msgpack_unpacked result;
+
+ msgpack_object tm_obj;
+
+ memcpy(&ext_data[0], &eventtime[0], 8);
+
+ /* create int object*/
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ /* https://github.com/fluent/fluentd/wiki/Forward-Protocol-Specification-v1#eventtime-ext-format */
+ msgpack_pack_ext(&mp_pck, 8/*fixext8*/, 0);
+ msgpack_pack_ext_body(&mp_pck, ext_data, sizeof(ext_data));
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, mp_sbuf.data, mp_sbuf.size, NULL);
+
+ tm_obj = result.data;
+ ret = flb_time_msgpack_to_time(&tm, &tm_obj);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_time_msgpack_to_time failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(tm.tm.tv_sec == expect_sec &&
+ llabs(tm.tm.tv_nsec - expect_nsec ) < 10000 /* 10us*/)) {
+ TEST_MSG("got %ld.%ld, expect %ld.%ld", tm.tm.tv_sec, tm.tm.tv_nsec, expect_sec, expect_nsec);
+ }
+
+ msgpack_sbuffer_destroy(&mp_sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void test_msgpack_to_time_invalid()
+{
+ struct flb_time tm;
+ char ext_data[8] = {0x00, 0x11, 0x22, 0xaa, 0xbb, 0xcc, 0xdd, 0xee};
+ int ret;
+
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+ msgpack_unpacked result;
+
+
+ msgpack_object tm_obj;
+
+ /* create int object*/
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_ext(&mp_pck, 5 /* invalid size */, 0);
+ msgpack_pack_ext_body(&mp_pck, ext_data, 5);
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, mp_sbuf.data, mp_sbuf.size, NULL);
+
+ tm_obj = result.data;
+
+ /* Check if ext */
+ TEST_CHECK(tm_obj.type == MSGPACK_OBJECT_EXT);
+ TEST_CHECK(tm_obj.via.ext.type == 0);
+ TEST_CHECK(tm_obj.via.ext.size == 5);
+
+ ret = flb_time_msgpack_to_time(&tm, &tm_obj);
+ if(!TEST_CHECK(ret != 0)) {
+ TEST_MSG("flb_time_msgpack_to_time should fail");
+ exit(EXIT_FAILURE);
+ }
+
+ msgpack_sbuffer_destroy(&mp_sbuf);
+ msgpack_unpacked_destroy(&result);
+
+
+ /* create int object*/
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_ext(&mp_pck, 8, 10 /* invalid type */);
+ msgpack_pack_ext_body(&mp_pck, ext_data, 8);
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, mp_sbuf.data, mp_sbuf.size, NULL);
+
+ tm_obj = result.data;
+
+ /* Check if ext */
+ TEST_CHECK(tm_obj.type == MSGPACK_OBJECT_EXT);
+ TEST_CHECK(tm_obj.via.ext.type == 10);
+ TEST_CHECK(tm_obj.via.ext.size == 8);
+
+ ret = flb_time_msgpack_to_time(&tm, &tm_obj);
+ if(!TEST_CHECK(ret != 0)) {
+ TEST_MSG("flb_time_msgpack_to_time should fail");
+ exit(EXIT_FAILURE);
+ }
+
+ msgpack_sbuffer_destroy(&mp_sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void test_append_to_msgpack_eventtime()
+{
+ struct flb_time tm;
+ int ret;
+ char expect_data[8] = {0};
+
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+ msgpack_unpacked result;
+
+ msgpack_object tm_obj;
+
+ memcpy(&expect_data[0], &eventtime[0], 8);
+
+ tm.tm.tv_sec = SEC_32BIT;
+ tm.tm.tv_nsec = NSEC_32BIT;
+
+ /* create int object*/
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ ret = flb_time_append_to_msgpack(&tm, &mp_pck, FLB_TIME_ETFMT_V1_FIXEXT);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_time_append_to_msgpack failed");
+ exit(EXIT_FAILURE);
+ }
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, mp_sbuf.data, mp_sbuf.size, NULL);
+
+ tm_obj = result.data;
+
+ /* Check if Eventtime */
+ TEST_CHECK(tm_obj.type == MSGPACK_OBJECT_EXT);
+ TEST_CHECK(tm_obj.via.ext.type == 0);
+ TEST_CHECK(tm_obj.via.ext.size == 8);
+
+ if (!TEST_CHECK(memcmp(&expect_data[0], tm_obj.via.ext.ptr, 8) == 0) ) {
+ TEST_MSG("got 0x%x, expect 0x%x", *(uint32_t*)tm_obj.via.ext.ptr, *((uint32_t*)&expect_data[0]));
+ }
+
+ msgpack_sbuffer_destroy(&mp_sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+TEST_LIST = {
+ { "flb_time_to_nanosec" , test_to_nanosec},
+ { "flb_time_append_to_mpack_v1" , test_append_to_mpack_v1},
+ { "msgpack_to_time_int" , test_msgpack_to_time_int},
+ { "msgpack_to_time_double" , test_msgpack_to_time_double},
+ { "msgpack_to_time_eventtime" , test_msgpack_to_time_eventtime},
+ { "msgpack_to_time_invalid" , test_msgpack_to_time_invalid},
+ { "append_to_msgpack_eventtime" , test_append_to_msgpack_eventtime},
+ { NULL, NULL }
+};
diff --git a/fluent-bit/tests/internal/fstore.c b/fluent-bit/tests/internal/fstore.c
new file mode 100644
index 00000000..1f4de61b
--- /dev/null
+++ b/fluent-bit/tests/internal/fstore.c
@@ -0,0 +1,84 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2020 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_fstore.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_compat.h>
+
+#include <chunkio/chunkio.h>
+#include <chunkio/cio_utils.h>
+
+#include "flb_tests_internal.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef FLB_SYSTEM_WINDOWS
+/* Not yet implemented! */
+#else
+#define FSF_STORE_PATH "/tmp/flb-fstore"
+#endif
+
+void cb_all()
+{
+ int ret;
+ void *out_buf;
+ size_t out_size;
+ struct stat st_data;
+ struct flb_fstore *fs;
+ struct flb_fstore_stream *st;
+ struct flb_fstore_file *fsf;
+
+ cio_utils_recursive_delete(FSF_STORE_PATH);
+
+ fs = flb_fstore_create(FSF_STORE_PATH, FLB_FSTORE_FS);
+ TEST_CHECK(fs != NULL);
+
+ st = flb_fstore_stream_create(fs, "abc");
+ TEST_CHECK(st != NULL);
+
+ fsf = flb_fstore_file_create(fs, st, "example.txt", 100);
+ TEST_CHECK(fsf != NULL);
+ if (!fsf) {
+ return;
+ }
+
+ ret = stat(FSF_STORE_PATH "/abc/example.txt", &st_data);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_fstore_file_append(fsf, "fluent-bit\n", 11);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_fstore_file_content_copy(fs, fsf, &out_buf, &out_size);
+ TEST_CHECK(ret == 0);
+
+ TEST_CHECK(memcmp(out_buf, "fluent-bit\n", 11) == 0);
+ TEST_CHECK(out_size == 11);
+ flb_free(out_buf);
+
+ flb_fstore_dump(fs);
+ flb_fstore_destroy(fs);
+}
+
+TEST_LIST = {
+ { "all" , cb_all},
+ { NULL }
+};
diff --git a/fluent-bit/tests/internal/fuzzers/CMakeLists.txt b/fluent-bit/tests/internal/fuzzers/CMakeLists.txt
new file mode 100644
index 00000000..e76be548
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/CMakeLists.txt
@@ -0,0 +1,60 @@
+set(UNIT_TESTS_FILES
+ aws_util_fuzzer.c
+ aws_credentials_fuzzer.c
+ base64_fuzzer.c
+ engine_fuzzer.c
+ cmetrics_decode_fuzz.c
+ config_fuzzer.c
+ config_random_fuzzer.c
+ ctrace_fuzzer.c
+ input_fuzzer.c
+ signv4_fuzzer.c
+ flb_json_fuzzer.c
+ flb_mp_fuzzer.c
+ filter_stdout_fuzzer.c
+ fstore_fuzzer.c
+ parser_fuzzer.c
+ parse_json_fuzzer.c
+ parse_logfmt_fuzzer.c
+ parse_ltsv_fuzzer.c
+ msgpack_parse_fuzzer.c
+ msgpack_to_gelf_fuzzer.c
+ multiline_fuzzer.c
+ pack_json_state_fuzzer.c
+ http_fuzzer.c
+ strp_fuzzer.c
+ utils_fuzzer.c
+ config_map_fuzzer.c
+ record_ac_fuzzer.c
+ config_yaml_fuzzer.c
+ )
+
+# Prepare list of unit tests
+foreach(source_file ${UNIT_TESTS_FILES})
+ get_filename_component(source_file_we ${source_file} NAME_WE)
+ set(source_file_we flb-it-fuzz-${source_file_we})
+
+ add_executable(
+ ${source_file_we}
+ ${source_file} local_test.c
+ )
+
+ if(FLB_JEMALLOC)
+ target_link_libraries(${source_file_we} libjemalloc ${CMAKE_THREAD_LIBS_INIT})
+ else()
+ target_link_libraries(${source_file_we} ${CMAKE_THREAD_LIBS_INIT})
+ endif()
+
+ if(FLB_STREAM_PROCESSOR)
+ target_link_libraries(${source_file_we} flb-sp)
+ endif()
+
+ target_link_libraries(${source_file_we} fluent-bit-static)
+
+ if (FLB_TESTS_OSSFUZZ)
+ add_executable(${source_file_we}_OSSFUZZ ${source_file})
+ target_link_libraries(${source_file_we}_OSSFUZZ ${CMAKE_THREAD_LIBS_INIT})
+ set_target_properties(${source_file_we}_OSSFUZZ PROPERTIES LINK_FLAGS $ENV{LIB_FUZZING_ENGINE})
+ target_link_libraries(${source_file_we}_OSSFUZZ fluent-bit-static)
+ endif()
+endforeach()
diff --git a/fluent-bit/tests/internal/fuzzers/aws_credentials_fuzzer.c b/fluent-bit/tests/internal/fuzzers/aws_credentials_fuzzer.c
new file mode 100644
index 00000000..d6ccb22e
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/aws_credentials_fuzzer.c
@@ -0,0 +1,132 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2023 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <fluent-bit.h>
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_aws_credentials.h>
+#include <fluent-bit/flb_mem.h>
+#include "flb_fuzz_header.h"
+
+int initialization_crutch()
+{
+ struct flb_config *config;
+ config = flb_config_init();
+ if (config == NULL) {
+ return -1;
+ }
+ flb_config_exit(config);
+ return 0;
+}
+
+
+void fuzz_sts(const uint8_t *data, size_t size) {
+ char *sks_response = get_null_terminated(150, &data, &size);
+
+ struct flb_aws_credentials *creds;
+ time_t expiration;
+
+ creds = flb_parse_sts_resp(sks_response, &expiration);
+ if (creds != NULL) {
+ flb_aws_credentials_destroy(creds);
+ }
+
+ if (size > 300) {
+ char *action = get_null_terminated(50, &data, &size);
+ char *role_arn = get_null_terminated(50, &data, &size);
+ char *session_name = get_null_terminated(50, &data, &size);
+ char *external_id = get_null_terminated(50, &data, &size);
+ char *identity_token = get_null_terminated(50, &data, &size);
+
+ flb_sds_t s1 = flb_sts_uri(action, role_arn, session_name,
+ external_id, identity_token);
+ if (s1 != NULL) {
+ flb_sds_destroy(s1);
+ }
+
+ flb_free(action);
+ flb_free(role_arn);
+ flb_free(session_name);
+ flb_free(external_id);
+ flb_free(identity_token);
+ }
+
+ if (sks_response != NULL) {
+ flb_free(sks_response);
+ }
+}
+
+
+void fuzz_http(const uint8_t *data, size_t size) {
+ time_t expiration;
+ struct flb_aws_credentials *creds = NULL;
+
+ char *response = get_null_terminated(250, &data, &size);
+ creds = flb_parse_http_credentials(response, 250, &expiration);
+ if (creds != NULL) {
+ flb_aws_credentials_destroy(creds);
+ }
+ flb_free(response);
+}
+
+
+void fuzz_process(const uint8_t *data, size_t size) {
+ char** tokens = NULL;
+ char *input = get_null_terminated(250, &data, &size);
+ tokens = parse_credential_process(input);
+ if (tokens != NULL) {
+ flb_free(tokens);
+ }
+ flb_free(input);
+}
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ /* Set flb_malloc_mod to be fuzzer-data dependent */
+ if (size < 304) {
+ return 0;
+ }
+ flb_malloc_p = 0;
+ flb_malloc_mod = *(int*)data;
+ data += 4;
+ size -= 4;
+
+ /* Avoid division by zero for modulo operations */
+ if (flb_malloc_mod == 0) {
+ flb_malloc_mod = 1;
+ }
+ if (initialization_crutch() == -1) {
+ return 0;
+ }
+
+ const uint8_t *data_copy = data;
+ size_t size_copy = size;
+ fuzz_sts(data_copy, size_copy);
+
+ data_copy = data;
+ size_copy = size;
+ fuzz_http(data_copy, size_copy);
+
+ data_copy = data;
+ size_copy = size;
+ fuzz_process(data_copy, size_copy);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/aws_util_fuzzer.c b/fluent-bit/tests/internal/fuzzers/aws_util_fuzzer.c
new file mode 100644
index 00000000..dcaa2b9e
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/aws_util_fuzzer.c
@@ -0,0 +1,107 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2023 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <fluent-bit.h>
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_aws_util.h>
+#include <fluent-bit/flb_mem.h>
+#include "flb_fuzz_header.h"
+
+int initialization_crutch()
+{
+ struct flb_config *config;
+ config = flb_config_init();
+ if (config == NULL) {
+ return -1;
+ }
+ flb_config_exit(config);
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ char *format = NULL;
+ char *tag = NULL;
+ char *tag_delimiter = NULL;
+
+ /* Set flb_malloc_mod to be fuzzer-data dependent */
+ if (size < 4) {
+ return 0;
+ }
+ flb_malloc_p = 0;
+ flb_malloc_mod = *(int*)data;
+ data += 4;
+ size -= 4;
+
+ /* Avoid division by zero for modulo operations */
+ if (flb_malloc_mod == 0) {
+ flb_malloc_mod = 1;
+ }
+
+ if (size < 300) {
+ return 0;
+ }
+
+ format = get_null_terminated(50, &data, &size);
+ tag = get_null_terminated(100, &data, &size);
+ tag_delimiter = get_null_terminated(100, &data, &size);
+
+ struct tm day = { 0, 0, 0, 15, 7, 120};
+ time_t t;
+ memset(&t, 0, sizeof(time_t));
+
+ if (format && tag && tag_delimiter) {
+ if (!initialization_crutch()) {
+ flb_sds_t s3_key_format = NULL;
+ s3_key_format = flb_get_s3_key(format, t, tag, tag_delimiter, 0);
+ if (s3_key_format) {
+ flb_sds_destroy(s3_key_format);
+ }
+ if (size > 200) {
+ char *json_val = get_null_terminated(100, &data, &size);
+ if (json_val != NULL) {
+ flb_sds_t s1 = flb_aws_error(json_val, strlen(json_val));
+ if (s1 != NULL) {
+ flb_sds_destroy(s1);
+ }
+ flb_free(json_val);
+ }
+ char *xml_val = get_null_terminated(100, &data, &size);
+ if (xml_val != NULL) {
+ flb_sds_t s2 = flb_aws_xml_error(xml_val, strlen(xml_val));
+ if (s2 != NULL) {
+ flb_sds_destroy(s2);
+ }
+ flb_free(xml_val);
+ }
+ }
+ }
+ }
+ if (format) {
+ flb_free(format);
+ }
+ if (tag) {
+ flb_free(tag);
+ }
+ if (tag_delimiter) {
+ flb_free(tag_delimiter);
+ }
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/base64_fuzzer.c b/fluent-bit/tests/internal/fuzzers/base64_fuzzer.c
new file mode 100644
index 00000000..90c4c08f
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/base64_fuzzer.c
@@ -0,0 +1,20 @@
+#include <unistd.h>
+#include <stdint.h>
+#include <fluent-bit/flb_base64.h>
+#include <fluent-bit/flb_mem.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ char out[100];
+ size_t olen;
+
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+
+ flb_base64_encode((unsigned char *) out, 100,
+ &olen, (unsigned char *)data, size);
+ flb_base64_decode((unsigned char *) out, 100,
+ &olen, (unsigned char *)data, size);
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/cmetrics_decode_fuzz.c b/fluent-bit/tests/internal/fuzzers/cmetrics_decode_fuzz.c
new file mode 100644
index 00000000..1d1098c0
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/cmetrics_decode_fuzz.c
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2023 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cmetrics/cmt_decode_opentelemetry.h>
+#include <cmetrics/cmt_decode_prometheus.h>
+
+
+int
+LLVMFuzzerTestOneInput(const uint8_t * data, size_t size)
+{
+ struct cfl_list decoded_contexts;
+ struct cmt *cmt = NULL;
+ size_t off = 0;
+ uint8_t decider;
+ int result;
+
+ /* At least one byte is needed for deciding which decoder to use */
+ if (size < 1) {
+ return 0;
+ }
+
+ decider = data[0] % 3;
+
+ /* Adjust data pointer since the first byte is used */
+ data += 1;
+ size -= 1;
+
+ /* Fuzz a given decoder */
+ if (decider == 0) {
+ result = cmt_decode_opentelemetry_create(&decoded_contexts, data, size,
+ &off);
+ if (result == CMT_DECODE_OPENTELEMETRY_SUCCESS) {
+ cmt_decode_opentelemetry_destroy (&decoded_contexts);
+ }
+ }
+ else if (decider == 1) {
+ result = cmt_decode_msgpack_create(&cmt, (char *) data, size, &off);
+ if (result == 0) {
+ cmt_destroy(cmt);
+ }
+ }
+ else if (decider == 2) {
+ if (size == 0) {
+ return 0;
+ }
+ struct cmt_decode_prometheus_parse_opts opts;
+ result = cmt_decode_prometheus_create(&cmt, data, size, &opts);
+ if (result == CMT_DECODE_PROMETHEUS_SUCCESS) {
+ cmt_decode_prometheus_destroy(cmt);
+ }
+ }
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/config_fuzzer.c b/fluent-bit/tests/internal/fuzzers/config_fuzzer.c
new file mode 100644
index 00000000..5512dac2
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/config_fuzzer.c
@@ -0,0 +1,417 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_slist.h>
+#include <fluent-bit/flb_kv.h>
+#include "flb_fuzz_header.h"
+
+/* A sample of configurations */
+char conf_file[] = "# Parser: no_year\n"
+"# ===============\n"
+"# the given format don't contain the Year, this is a common\n"
+"# case on old Syslog implementations.\n"
+"#\n"
+"[PARSER]\n"
+" Name no_year\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %b %d %H:%M:%S\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: no_year_N\n"
+"# =================\n"
+"# Just for compatibility, check a string with no year but including Nanoseconds.\n"
+"#\n"
+"[PARSER]\n"
+" Name no_year_N\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %b %d %H:%M:%S.%L\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: no_year_NC\n"
+"# =================\n"
+"# Just for compatibility, check a string with no year but including Nanoseconds with comma as fractional separator.\n"
+"#\n"
+"[PARSER]\n"
+" Name no_year_NC\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %b %d %H:%M:%S,%L\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: no_year_TZ\n"
+"# =================\n"
+"# Time string with no year and including timezone\n"
+"#\n"
+"[PARSER]\n"
+" Name no_year_TZ\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %b %d %H:%M:%S %z\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: no_year_N_TZ\n"
+"# ====================\n"
+"# Time string with no year, nanoseconds and timezone\n"
+"#\n"
+"[PARSER]\n"
+" Name no_year_N_TZ\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %b %d %H:%M:%S.%L %z\n"
+" Time_Keep On\n"
+"\n"
+"\n"
+"# Parser: no_year_NC_TZ\n"
+"# ====================\n"
+"# Time string with no year, nanoseconds and timezone with comma as fractional separator.\n"
+"#\n"
+"[PARSER]\n"
+" Name no_year_NC_TZ\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %b %d %H:%M:%S,%L %z\n"
+" Time_Keep On\n"
+"\n"
+"\n"
+"# Parser: default_UTC\n"
+"# ===================\n"
+"# Time string with timezone in UTC\n"
+"#\n"
+"[PARSER]\n"
+" Name default_UTC\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%S\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: default_UTC_Z\n"
+"# =====================\n"
+"# Time string with timezone in UTC and ending Z\n"
+"#\n"
+"[PARSER]\n"
+" Name default_UTC_Z\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%SZ\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: default_UTC_N_Z\n"
+"# =======================\n"
+"# Time string with timezone in UTC, nanoseconds and ending Z\n"
+"#\n"
+"[PARSER]\n"
+" Name default_UTC_N_Z\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%S.%LZ\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: default_UTC_NC_Z\n"
+"# =======================\n"
+"# Time string with timezone in UTC, nanoseconds with comma as fractional separator and ending Z\n"
+"#\n"
+"[PARSER]\n"
+" Name default_UTC_NC_Z\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%S,%LZ\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: generic_TZ\n"
+"# ==================\n"
+"# Generic date with timezone\n"
+"#\n"
+"[PARSER]\n"
+" Name generic_TZ\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%S %z\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: generic\n"
+"# ===============\n"
+"# Generic date\n"
+"#\n"
+"[PARSER]\n"
+" Name generic\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%S\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: generic_N\n"
+"# ===============\n"
+"# Generic date with nanoseconds\n"
+"#\n"
+"[PARSER]\n"
+" Name generic_N\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%S.%L\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: generic_NC\n"
+"# ===============\n"
+"# Generic date with nanoseconds with comma as fractional separator\n"
+"#\n"
+"[PARSER]\n"
+" Name generic_NC\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%S,%L\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: generic_N_TZ\n"
+"# ====================\n"
+"# Generic date with nanoseconds and timezone\n"
+"#\n"
+"[PARSER]\n"
+" Name generic_N_TZ\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%S.%L %z\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: generic_NC_TZ\n"
+"# ====================\n"
+"# Generic date with nanoseconds with comma as fractional separator and timezone\n"
+"#\n"
+"[PARSER]\n"
+" Name generic_NC_TZ\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%S,%L %z\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: apache_error\n"
+"# ====================\n"
+"# Apache error log time format\n"
+"#\n"
+"[PARSER]\n"
+" Name apache_error\n"
+" Format json\n"
+" Time_Key time\n"
+" Time_Format %a %b %d %H:%M:%S.%L %Y\n"
+" Time_Keep On\n"
+"# Parser: mysql_quoted_stuff\n"
+"# ====================\n"
+"# Apache error log time format\n"
+"#\n"
+"[PARSER]\n"
+" Name mysql_quoted_stuff\n"
+" Format regex\n"
+" Regex ^(?<time>.*?),(?<key001>.*)$\n"
+" Time_Key time\n"
+" Time_Format %Y-%M-%S %H:%M:%S\n"
+" Time_Keep On\n"
+" Decode_Field_As mysql_quoted key001\n"
+"# Parser: REGEX_generic_NC_TZ\n"
+"# ====================\n"
+"# Generic date with nanoseconds with comma as fractional separator and timezone\n"
+"#\n"
+"[PARSER]\n"
+" Name REGEX_generic_NC_TZ\n"
+" Format regex\n"
+" Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$\n"
+" Time_Key time\n"
+" Time_Format %m/%d/%Y %H:%M:%S,%L %z\n"
+" Time_Keep On\n"
+"\n"
+"# Parser: REGEX_apache_error\n"
+"# ====================\n"
+"# Apache error log time format\n"
+"#\n"
+"[PARSER]\n"
+" Name REGEX_apache_error\n"
+" Format regex\n"
+" Regex ^(?<key001>[^ ]*) (?<key002>[^ ]*) (?<time>.+)$\n"
+" Time_Key time\n"
+" Time_Format %a %b %d %H:%M:%S.%L %Y\n"
+" Time_Keep On\n"
+"\n"
+"\n"
+"\n"
+"# Parser: REGEX_mysql_quoted_stuff\n"
+"# ====================\n"
+"# Apache error log time format\n"
+"#\n"
+"[PARSER]\n"
+" Name REGEX_mysql_quoted_stuff\n"
+" Format regex\n"
+" Regex ^(?<time>.*?),(?<key001>.*)$\n"
+" Time_Key time\n"
+" Time_Format %Y-%M-%S %H:%M:%S\n"
+" Time_Keep On\n"
+" Decode_Field_As mysql_quoted key001\n"
+"\n"
+"\n"
+"\n"
+"# Parser: REGEX2_mysql_quoted_stuff\n"
+"# ====================\n"
+"# Apache error log time format\n"
+"#\n"
+"[PARSER]\n"
+" Name REGEX2_mysql_quoted_stuff\n"
+" Format logfmt\n"
+" Regex ^(?<time>.*?),(?<key001>.*)$\n"
+" Time_Key time\n"
+" Time_Format %Y-%M-%S %H:%M:%S\n"
+" Time_Keep On\n"
+" Decode_Field_As mysql_quoted key001\n"
+" Types A1:integer A2:string A3:bool A4:float A5:hex\n"
+"\n"
+"\n"
+"\n"
+"# Parser: REGEX3_mysql_quoted_stuff\n"
+"# ====================\n"
+"# Apache error log time format\n"
+"#\n"
+"[PARSER]\n"
+" Name REGEX3_mysql_quoted_stuff\n"
+" Format json\n"
+" Regex ^(?<time>.*?),(?<key001>.*)$\n"
+" Time_Key time\n"
+" Time_Format %Y-%M-%S %H:%M:%S\n"
+" Time_Keep On\n"
+" Decode_Field_As escaped_utf8 key001\n"
+" Types A1:integer A2:string A3:bool A4:float A5:hex\n"
+"\n"
+"\n"
+"\n"
+"# Parser: REGEX33_mysql_quoted_stuff\n"
+"# ====================\n"
+"# Apache error log time format\n"
+"#\n"
+"[PARSER]\n"
+" Name REGEX33_mysql_quoted_stuff\n"
+" Format json\n"
+" Regex ^(?<time>.*?),(?<key001>.*)$\n"
+" Time_Key time\n"
+" Time_Format %Y-%M-%S %H:%M:%S\n"
+" Time_Keep On\n"
+" Decode_Field_As escaped key001\n"
+" Types A1:integer A2:string A3:bool A4:float A5:hex\n"
+"\n"
+"\n"
+"\n"
+"# Parser: REGEX4_mysql_quoted_stuff\n"
+"# ====================\n"
+"# Apache error log time format\n"
+"#\n"
+"[PARSER]\n"
+" Name REGEX4_mysql_quoted_stuff\n"
+" Format json\n"
+" Regex ^(?<time>.*?),(?<key001>.*)$\n"
+" Time_Key time\n"
+" Time_Format %Y-%M-%S %H:%M:%S\n"
+" Time_Keep On\n"
+" Decode_Field_As json key001\n"
+" Types A1:integer A2:string A3:bool A4:float A5:hex\n"
+"[MULTILINE_PARSER]\n"
+" name exception_test\n"
+" type regex\n"
+" flush_timeout 1000\n"
+" rule \"start_state\" \"/(Dec \\d+ \\d+\\:\\d+\\:\\d+)(.*)/\" \"cont\"\n"
+" rule \"cont\" \"/^\\s+at.*/\" \"cont\"\n";
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+
+ /* Limit the size of the config files to 32KB. */
+ if (size > 32768) {
+ return 0;
+ }
+
+ /* Write the config file to a location we know OSS-Fuzz has */
+ char filename[256];
+ sprintf(filename, "/tmp/libfuzzer.%d", getpid());
+ FILE *fp = fopen(filename, "wb");
+ if (!fp) {
+ return 0;
+ }
+ fwrite(conf_file, strlen(conf_file), 1, fp);
+ fclose(fp);
+
+
+ /* Now parse random data based on the config files */
+ struct flb_config *config = NULL;
+ config = flb_config_init();
+ int ret = flb_parser_conf_file(filename, config);
+ if (ret == 0) {
+ struct mk_list *head = NULL;
+ mk_list_foreach(head, &config->parsers) {
+ size_t out_size;
+ char *out_buf = NULL;
+ struct flb_parser *parser = NULL;
+ struct flb_time out_time;
+ parser = mk_list_entry(head, struct flb_parser, _head);
+ flb_parser_do(parser, (const char*)data, size, (void **)&out_buf,
+ &out_size, &out_time);
+ if (out_buf != NULL) {
+ free(out_buf);
+ }
+ }
+ }
+ flb_parser_exit(config);
+ flb_config_exit(config);
+
+ if (size > 100) {
+ /* Now let's do a second run where we also call flb_config_set_property */
+ config = flb_config_init();
+ ret = flb_parser_conf_file(filename, config);
+ char *key_1 = get_null_terminated(15, &data, &size);
+ char *val_1 = get_null_terminated(15, &data, &size);
+ char *key_2 = get_null_terminated(15, &data, &size);
+ char *val_2 = get_null_terminated(15, &data, &size);
+ char *progname = get_null_terminated(15, &data, &size);
+
+ flb_config_set_property(config, key_1, val_1);
+ flb_config_set_property(config, key_2, val_2);
+ flb_config_set_program_name(config, progname);
+ set_log_level_from_env(config);
+
+ struct mk_list prop;
+ flb_kv_init(&prop);
+ flb_kv_item_create(&prop, key_1, val_1);
+ flb_config_prop_get(progname, &prop);
+ flb_slist_entry_get(&prop, (int)data[0]);
+ flb_slist_dump(&prop);
+
+ if (ret == 0) {
+ struct mk_list *head = NULL;
+ mk_list_foreach(head, &config->parsers) {
+ size_t out_size;
+ char *out_buf = NULL;
+ struct flb_parser *parser = NULL;
+ struct flb_time out_time;
+
+ parser = mk_list_entry(head, struct flb_parser, _head);
+ flb_parser_do(parser, (const char*)data, size, (void **)&out_buf,
+ &out_size, &out_time);
+ if (out_buf != NULL) {
+ free(out_buf);
+ }
+ }
+ }
+ flb_parser_exit(config);
+ flb_config_exit(config);
+ flb_free(key_1);
+ flb_free(val_1);
+ flb_free(key_2);
+ flb_free(val_2);
+ flb_free(progname);
+ flb_kv_release(&prop);
+ }
+
+ /* clean up the file */
+ unlink(filename);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/config_map_fuzzer.c b/fluent-bit/tests/internal/fuzzers/config_map_fuzzer.c
new file mode 100644
index 00000000..79697f65
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/config_map_fuzzer.c
@@ -0,0 +1,212 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_kv.h>
+#include <fluent-bit/flb_slist.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_config.h>
+#include <fluent-bit/flb_config_map.h>
+
+#include "flb_fuzz_header.h"
+
+struct context {
+ /* Single values */
+ int num_int;
+ size_t size;
+ time_t time;
+ char boolean;
+ double num_double;
+ flb_sds_t string;
+ struct mk_list *list1;
+ struct mk_list *list2;
+
+ /* Multiple entries */
+ struct mk_list *mult_num_int;
+ struct mk_list *mult_boolean;
+ struct mk_list *mult_num_double;
+ struct mk_list *mult_string;
+ struct mk_list *mult_list1;
+ struct mk_list *mult_list2;
+};
+
+struct flb_config_map config_map[] = {
+ {
+ FLB_CONFIG_MAP_BOOL,
+ "boolean",
+ "true",
+ 0, FLB_TRUE, offsetof(struct context, boolean),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_INT,
+ "num_int",
+ "123",
+ 0, FLB_TRUE, offsetof(struct context, num_int),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_DOUBLE,
+ "num_double", "0.12345",
+ 0, FLB_TRUE, offsetof(struct context, num_double),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_STR,
+ "string",
+ "test",
+ 0, FLB_TRUE, offsetof(struct context, string),
+ NULL
+ },
+
+ /* SIZE */
+ {
+ FLB_CONFIG_MAP_SIZE,
+ "test_size",
+ "2M",
+ 0, FLB_TRUE, offsetof(struct context, size),
+ NULL
+ },
+
+ /* TIME */
+ {
+ FLB_CONFIG_MAP_TIME,
+ "test_time",
+ "2H",
+ 0, FLB_TRUE, offsetof(struct context, time),
+ NULL
+ },
+
+ /* CSLIST */
+ {
+ FLB_CONFIG_MAP_CLIST,
+ "test_clist",
+ "a, b, c ,d,e , f, g,h,i,jk , lm , n o,pqr,, , ,stuv,xyz",
+ 0, FLB_TRUE, offsetof(struct context, list1),
+ NULL
+ },
+
+ /* SLIST */
+ {
+ FLB_CONFIG_MAP_SLIST_4,
+ "test_slist",
+ "a b c de f ghi jk l m n o pqr stuv xyz",
+ 0, FLB_TRUE, offsetof(struct context, list2),
+ NULL
+ },
+
+ /* EOF */
+ {0}
+};
+
+struct flb_config_map config_map_mult[] = {
+ {
+ FLB_CONFIG_MAP_BOOL,
+ "no_mult",
+ "true",
+ 0, FLB_TRUE, 1,
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_BOOL,
+ "mult_boolean",
+ NULL,
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_boolean),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_INT,
+ "mult_num_int",
+ "123",
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_num_int),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_DOUBLE,
+ "mult_num_double", "0.12345",
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_num_double),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_STR,
+ "mult_string",
+ "test",
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_string),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_CLIST,
+ "mult_clist",
+ "a, b, c ,d,e , f, g,h,i,jk , lm , n o,pqr,, , ,stuv,xyz",
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_list1),
+ NULL
+ },
+ {
+ FLB_CONFIG_MAP_SLIST_4,
+ "mult_slist",
+ "a b c de f ghi jk l m n o pqr stuv xyz",
+ FLB_CONFIG_MAP_MULT, FLB_TRUE, offsetof(struct context, mult_list2),
+ NULL
+ },
+
+ /* EOF */
+ {0}
+};
+
+struct flb_config_map *configs[] = {config_map_mult, config_map};
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ /* Set flb_malloc_mod to be fuzzer-data dependent */
+ if (size < 4) {
+ return 0;
+ }
+ flb_malloc_p = 0;
+ flb_malloc_mod = *(int*)data;
+ data += 4;
+ size -= 4;
+
+ /* Avoid division by zero for modulo operations */
+ if (flb_malloc_mod == 0) {
+ flb_malloc_mod = 1;
+ }
+
+ if (size < 40) {
+ return 0;
+ }
+
+ struct mk_list *map = NULL;
+ struct flb_config *config = NULL;
+ struct context ctx;
+ bzero(&ctx, sizeof(struct context));
+ struct mk_list prop;
+ bzero(&prop, sizeof(struct mk_list));
+
+ char *fuzz_str1 = get_null_terminated(15, &data, &size);
+ char *fuzz_str2 = get_null_terminated(15, &data, &size);
+ char *fuzz_str3 = get_null_terminated(size, &data, &size);
+
+ for (int i = 0; i < 2; i++) {
+ config = flb_config_init();
+ if (config) {
+ memset(&ctx, '\0', sizeof(struct context));
+
+ flb_kv_init(&prop);
+ if (flb_kv_item_create(&prop, fuzz_str1, fuzz_str2) != NULL) {
+ /* Assign one of the config maps */
+ map = flb_config_map_create(config, configs[i]);
+ if (map) {
+ if (flb_config_map_set(&prop, map, &ctx) != -1) {
+ flb_config_map_properties_check(fuzz_str3, &prop, map);
+ }
+ flb_config_map_destroy(map);
+ }
+ }
+ flb_kv_release(&prop);
+ flb_config_exit(config);
+ }
+ }
+
+ flb_free(fuzz_str1);
+ flb_free(fuzz_str2);
+ flb_free(fuzz_str3);
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/config_random_fuzzer.c b/fluent-bit/tests/internal/fuzzers/config_random_fuzzer.c
new file mode 100644
index 00000000..ead06ad0
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/config_random_fuzzer.c
@@ -0,0 +1,57 @@
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_slist.h>
+#include "flb_fuzz_header.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+
+ /* Limit the size of the config files to 32KB. */
+ if (size > 32768) {
+ return 0;
+ }
+
+ /* Write the config file to a location we know OSS-Fuzz has */
+ char filename[256];
+ sprintf(filename, "/tmp/libfuzzer.%d", getpid());
+ FILE *fp = fopen(filename, "wb");
+ if (!fp) {
+ return 0;
+ }
+ fwrite(data, size, 1, fp);
+ fclose(fp);
+
+ /* Now parse a random config file */
+ struct flb_config *config = NULL;
+ config = flb_config_init();
+ flb_parser_conf_file(filename, config);
+ flb_parser_exit(config);
+ flb_config_exit(config);
+
+ /* Cleanup written config file */
+ unlink(filename);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/config_yaml_fuzzer.c b/fluent-bit/tests/internal/fuzzers/config_yaml_fuzzer.c
new file mode 100644
index 00000000..99e17ba6
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/config_yaml_fuzzer.c
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2023 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_kv.h>
+#include <fluent-bit/flb_config_format.h>
+
+#include <cfl/cfl.h>
+#include <cfl/cfl_list.h>
+
+#include "flb_fuzz_header.h"
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+
+ /* Limit the size of the config files to 32KB. */
+ if (size > 32768) {
+ return 0;
+ }
+
+ /* Write the config file to a location we know OSS-Fuzz has */
+ char filename[256];
+ sprintf(filename, "/tmp/libfuzzer.%d.yaml", getpid());
+ FILE *fp = fopen(filename, "wb");
+ if (!fp) {
+ return 0;
+ }
+ fwrite(data, size, 1, fp);
+ fclose(fp);
+
+
+ struct flb_cf *cf;
+ struct flb_cf_section *s;
+
+ cf = flb_cf_yaml_create(NULL, filename, NULL, 0);
+ if (cf != NULL) {
+ flb_cf_destroy(cf);
+ }
+
+ /* clean up the file */
+ unlink(filename);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/ctrace_fuzzer.c b/fluent-bit/tests/internal/fuzzers/ctrace_fuzzer.c
new file mode 100644
index 00000000..bcd2ca58
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/ctrace_fuzzer.c
@@ -0,0 +1,24 @@
+#include <stdint.h>
+#include <fluent-bit/flb_mem.h>
+#include <ctraces/ctraces.h>
+#include <ctraces/ctr_decode_msgpack.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size){
+ size_t off = 0;
+ struct ctrace *ctr = NULL;
+ size_t msgpack_text_size;
+ char *msgpack_text_buffer = NULL;
+
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+
+ ctr_decode_msgpack_create(&ctr, data, size, &off);
+ if (ctr != NULL) {
+ ctr_encode_msgpack_create(ctr, &msgpack_text_buffer, &msgpack_text_size);
+ ctr_encode_msgpack_destroy(msgpack_text_buffer);
+
+ ctr_destroy(ctr);
+ }
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/engine_fuzzer.c b/fluent-bit/tests/internal/fuzzers/engine_fuzzer.c
new file mode 100644
index 00000000..498b86a8
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/engine_fuzzer.c
@@ -0,0 +1,171 @@
+#include <fluent-bit.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_input_chunk.h>
+#include <fluent-bit/flb_metrics.h>
+#include "flb_fuzz_header.h"
+
+#include <stdio.h>
+#include <monkey/mk_core.h>
+
+struct flb_parser *parser;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+int filter_ffd;
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 100) {
+ return 0;
+ }
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+
+ uint8_t ud = data[0];
+ MOVE_INPUT(1);
+ #define NM_SIZE 50
+ char *null_terminated = get_null_terminated(NM_SIZE, &data, &size);
+
+ char *nm3 = get_null_terminated(10, &data, &size);
+ char *nm4 = get_null_terminated(10, &data, &size);
+ int random_i1 = *(int *)data;
+ MOVE_INPUT(4);
+ int random_i2 = *(int *)data;
+
+ #define FUNC_NUMS 10
+ switch (ud % FUNC_NUMS) {
+ case 0:
+ flb_output(ctx, null_terminated, nm3);
+ break;
+ case 1:
+ flb_filter(ctx, null_terminated, nm3);
+ break;
+ case 2:
+ flb_input(ctx, null_terminated, nm3);
+ break;
+ case 3:
+ flb_output_check(ctx->config);
+ break;
+ case 4: {
+ struct mk_list *head;
+ struct flb_input_instance *entry;
+ mk_list_foreach(head, &ctx->config->inputs) {
+ entry = mk_list_entry(head, struct flb_input_instance, _head);
+ flb_input_name_exists(nm3, ctx->config);
+ flb_input_get_property(nm3, entry);
+ flb_input_name(entry);
+ flb_input_collector_running(0, entry);
+ flb_input_collector_pause(random_i1, entry);
+ flb_input_collector_resume(random_i2, entry);
+ flb_input_net_default_listener(nm4, random_i1, entry);
+ flb_input_collector_start(random_i2, entry);
+ }
+ }
+ break;
+ case 5: {
+ struct mk_list *head;
+ struct flb_input_instance *entry;
+ mk_list_foreach(head, &ctx->config->inputs) {
+ entry = mk_list_entry(head, struct flb_input_instance, _head);
+ if (entry->storage != NULL) {
+ char bufbuf[100];
+ flb_input_chunk_append_raw(entry, FLB_INPUT_LOGS, 0, "A",
+ 1, "\0", 0);
+
+ struct flb_input_chunk *ic = NULL;
+ ic = flb_input_chunk_create(entry, FLB_INPUT_LOGS, nm3, 10);
+ if (ic != NULL) {
+ flb_input_chunk_get_size(ic);
+ flb_input_chunk_set_up_down(ic);
+ flb_input_chunk_down(ic);
+ flb_input_chunk_set_up(ic);
+ flb_input_chunk_get_name(ic);
+ char *tag_buf;
+ int tag_len;
+ flb_input_chunk_get_tag(ic, &tag_buf, &tag_len);
+ size_t flushed;
+ flb_input_chunk_flush(ic, &flushed);
+ }
+ }
+ }
+ }
+ break;
+ case 6:
+ flb_input_check(ctx->config);
+ flb_input_pause_all(ctx->config);
+ break;
+ case 7: {
+ struct mk_list *head;
+ struct flb_output_instance *entry;
+ mk_list_foreach(head, &ctx->config->outputs) {
+ entry = mk_list_entry(head, struct flb_output_instance, _head);
+ flb_output_net_default(nm4, random_i1, entry);
+ flb_output_name(entry);
+ }
+ }
+ break;
+ default:
+ flb_lib_push(ctx, in_ffd, null_terminated, NM_SIZE);
+ break;
+ }
+
+ flb_free(null_terminated);
+ flb_free(nm3);
+ flb_free(nm4);
+ return 0;
+}
+
+int callback_test(void* data, size_t size, void* cb_data)
+{
+ return 0;
+}
+
+struct flb_lib_out_cb cb;
+
+
+int LLVMFuzzerInitialize(int *argc, char ***argv) {
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "0", "Grace",
+ "0", "Log_Level", "debug", NULL);
+
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ flb_input_set(ctx, in_ffd, (char *) "test", NULL);
+ flb_input_set(ctx, in_ffd, (char *) "BBBB", NULL);
+ flb_input_set(ctx, in_ffd, (char *) "AAAA", NULL);
+ flb_input_set(ctx, in_ffd, (char *) "AAAAA", NULL);
+ flb_input_set(ctx, in_ffd, (char *) "CC", NULL);
+ flb_input_set(ctx, in_ffd, (char *) "A", NULL);
+
+ parser = flb_parser_create("timestamp", "regex", "^(?<time>.*)$", FLB_TRUE,
+ "%s.%L", "time", NULL, MK_FALSE, 0, FLB_FALSE,
+ NULL, 0, NULL, ctx->config);
+ filter_ffd = flb_filter(ctx, (char *) "parser", NULL);
+ int ret;
+ ret = flb_filter_set(ctx, filter_ffd, "Match", "test",
+ "Key_Name", "@timestamp",
+ "Parser", "timestamp",
+ "Reserve_Data", "On",
+ NULL);
+
+ cb.cb = callback_test;
+ cb.data = NULL;
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ flb_output_set(ctx, out_ffd, "Match", "*",
+ "format", "json", NULL);
+
+ flb_output_set(ctx, out_ffd,"match", "test", NULL);
+ flb_output_set(ctx, out_ffd,"region", "us-west-2", NULL);
+ flb_output_set(ctx, out_ffd,"log_group_name", "fluent", NULL);
+ flb_output_set(ctx, out_ffd,"log_stream_prefix", "from-fluent-", NULL);
+ flb_output_set(ctx, out_ffd,"auto_create_group", "On", NULL);
+ flb_output_set(ctx, out_ffd,"net.keepalive", "Off", NULL);
+ flb_output_set(ctx, out_ffd,"Retry_Limit", "1", NULL);
+
+ /* start the engine */
+ flb_start(ctx);
+}
diff --git a/fluent-bit/tests/internal/fuzzers/filter_stdout_fuzzer.c b/fluent-bit/tests/internal/fuzzers/filter_stdout_fuzzer.c
new file mode 100644
index 00000000..ed9a3f49
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/filter_stdout_fuzzer.c
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2022 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <fluent-bit.h>
+#include "flb_fuzz_header.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ int ret;
+ flb_ctx_t *ctx;
+ int in_ffd;
+ int out_ffd;
+
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+
+
+ ctx = flb_create();
+ flb_service_set(ctx, "Flush", "1", "Grace", "1", "Log_Level", "error", NULL);
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ if (in_ffd >= 0) {
+ flb_input_set(ctx, in_ffd, "tag", "test", NULL);
+
+ out_ffd = flb_output(ctx, (char *) "stdout", NULL);
+ if (out_ffd >= 0) {
+ flb_output_set(ctx, out_ffd, "match", "test", NULL);
+
+ ret = flb_start(ctx);
+ if (ret == 0) {
+ char *p = get_null_terminated(size, &data, &size);
+ for (int i = 0; i < strlen(p); i++) {
+ flb_lib_push(ctx, in_ffd, p+i, 1);
+ }
+ free(p);
+
+ sleep(1); /* waiting flush */
+ }
+ }
+ }
+ flb_stop(ctx);
+ flb_destroy(ctx);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/flb_fuzz_header.h b/fluent-bit/tests/internal/fuzzers/flb_fuzz_header.h
new file mode 100644
index 00000000..cda08920
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/flb_fuzz_header.h
@@ -0,0 +1,42 @@
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#define GET_MOD_EQ(max, idx) (data[0] % max) == idx
+#define MOVE_INPUT(offset) data += offset; size -= offset;
+
+#define TIMEOUT_GUARD if (size > 32768) return 0;
+
+char *get_null_terminated(size_t size, const uint8_t **data,
+ size_t *total_data_size)
+{
+ char *tmp = flb_malloc(size+1);
+ if (tmp == NULL) {
+ tmp = malloc(size+1);
+ }
+ memcpy(tmp, *data, size);
+ tmp[size] = '\0';
+
+ /* Modify the fuzz variables */
+ *total_data_size -= size;
+ *data += size;
+
+ return tmp;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/flb_json_fuzzer.c b/fluent-bit/tests/internal/fuzzers/flb_json_fuzzer.c
new file mode 100644
index 00000000..916b1a51
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/flb_json_fuzzer.c
@@ -0,0 +1,82 @@
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdlib.h>
+#include <stdint.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_str.h>
+#include "flb_fuzz_header.h"
+
+int LLVMFuzzerTestOneInput(unsigned char *data, size_t size)
+{
+ TIMEOUT_GUARD
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+
+ if (size < 1) {
+ return 0;
+ }
+ unsigned char decider = *data;
+ data++;
+ size--;
+
+ /* json packer */
+ char *out_buf = NULL;
+ size_t out_size;
+ int root_type;
+ int ret = flb_pack_json((char*)data, size, &out_buf, &out_size, &root_type, NULL);
+
+ if (ret == 0) {
+ size_t off = 0;
+ msgpack_unpacked result;
+ msgpack_unpacked_init(&result);
+ int ret2 = msgpack_unpack_next(&result, out_buf, out_size, &off);
+ if (ret2 == MSGPACK_UNPACK_SUCCESS) {
+ msgpack_object root = result.data;
+ char *tmp = NULL;
+ tmp = flb_msgpack_to_json_str(0, &root);
+ if (tmp != NULL) {
+ flb_free(tmp);
+ }
+ }
+ msgpack_unpacked_destroy(&result);
+ flb_sds_t d;
+ d = flb_sds_create("date");
+ if (decider < 0x30) {
+ flb_sds_t ret_s = flb_pack_msgpack_to_json_format(out_buf, out_size,
+ FLB_PACK_JSON_FORMAT_LINES,
+ (int)decider, d);
+ free(out_buf);
+ if (ret_s != NULL) {
+ flb_sds_destroy(ret_s);
+ }
+ }
+ else {
+ flb_sds_t ret_s = flb_pack_msgpack_to_json_format(out_buf, out_size,
+ FLB_PACK_JSON_FORMAT_LINES,
+ FLB_PACK_JSON_DATE_EPOCH, NULL);
+ free(out_buf);
+ if (ret_s != NULL) {
+ flb_sds_destroy(ret_s);
+ }
+ }
+ flb_sds_destroy(d);
+ }
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/flb_mp_fuzzer.c b/fluent-bit/tests/internal/fuzzers/flb_mp_fuzzer.c
new file mode 100644
index 00000000..b33c9040
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/flb_mp_fuzzer.c
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2023 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdlib.h>
+#include <stdint.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_mp.h>
+
+#include "flb_fuzz_header.h"
+
+int LLVMFuzzerTestOneInput(unsigned char *data, size_t size)
+{
+ /* Set flb_malloc_mod to be fuzzer-data dependent */
+ if (size < 5) {
+ return 0;
+ }
+ flb_malloc_p = 0;
+ flb_malloc_mod = *(int*)data;
+ data += 4;
+ size -= 4;
+
+ /* Avoid division by zero for modulo operations */
+ if (flb_malloc_mod == 0) {
+ flb_malloc_mod = 1;
+ }
+
+ unsigned char decider = *data;
+ data++;
+ size--;
+
+ int out_records;
+ size_t processed_bytes;
+ if (decider % 2 == 0) {
+ flb_mp_validate_log_chunk(data, size, &out_records, &processed_bytes);
+ }
+ else if (decider % 2 == 1) {
+ flb_mp_validate_metric_chunk(data, size, &out_records, &processed_bytes);
+ }
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/fstore_fuzzer.c b/fluent-bit/tests/internal/fuzzers/fstore_fuzzer.c
new file mode 100644
index 00000000..e2a95106
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/fstore_fuzzer.c
@@ -0,0 +1,86 @@
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_fstore.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_compat.h>
+
+#include <chunkio/chunkio.h>
+#include <chunkio/cio_utils.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+#define FSF_STORE_PATH "/tmp/flb-fstore"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ int ret;
+ void *out_buf;
+ size_t out_size;
+ struct stat st_data;
+ struct flb_fstore *fs;
+ struct flb_fstore_stream *st;
+ struct flb_fstore_file *fsf;
+
+ /* Set flb_malloc_mod to be fuzzer-data dependent */
+ if (size < 4) {
+ return 0;
+ }
+ flb_malloc_p = 0;
+ flb_malloc_mod = *(int*)data;
+ data += 4;
+ size -= 4;
+
+ /* Avoid division by zero for modulo operations */
+ if (flb_malloc_mod == 0) {
+ flb_malloc_mod = 1;
+ }
+
+ cio_utils_recursive_delete(FSF_STORE_PATH);
+ fs = flb_fstore_create(FSF_STORE_PATH, FLB_FSTORE_FS);
+ if (fs == NULL) {
+ return 0;
+ }
+ st = flb_fstore_stream_create(fs, "abc");
+ if (st != NULL) {
+ fsf = flb_fstore_file_create(fs, st, "example.txt", size);
+
+ if (fsf != NULL) {
+ ret = flb_fstore_file_append(fsf, data, size);
+ if (ret == 0) {
+ ret = flb_fstore_file_content_copy(fs, fsf, &out_buf, &out_size);
+ if (ret == 0) {
+ assert(memcmp(out_buf, data, size) == 0);
+ }
+ flb_free(out_buf);
+ }
+ }
+ }
+
+ flb_fstore_dump(fs);
+ flb_fstore_destroy(fs);
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/http_fuzzer.c b/fluent-bit/tests/internal/fuzzers/http_fuzzer.c
new file mode 100644
index 00000000..28f0c8d2
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/http_fuzzer.c
@@ -0,0 +1,116 @@
+#include <stdlib.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_error.h>
+#include <fluent-bit/flb_socket.h>
+#include <fluent-bit/flb_stream.h>
+#include <fluent-bit/flb_connection.h>
+#include <fluent-bit/flb_http_client.h>
+
+#include "flb_fuzz_header.h"
+
+extern int fuzz_process_data(struct flb_http_client *c);
+extern int fuzz_check_connection(struct flb_http_client *c);
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+
+ struct flb_upstream *u;
+ struct flb_connection *u_conn = NULL;
+ struct flb_http_client *c;
+ struct flb_config *config;
+ char *uri = NULL;
+
+ if (size < 160) {
+ return 0;
+ }
+
+ config = flb_config_init();
+ if (config == NULL) {
+ return 0;
+ }
+
+ u = flb_upstream_create(config, "127.0.0.1", 8001, 0, NULL);
+
+ u_conn = flb_connection_create(-1,
+ FLB_TRANSPORT_TCP,
+ (void *) u,
+ NULL,
+ NULL);
+
+ if (u_conn == NULL) {
+ return 0;
+ }
+
+ char *proxy = NULL;
+ if (GET_MOD_EQ(2,1)) {
+ proxy = get_null_terminated(50, &data, &size);
+ }
+
+ uri = get_null_terminated(20, &data, &size);
+
+ int method = (int)data[0];
+ c = flb_http_client(u_conn, method, uri, NULL, 0,
+ "127.0.0.1", 8001, proxy, 0);
+ if (c != NULL) {
+ char *null_terminated = get_null_terminated(30, &data, &size);
+
+ /* Perform a set of operations on the http_client */
+ flb_http_basic_auth(c, null_terminated, null_terminated);
+ flb_http_set_content_encoding_gzip(c);
+ flb_http_set_keepalive(c);
+ flb_http_strip_port_from_host(c);
+ flb_http_allow_duplicated_headers(c, 0);
+
+ flb_http_buffer_size(c, (*(size_t *)data) & 0xfff);
+ MOVE_INPUT(4)
+ flb_http_add_header(c, "User-Agent", 10, "Fluent-Bit", 10);
+ flb_http_add_header(c, (char*)data, size, "Fluent-Bit", 10);
+ flb_http_buffer_size(c, (int)data[0]);
+ MOVE_INPUT(1)
+ flb_http_buffer_available(c);
+
+ size_t b_sent;
+ flb_http_do(c, &b_sent);
+
+ size_t out_size = 0;
+ flb_http_buffer_increase(c, (*(size_t *)data) & 0xfff, &out_size);
+ MOVE_INPUT(4)
+
+ /* Now we need to simulate the reading of data */
+ c->resp.status = 200;
+
+ if (c->resp.data != NULL) {
+ flb_free(c->resp.data);
+ }
+
+ char *new_nulltm = get_null_terminated(30, &data, &size);
+ c->resp.data_len = 30;
+ c->resp.data = new_nulltm;
+ fuzz_process_data(c);
+ fuzz_check_connection(c);
+
+ flb_http_client_destroy(c);
+ flb_free(null_terminated);
+ }
+
+ /* Now try the http_client_proxy_connect function. */
+ flb_http_client_proxy_connect(u_conn);
+
+ flb_connection_destroy(u_conn);
+ flb_upstream_destroy(u);
+ flb_config_exit(config);
+ if (uri != NULL) {
+ flb_free(uri);
+ }
+ if (proxy != NULL) {
+ flb_free(proxy);
+ }
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/input_fuzzer.c b/fluent-bit/tests/internal/fuzzers/input_fuzzer.c
new file mode 100644
index 00000000..57458d36
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/input_fuzzer.c
@@ -0,0 +1,164 @@
+#include <fluent-bit.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <fluent-bit/flb_input_chunk.h>
+#include <fluent-bit/flb_storage.h>
+#include <fluent-bit/flb_router.h>
+#include <fluent-bit/flb_time.h>
+
+#include "chunkio/chunkio.h"
+#include "flb_fuzz_header.h"
+
+
+const char *input_chunk_property_keywords[] = {
+ "log_suppress_interval",
+ "routable",
+ "alias",
+ "mem_buf_limit",
+ "listen",
+ "log_level",
+ "host",
+ "port",
+ "ipv6",
+ "net.",
+ "tls",
+ "tls.verify",
+ "tls.debug",
+ "tls.ca_path",
+ "tls.key_file",
+ "tls.vhost",
+ "tls.ca_file",
+ "tls.crt_file",
+ "tls.key_passwd",
+ "threaded",
+ "storage.type",
+};
+
+int LLVMFuzzerTestOneInput(const uint8_t *data3, size_t size3)
+{
+ int i;
+ int ret;
+ int in_ffd;
+ int out_ffd;
+
+ flb_ctx_t *ctx;
+ size_t total_bytes;
+ struct flb_input_instance *i_ins;
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct flb_input_chunk *ic;
+ struct flb_task *task;
+
+ if (size3 < 60) {
+ return 0;
+ }
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_p = 0;
+ flb_malloc_mod = 25000;
+ char *input_buffer1 = get_null_terminated(30, &data3, &size3);
+ if (input_buffer1 == NULL) {
+ return 0;
+ }
+ size_t input_buffer1_len = strlen(input_buffer1);
+
+ char *input_buffer2 = get_null_terminated(10, &data3, &size3);
+ if (input_buffer2 == NULL) {
+ return 0;
+ }
+ size_t input_buffer_len2 = strlen(input_buffer2);
+
+ char *input_buffer3 = get_null_terminated(10, &data3, &size3);
+ if (input_buffer3 == NULL) {
+ return 0;
+ }
+ size_t input_buffer_len3 = strlen(input_buffer3);
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+
+ /* create chunks in /tmp folder */
+ ret = flb_service_set(ctx,
+ "flush", "2", "grace", "1",
+ "storage.path", "/tmp/input-chunk-test/",
+ "Log_Level", "error",
+ NULL);
+ if (ret != 0) {
+ flb_free(input_buffer1);
+ flb_free(input_buffer2);
+ flb_free(input_buffer3);
+ return 0;
+ }
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ ret = flb_input_set(ctx, in_ffd,
+ "tag", "test",
+ "storage.type", "filesystem",
+ NULL);
+ if (ret != 0) {
+ flb_free(input_buffer1);
+ flb_free(input_buffer2);
+ flb_free(input_buffer3);
+ return 0;
+ }
+
+ /* an invalid output destination */
+ out_ffd = flb_output(ctx, (char *) "http", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "Host", "127.0.0.1",
+ "Port", "1",
+ "storage.total_limit_size", "1K",
+ NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ if (ret != 0) {
+ flb_free(input_buffer1);
+ flb_free(input_buffer2);
+ flb_free(input_buffer3);
+ return 0;
+ }
+
+ i_ins = mk_list_entry_first(&ctx->config->inputs,
+ struct flb_input_instance,
+ _head);
+
+ /* main fuzzing logic */
+ flb_input_set_property(i_ins, input_buffer2, input_buffer3);
+ for (int i = 0; i < sizeof(input_chunk_property_keywords)/sizeof(char*); i++) {
+ flb_input_set_property(i_ins,
+ input_chunk_property_keywords[i],
+ input_buffer3);
+ }
+
+ /* Ingest fuzz data sample */
+ for (i = 0; i < 2; ++i) {
+ flb_lib_push(ctx, in_ffd, (char *) input_buffer1, input_buffer1_len);
+ sleep(1);
+ total_bytes = flb_input_chunk_total_size(i_ins);
+ ret = total_bytes > 1000 ? -1 : 0;
+ }
+
+ /* FORCE clean up test tasks */
+ mk_list_foreach_safe(head, tmp, &i_ins->tasks) {
+ task = mk_list_entry(head, struct flb_task, _head);
+ flb_info("[task] cleanup test task");
+ flb_task_destroy(task, FLB_TRUE);
+ }
+
+ /* clean up test chunks */
+ mk_list_foreach_safe(head, tmp, &i_ins->chunks) {
+ ic = mk_list_entry(head, struct flb_input_chunk, _head);
+ flb_input_chunk_destroy(ic, FLB_TRUE);
+ }
+ flb_free(input_buffer1);
+ flb_free(input_buffer2);
+ flb_free(input_buffer3);
+
+ flb_time_msleep(200);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
diff --git a/fluent-bit/tests/internal/fuzzers/local_test.c b/fluent-bit/tests/internal/fuzzers/local_test.c
new file mode 100644
index 00000000..309f66b7
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/local_test.c
@@ -0,0 +1,85 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2020 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_log.h>
+#include <fluent-bit/flb_mem.h>
+#include <monkey/mk_core.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+/* declare external function test */
+int LLVMFuzzerTestOneInput(unsigned char *data, size_t size);
+
+int main(int argc, char **argv)
+{
+ int i;
+ int ret;
+ FILE *fp;
+ char *buffer;
+ long bytes;
+ struct stat st;
+
+ if (argc < 2) {
+ flb_error("usage: %s TESTCASE_FILE", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Validate the file */
+ ret = stat(argv[1], &st);
+ if (ret == -1) {
+ flb_errno();
+ flb_error("cannot stat(2) testcase file '%s'", argv[1]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!(fp = fopen(argv[1], "rb"))) {
+ flb_errno();
+ flb_error("cannot fopen(2) testcase file '%s'", argv[1]);
+ return -1;
+ }
+
+ buffer = flb_malloc(st.st_size);
+ if (!buffer) {
+ flb_errno();
+ return -1;
+ }
+
+ bytes = fread(buffer, st.st_size, 1, fp);
+ if (bytes < 1) {
+ fclose(fp);
+ flb_free(buffer);
+ return -1;
+ }
+ fclose(fp);
+
+ /* Invoke the fuzzer entry-point function */
+ for (i = 0; i < 100; i++) {
+ ret = LLVMFuzzerTestOneInput((unsigned char *) buffer, st.st_size);
+ }
+ flb_free(buffer);
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/msgpack_parse_fuzzer.c b/fluent-bit/tests/internal/fuzzers/msgpack_parse_fuzzer.c
new file mode 100644
index 00000000..cbf2ecf1
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/msgpack_parse_fuzzer.c
@@ -0,0 +1,30 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <msgpack.h>
+#include <fluent-bit/flb_pack.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size){
+ /* Set flb_malloc_mod to be fuzzer-data dependent */
+ if (size < 4) {
+ return 0;
+ }
+ flb_malloc_p = 0;
+ flb_malloc_mod = *(int*)data;
+ data += 4;
+ size -= 4;
+
+ /* Avoid division by zero for modulo operations */
+ if (flb_malloc_mod == 0) {
+ flb_malloc_mod = 1;
+ }
+
+ if (size != 512)
+ return 0;
+
+ /* target the conversion of raw msgpack to json */
+ flb_sds_t record;
+ record = flb_msgpack_raw_to_json_sds(data, size);
+ flb_sds_destroy(record);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/msgpack_to_gelf_fuzzer.c b/fluent-bit/tests/internal/fuzzers/msgpack_to_gelf_fuzzer.c
new file mode 100644
index 00000000..cf57437b
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/msgpack_to_gelf_fuzzer.c
@@ -0,0 +1,25 @@
+#include <stdint.h>
+#include <string.h>
+#include <msgpack.h>
+#include <fluent-bit/flb_pack.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size){
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_mod = 25000;
+ flb_malloc_p = 0;
+ if (size != 512)
+ return 0;
+
+ /* Target the conversion of raw msgpack to gelf */
+ flb_sds_t record;
+ struct flb_time tm = {0};
+ struct flb_gelf_fields fields = {0};
+ fields.short_message_key = flb_sds_create("AAAAAAAAAA");
+ record = flb_msgpack_raw_to_gelf((char*)data, size, &tm, &fields);
+
+ /* cleanup */
+ flb_sds_destroy(record);
+ flb_sds_destroy(fields.short_message_key);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/multiline_fuzzer.c b/fluent-bit/tests/internal/fuzzers/multiline_fuzzer.c
new file mode 100644
index 00000000..bc03e246
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/multiline_fuzzer.c
@@ -0,0 +1,180 @@
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/multiline/flb_ml.h>
+#include <fluent-bit/multiline/flb_ml_parser.h>
+#include <fluent-bit/multiline/flb_ml_rule.h>
+
+#include "flb_fuzz_header.h"
+
+static int flush_callback(struct flb_ml_parser *parser,
+ struct flb_ml_stream *mst, void *data, char *buf_data,
+ size_t buf_size) {
+ return 0;
+}
+
+struct record_check {
+ char *buf;
+};
+
+struct expected_result {
+ int current_record;
+ char *key;
+ struct record_check *out_records;
+};
+
+char *random_strings[4];
+
+void test_multiline_parser(msgpack_object *root2, int rand_val) {
+ struct expected_result res = {0};
+ struct flb_config *config = NULL;
+
+ config = flb_config_init();
+
+ struct flb_ml *ml = NULL;
+ ml = flb_ml_create(config, "fuzz-test");
+
+ if (ml != NULL) {
+ uint64_t stream_ids[5];
+
+ flb_ml_parser_instance_create(ml, "docker");
+ flb_ml_parser_instance_create(ml, "python");
+ flb_ml_parser_instance_create(ml, "go");
+ flb_ml_parser_instance_create(ml, "cri");
+ struct flb_ml_parser_ins *mlp_i =
+ flb_ml_parser_instance_create(ml, "java");
+ flb_ml_parser_instance_set(mlp_i, "key_content", "log");
+
+ if (rand_val & 0x01) {
+ flb_ml_stream_create(ml, "java", -1, flush_callback, (void *)&res,
+ &(stream_ids[0]));
+ }
+ if (rand_val >> 1 & 0x01) {
+ flb_ml_stream_create(ml, "python", -1, flush_callback, (void *)&res,
+ &(stream_ids[1]));
+ }
+ if (rand_val >> 2 & 0x01) {
+ flb_ml_stream_create(ml, "go", -1, flush_callback, (void *)&res,
+ &(stream_ids[2]));
+ }
+ if (rand_val >> 3 & 0x01) {
+ flb_ml_stream_create(ml, "docker", -1, flush_callback, (void *)&res,
+ &(stream_ids[3]));
+ }
+ if (rand_val >> 4 & 0x01) {
+ flb_ml_stream_create(ml, "cri", -1, flush_callback, (void *)&res,
+ &(stream_ids[4]));
+ }
+
+ /* Target with msgpack object */
+ if (root2 != NULL) {
+ struct flb_time tm;
+ flb_time_get(&tm);
+ for (int i = 0; i < 4; i++) {
+ flb_ml_append_object(ml, stream_ids[i], &tm, NULL, root2);
+ }
+ }
+
+ /* Target with raw text */
+ struct flb_time tm2;
+ flb_time_get(&tm2);
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 5; j++) {
+ if (random_strings[i] != NULL && stream_ids[j] != NULL) {
+ /* stream_ids index by j, random_strings index by i */
+ flb_ml_append_text(ml, stream_ids[j], &tm2,
+ random_strings[i], strlen(random_strings[i]));
+ flb_ml_append_text(ml, stream_ids[j], &tm2,
+ random_strings[i], strlen(random_strings[i]));
+ flb_ml_append_text(ml, stream_ids[j], &tm2,
+ random_strings[i], strlen(random_strings[i]));
+ flb_ml_append_text(ml, stream_ids[j], &tm2,
+ random_strings[i],strlen(random_strings[i]));
+ flb_ml_append_text(ml, stream_ids[j], &tm2,
+ random_strings[i], strlen(random_strings[i]));
+ }
+ }
+ }
+ }
+
+ flb_ml_flush_pending_now(ml);
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ TIMEOUT_GUARD
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_mod = 25000;
+ flb_malloc_p = 0;
+ /* Ensure there's enough data */
+ if (size < 250) {
+ return 0;
+ }
+
+ int rand_val = *(int *)data;
+ data += 4;
+ size -= 4;
+ for (int i = 0; i < 4; i++) {
+ random_strings[i] = NULL;
+ }
+
+ random_strings[0] = get_null_terminated(40, &data, &size);
+ random_strings[1] = get_null_terminated(40, &data, &size);
+ random_strings[2] = get_null_terminated(40, &data, &size);
+ random_strings[3] = get_null_terminated(40, &data, &size);
+
+ char *out_buf = NULL;
+ size_t out_size;
+ int root_type;
+ int ret =
+ flb_pack_json((char *)data, size, &out_buf, &out_size, &root_type, NULL);
+ if (ret == 0) {
+ size_t off = 0;
+ msgpack_unpacked result;
+ msgpack_unpacked_init(&result);
+ int ret2 = msgpack_unpack_next(&result, out_buf, out_size, &off);
+ if (ret2 == MSGPACK_UNPACK_SUCCESS) {
+ msgpack_object root = result.data;
+
+ /* Pass fuzz data into the multiline parser code */
+ test_multiline_parser(&root, rand_val);
+ }
+ msgpack_unpacked_destroy(&result);
+ free(out_buf);
+ } else {
+ test_multiline_parser(NULL, rand_val);
+ }
+
+ for (int i = 0; i < 4; i++) {
+ if (random_strings[i] != NULL) {
+ free(random_strings[i]);
+ }
+ }
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/pack_json_state_fuzzer.c b/fluent-bit/tests/internal/fuzzers/pack_json_state_fuzzer.c
new file mode 100644
index 00000000..4549557a
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/pack_json_state_fuzzer.c
@@ -0,0 +1,25 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <fluent-bit/flb_pack.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size){
+ int out_size= 0;
+ char *out_buf = NULL;
+ struct flb_pack_state state;
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_mod = 25000;
+ flb_malloc_p = 0;
+
+ /* Exit early to avoid timeouts due to excessive size */
+ if (size > 4096)
+ return 0;
+
+ /* Target json packer */
+ flb_pack_state_init(&state);
+ flb_pack_json_state(data, size, &out_buf, &out_size, &state);
+ flb_pack_state_reset(&state);
+ if (out_buf != NULL)
+ flb_free(out_buf);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/parse_json_fuzzer.c b/fluent-bit/tests/internal/fuzzers/parse_json_fuzzer.c
new file mode 100644
index 00000000..dab8880d
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/parse_json_fuzzer.c
@@ -0,0 +1,69 @@
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_parser.h>
+#include "flb_fuzz_header.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size){
+ TIMEOUT_GUARD
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ struct flb_config *fuzz_config;
+ struct flb_parser *fuzz_parser;
+
+ /* Set flb_malloc_mod to be fuzzer-data dependent */
+ if (size < 4) {
+ return 0;
+ }
+ flb_malloc_p = 0;
+ flb_malloc_mod = *(int*)data;
+ data += 4;
+ size -= 4;
+
+ /* Avoid division by zero for modulo operations */
+ if (flb_malloc_mod == 0) {
+ flb_malloc_mod = 1;
+ }
+
+ /* json parser */
+ fuzz_config = flb_config_init();
+ if (fuzz_config == NULL) {
+ return 0;
+ }
+
+ fuzz_parser = flb_parser_create("fuzzer", "json", NULL, FLB_TRUE, NULL,
+ NULL, NULL, MK_FALSE, MK_TRUE, FLB_FALSE,
+ NULL, 0, NULL, fuzz_config);
+ if (fuzz_parser) {
+ flb_parser_do(fuzz_parser, (char*)data, size,
+ &out_buf, &out_size, &out_time);
+
+ if (out_buf != NULL) {
+ free(out_buf);
+ }
+
+ flb_parser_destroy(fuzz_parser);
+ }
+ flb_config_exit(fuzz_config);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/parse_logfmt_fuzzer.c b/fluent-bit/tests/internal/fuzzers/parse_logfmt_fuzzer.c
new file mode 100644
index 00000000..c65a2adb
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/parse_logfmt_fuzzer.c
@@ -0,0 +1,50 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_parser.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size){
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ struct flb_config *fuzz_config;
+ struct flb_parser *fuzz_parser;
+
+ /* Set flb_malloc_mod to be fuzzer-data dependent */
+ if (size < 4) {
+ return 0;
+ }
+ flb_malloc_p = 0;
+ flb_malloc_mod = *(int*)data;
+ data += 4;
+ size -= 4;
+
+ /* Avoid division by zero for modulo operations */
+ if (flb_malloc_mod == 0) {
+ flb_malloc_mod = 1;
+ }
+
+ /* logfmt parser */
+ fuzz_config = flb_config_init();
+ if (fuzz_config == NULL) {
+ return 0;
+ }
+ fuzz_parser = flb_parser_create("fuzzer", "logfmt", NULL, FLB_TRUE,
+ NULL, NULL, NULL, MK_FALSE,
+ MK_TRUE, FLB_FALSE, NULL, 0, NULL,
+ fuzz_config);
+ if (fuzz_parser) {
+ flb_parser_do(fuzz_parser, (char*)data, size,
+ &out_buf, &out_size, &out_time);
+
+ if (out_buf != NULL) {
+ free(out_buf);
+ }
+ flb_parser_destroy(fuzz_parser);
+ }
+
+ flb_config_exit(fuzz_config);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/parse_ltsv_fuzzer.c b/fluent-bit/tests/internal/fuzzers/parse_ltsv_fuzzer.c
new file mode 100644
index 00000000..cb8bf95a
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/parse_ltsv_fuzzer.c
@@ -0,0 +1,35 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_parser.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size){
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ struct flb_config *fuzz_config;
+ struct flb_parser *fuzz_parser;
+
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_mod = 25000;
+ flb_malloc_p = 0;
+
+ /* ltsvc parser */
+ fuzz_config = flb_config_init();
+ fuzz_parser = flb_parser_create("fuzzer", "ltsv", NULL, FLB_TRUE,
+ NULL, NULL, NULL, MK_FALSE,
+ MK_TRUE, FLB_FALSE, NULL, 0, NULL,
+ fuzz_config);
+ flb_parser_do(fuzz_parser, (char*)data, size,
+ &out_buf, &out_size, &out_time);
+
+ if (out_buf != NULL) {
+ free(out_buf);
+ }
+
+ flb_parser_destroy(fuzz_parser);
+ flb_config_exit(fuzz_config);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/parser_fuzzer.c b/fluent-bit/tests/internal/fuzzers/parser_fuzzer.c
new file mode 100644
index 00000000..a0355551
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/parser_fuzzer.c
@@ -0,0 +1,203 @@
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fluent-bit/flb_utils.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_parser_decoder.h>
+
+#include "flb_fuzz_header.h"
+
+#define TYPES_LEN 5
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ TIMEOUT_GUARD
+
+ char *format = NULL;
+ char *time_fmt = NULL;
+ char *time_key = NULL;
+ char *time_offset = NULL;
+ char *pregex = NULL;
+ struct flb_parser_types *types = NULL;
+ struct flb_config *fuzz_config = NULL;
+ struct flb_parser *fuzz_parser = NULL;
+ int time_keep = 0;
+ int types_len = 0;
+
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_mod = 25000;
+ flb_malloc_p = 0;
+
+ if (size < 100) {
+ return 0;
+ }
+
+ /* json parser */
+ fuzz_config = flb_config_init();
+
+ /* format + pregex */
+ if (GET_MOD_EQ(4,0)) {
+ format = "json";
+ }
+ else if (GET_MOD_EQ(4,1)) {
+ format = "regex";
+#ifdef PREG_FUZZ
+ pregex = malloc(30);
+ pregex[29] = '\0';
+ memcpy(pregex, data, 29);
+ data += 29;
+ size -= 29;
+#else
+ pregex = "^(?<INT>[^ ]+) (?<FLOAT>[^ ]+) (?<BOOL>[^ ]+) (?<STRING>.+)$";
+#endif
+ }
+ else if (GET_MOD_EQ(4,2)) {
+ format = "ltsv";
+ }
+ else {
+ format = "logfmt";
+ }
+ MOVE_INPUT(1);
+
+ /* time_fmt */
+ if (GET_MOD_EQ(2,1)) {
+ time_fmt = get_null_terminated(15, &data, &size);
+ }
+ MOVE_INPUT(1);
+
+ /* time_key */
+ if (GET_MOD_EQ(2,1)) {
+ time_key = get_null_terminated(15, &data, &size);
+ }
+ MOVE_INPUT(1);
+
+ /* time_offset */
+ if (GET_MOD_EQ(2,1)) {
+ time_offset = get_null_terminated(15, &data, &size);
+ }
+ MOVE_INPUT(1);
+
+ /* time_keep */
+ time_keep = (GET_MOD_EQ(2,1)) ? MK_TRUE : MK_FALSE;
+ MOVE_INPUT(1);
+
+ /* types_str */
+ if (GET_MOD_EQ(2,1)) {
+ types = flb_malloc(sizeof(struct flb_parser_types) * TYPES_LEN);
+ char *parser_type_keys[5] = {"AAA", "BBB", "CCC", "DDD", "EEE" };
+ int parser_types[5] = {FLB_PARSER_TYPE_INT, FLB_PARSER_TYPE_FLOAT,
+ FLB_PARSER_TYPE_BOOL, FLB_PARSER_TYPE_STRING,
+ FLB_PARSER_TYPE_HEX};
+ for (int i = 0; i < TYPES_LEN; i++) {
+ types[i].key = strdup(parser_type_keys[i]);
+ types[i].key_len = strlen(parser_type_keys[i]);
+ types[i].type = parser_types[i];
+ }
+ types_len = TYPES_LEN;
+ }
+ MOVE_INPUT(1);
+
+ /* decoders */
+ struct mk_list *list = NULL;
+ if (GET_MOD_EQ(2,1)) {
+ MOVE_INPUT(1);
+ list = flb_malloc(sizeof(struct mk_list));
+ mk_list_init(list);
+
+ struct flb_parser_dec *dec = malloc(sizeof(struct flb_parser_dec));
+ dec->key = flb_sds_create_len("AAA", 3);
+ dec->buffer = flb_sds_create_size(FLB_PARSER_DEC_BUF_SIZE);
+ dec->add_extra_keys = FLB_TRUE;
+ mk_list_init(&dec->rules);
+ mk_list_add(&dec->_head, list);
+
+ struct flb_parser_dec_rule *dec_rule = malloc(sizeof(struct flb_parser_dec_rule));
+ dec_rule->type = (int)(data[0] % 0x02);
+ MOVE_INPUT(1);
+ dec_rule->backend = (int)(data[0] % 0x04);
+ MOVE_INPUT(1);
+ dec_rule->action = (int)data[0] % 0x03;
+ mk_list_add(&dec_rule->_head, &dec->rules);
+
+ if (GET_MOD_EQ(2,1)) {
+ struct flb_parser_dec_rule *dec_rule2 = malloc(sizeof(struct flb_parser_dec_rule));
+ dec_rule2->type = (int)(data[0] % 0x02);
+ MOVE_INPUT(1);
+ dec_rule2->backend = (int)(data[0] % 0x04);
+ MOVE_INPUT(1);
+ dec_rule->action = (int)data[0] % 0x03;
+ mk_list_add(&dec_rule2->_head, &dec->rules);
+ }
+ }
+ MOVE_INPUT(1);
+ /* print our config struct */
+ flb_utils_print_setup(fuzz_config);
+
+ /* now call into the parser */
+ fuzz_parser = flb_parser_create("fuzzer", format, pregex, FLB_TRUE,
+ time_fmt, time_key, time_offset, time_keep, 0, FLB_FALSE,
+ types, types_len, list, fuzz_config);
+
+ /* Second step is to use the random parser to parse random input */
+ if (fuzz_parser != NULL) {
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ flb_parser_do(fuzz_parser, (char*)data, size,
+ &out_buf, &out_size, &out_time);
+ if (out_buf != NULL) {
+ free(out_buf);
+ }
+ flb_parser_destroy(fuzz_parser);
+ }
+ else {
+ /* Parser creation failed but we still need to clean
+ * up types and decoders */
+ if (types != NULL) {
+ for (int i=0; i< TYPES_LEN; i++){
+ flb_free(types[i].key);
+ }
+ flb_free(types);
+ }
+ if (list != NULL) {
+ flb_parser_decoder_list_destroy(list);
+ }
+ }
+
+ /* Cleanup everything but the parser */
+ flb_config_exit(fuzz_config);
+ if (time_fmt != NULL) {
+ flb_free(time_fmt);
+ }
+ if (time_key != NULL) {
+ flb_free(time_key);
+ }
+ if (time_offset != NULL) {
+ flb_free(time_offset);
+ }
+#ifdef PREG_FUZZ
+ if (pregex != NULL) {
+ flb_free(pregex);
+ }
+#endif
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/record_ac_fuzzer.c b/fluent-bit/tests/internal/fuzzers/record_ac_fuzzer.c
new file mode 100644
index 00000000..9f2ae580
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/record_ac_fuzzer.c
@@ -0,0 +1,101 @@
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_error.h>
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_record_accessor.h>
+#include <fluent-bit/record_accessor/flb_ra_parser.h>
+#include <msgpack.h>
+#include "flb_fuzz_header.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ /* Limit size to 32KB */
+ if (size > 32768 || size < 6) {
+ return 0;
+ }
+
+ char *outbuf = NULL;
+ char *ra_str = NULL;
+ size_t outsize;
+ int type;
+ int len;
+ size_t off = 0;
+ msgpack_object map;
+
+ /* Set flb_malloc_mod to be fuzzer-data dependent */
+ flb_malloc_p = 0;
+ flb_malloc_mod = *(int*)data;
+ data += 4;
+ size -= 4;
+
+ /* Avoid division by zero for modulo operations */
+ if (flb_malloc_mod == 0) {
+ flb_malloc_mod = 1;
+ }
+
+ if (size < 100) {
+ return 0;
+ }
+
+ struct flb_record_accessor *ra = NULL;
+
+ /* Sample JSON message */
+ len = 60;
+ char *json_raw = get_null_terminated(len, &data, &size);
+
+ /* Convert to msgpack */
+ int ret = flb_pack_json(json_raw, len, &outbuf, &outsize, &type, NULL);
+ if (ret == -1) {
+ flb_free(json_raw);
+ return 0;
+ }
+ flb_free(json_raw);
+
+ char *null_terminated = get_null_terminated(size, &data, &size);
+
+ ra_str = flb_sds_create(null_terminated);
+ if (ra_str != NULL) {
+ ra = flb_ra_create(ra_str, FLB_FALSE);
+ if (!ra) {
+ flb_sds_destroy(ra_str);
+ flb_free(null_terminated);
+ flb_free(outbuf);
+ return 0;
+ }
+
+ flb_ra_is_static(ra);
+
+ msgpack_unpacked result;
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, outbuf, outsize, &off);
+ map = result.data;
+
+ flb_sds_t str = flb_ra_translate(ra, NULL, -1, map, NULL);
+ if (!str) {
+ flb_ra_destroy(ra);
+ flb_sds_destroy(ra_str);
+ msgpack_unpacked_destroy(&result);
+
+ /* General cleanup */
+ flb_free(null_terminated);
+ flb_free(outbuf);
+ return 0;
+ }
+ flb_ra_dump(ra);
+
+
+ flb_sds_destroy(str);
+ flb_ra_destroy(ra);
+ flb_sds_destroy(ra_str);
+ msgpack_unpacked_destroy(&result);
+ }
+
+ if (outbuf != NULL) {
+ flb_free(outbuf);
+ }
+ if (null_terminated != NULL) {
+ flb_free(null_terminated);
+ }
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/signv4_fuzzer.c b/fluent-bit/tests/internal/fuzzers/signv4_fuzzer.c
new file mode 100644
index 00000000..4393d159
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/signv4_fuzzer.c
@@ -0,0 +1,111 @@
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_http_client.h>
+#include <fluent-bit/flb_upstream.h>
+#include <fluent-bit/flb_signv4.h>
+#include <fluent-bit/flb_aws_credentials.h>
+#include <monkey/mk_core.h>
+#include <unistd.h>
+#include <fluent-bit/flb_sds.h>
+#include "flb_fuzz_header.h"
+
+#define AWS_ACCESS_KEY_ID "AWS_ACCESS_KEY_ID"
+#define AWS_SECRET_ACCESS_KEY "AWS_SECRET_ACCESS_KEY"
+#define AWS_SESSION_TOKEN "AWS_SESSION_TOKEN"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ if (size < 59) {
+ return 0;
+ }
+
+ /* Set flb_malloc_mod to be fuzzer-data dependent */
+ flb_malloc_p = 0;
+ flb_malloc_mod = *(int*)data;
+ data += 4;
+ size -= 4;
+
+ /* Avoid division by zero for modulo operations */
+ if (flb_malloc_mod == 0) {
+ flb_malloc_mod = 1;
+ }
+
+ char s3_mode = data[0];
+ MOVE_INPUT(1)
+ int method = (int)data[0];
+
+ /* Prepare a general null-terminated string */
+ char *uri = get_null_terminated(50, &data, &size);
+ char *null_terminated = get_null_terminated(size, &data, &size);
+
+ /* Now begin the core work of the fuzzer */
+ struct flb_config *config;
+ struct mk_list *tests;
+ struct flb_aws_provider *provider;
+ config = flb_calloc(1, sizeof(struct flb_config));
+ if (!config) {
+ flb_free(uri);
+ flb_free(null_terminated);
+ return 0;
+ }
+ mk_list_init(&config->upstreams);
+ provider = flb_aws_env_provider_create();
+
+ /* Create the necessary http context */
+ struct flb_upstream *http_u;
+ struct flb_connection *http_u_conn = NULL;
+ struct flb_http_client *http_c;
+ struct flb_config *http_config;
+
+ http_config = flb_config_init();
+ if (http_config == NULL) {
+ flb_aws_provider_destroy(provider);
+ flb_free(uri);
+ flb_free(null_terminated);
+ flb_free(config);
+ return 0;
+ }
+
+ http_u = flb_upstream_create(http_config, "127.0.0.1", 8001, 0, NULL);
+ if (http_u != NULL) {
+ http_u_conn = flb_calloc(1, sizeof(struct flb_connection));
+ if (http_u_conn != NULL) {
+ http_u_conn->upstream = http_u;
+
+ http_c = flb_http_client(http_u_conn, method, uri,
+ null_terminated, size, "127.0.0.1", 8001, NULL, 0);
+ if (http_c) {
+ /* Call into the main target flb_signv4_do*/
+ time_t t = 1440938160;
+ char *region = "us-east-1";
+ char *access_key = "AKIDEXAMPLE";
+ char *service = "service";
+ char *secret_key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY";
+ int ret = setenv(AWS_ACCESS_KEY_ID, access_key, 1);
+ if (ret >= 0) {
+ ret = setenv(AWS_SECRET_ACCESS_KEY, secret_key, 1);
+ if (ret >= 0) {
+ flb_sds_t signature = flb_signv4_do(http_c, FLB_TRUE, FLB_FALSE,
+ t, region, service, s3_mode, NULL, provider);
+ if (signature) {
+ flb_sds_destroy(signature);
+ }
+ }
+ }
+ flb_http_client_destroy(http_c);
+ }
+ }
+ flb_upstream_destroy(http_u);
+ }
+
+ /* Cleanup */
+ flb_config_exit(http_config);
+ flb_aws_provider_destroy(provider);
+ flb_free(config);
+
+ flb_free(null_terminated);
+ flb_free(http_u_conn);
+ flb_free(uri);
+
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/strp_fuzzer.c b/fluent-bit/tests/internal/fuzzers/strp_fuzzer.c
new file mode 100644
index 00000000..3de21c7a
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/strp_fuzzer.c
@@ -0,0 +1,35 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_parser.h>
+#include <msgpack.h>
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_utils.h>
+#include <fluent-bit/flb_version.h>
+#include <fluent-bit/flb_strptime.h>
+
+#include "flb_fuzz_header.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ if (size < 40) {
+ return 0;
+ }
+
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_mod = 25000;
+ flb_malloc_p = 0;
+
+ char *fmt = get_null_terminated(size - 30, &data, &size);
+ char *buf = get_null_terminated(size, &data, &size);
+
+ struct tm tt;
+ flb_strptime(buf, fmt, &tt);
+
+ flb_free(buf);
+ flb_free(fmt);
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/fuzzers/utils_fuzzer.c b/fluent-bit/tests/internal/fuzzers/utils_fuzzer.c
new file mode 100644
index 00000000..2352adf3
--- /dev/null
+++ b/fluent-bit/tests/internal/fuzzers/utils_fuzzer.c
@@ -0,0 +1,242 @@
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <msgpack.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_utils.h>
+#include <fluent-bit/flb_slist.h>
+#include <fluent-bit/flb_gzip.h>
+#include <fluent-bit/flb_hash_table.h>
+#include <fluent-bit/flb_uri.h>
+#include <fluent-bit/flb_hash.h>
+#include <fluent-bit/flb_regex.h>
+#include "flb_fuzz_header.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ TIMEOUT_GUARD
+
+ if (size < 750) {
+ return 0;
+ }
+
+ /* Set fuzzer-malloc chance of failure */
+ flb_malloc_mod = 25000;
+ flb_malloc_p = 0;
+
+ uint64_t ran_hash = *(uint64_t *)data;
+ char *null_terminated1 = get_null_terminated(25, &data, &size);
+ char *null_terminated2 = get_null_terminated(25, &data, &size);
+ char *null_terminated3 = get_null_terminated(25, &data, &size);
+
+ /* Prepare a general null-terminated string */
+ char *null_terminated = (char*)malloc(size+1);
+ null_terminated[size] = '\0';
+ memcpy(null_terminated, data, size);
+
+ /* Fuzzing of flb_utils.c */
+ int sec;
+ long nsec;
+ size_t new_size;
+ char *prot = NULL;
+ char *host = NULL;
+ char *port = NULL;
+ char *uri = NULL;
+ char *new_dst = NULL;
+
+ if (flb_utils_write_str_buf(null_terminated, size, &new_dst, &new_size) == 0) {
+ flb_free(new_dst);
+ }
+
+ struct mk_list *list = flb_utils_split(null_terminated, 'A', 3);
+ if (list != NULL) {
+ flb_utils_split_free(list);
+ }
+ struct mk_list *list2 = flb_utils_split_quoted(null_terminated, 'A', 3);
+ if (list2 != NULL) {
+ flb_utils_split_free(list2);
+ }
+
+ if (flb_utils_url_split(null_terminated, &prot, &host, &port, &uri) == 0) {
+ flb_free(prot);
+ flb_free(port);
+ flb_free(host);
+ flb_free(uri);
+ }
+
+ char *split_protocol = NULL;
+ char *split_username = NULL;
+ char *split_password = NULL;
+ char *split_host = NULL;
+ char *split_port = NULL;
+ if (flb_utils_proxy_url_split(null_terminated, &split_protocol,
+ &split_username, &split_password, &split_host, &split_port) == 0) {
+ if (split_protocol) {
+ flb_free(split_protocol);
+ }
+ if (split_username) {
+ flb_free(split_username);
+ }
+ if (split_password) {
+ flb_free(split_password);
+ }
+ if (split_host) {
+ flb_free(split_host);
+ }
+ if (split_port) {
+ flb_free(split_port);
+ }
+ }
+
+
+ flb_utils_size_to_bytes(null_terminated);
+ flb_utils_time_split(null_terminated, &sec, &nsec);
+ flb_utils_time_to_seconds(null_terminated);
+ flb_utils_bool(null_terminated);
+ flb_utils_hex2int(null_terminated, size);
+
+ /* Fuzzong of flb_uri.c */
+ struct flb_uri *uri2 = NULL;
+ uri2 = flb_uri_create(null_terminated);
+ if (uri2 != NULL) {
+ flb_uri_get(uri2, (int)data[0]);
+ flb_uri_dump(uri2);
+ flb_uri_destroy(uri2);
+ }
+ flb_sds_t encoded = flb_uri_encode((char*)data, size);
+ if (encoded != NULL) {
+ flb_sds_destroy(encoded);
+ }
+
+ /* Fuzzing of flb_hash.c */
+ struct flb_hash_table *ht = NULL;
+ ht = flb_hash_table_create((int)(data[2] % 0x04),
+ (size_t)data[0],
+ (int)data[1]);
+ if (ht != NULL) {
+ flb_hash_table_add(ht, null_terminated, size, null_terminated, size);
+
+ char *out_buf = NULL;
+ size_t out_size;
+ flb_hash_table_get(ht, null_terminated, size, (void **)&out_buf, &out_size);
+
+ /* now let's create some more instances */
+ char *instances1[128] = { NULL };
+ char *instances2[128] = { NULL };
+ for (int i = 0; i < 128; i++) {
+ char *in1 = malloc(3);
+ char *in2 = malloc(3);
+ memcpy(in1, data+(i*4), 2);
+ memcpy(in2, data+(i*4)+2, 2);
+ in1[2] = '\0';
+ in2[2] = '\0';
+ flb_hash_table_add(ht, in1, 2, in2, 2);
+ instances1[i] = in1;
+ instances2[i] = in2;
+ }
+
+ for(int i = 0; i < 20; i++) {
+ char *hash_out_buf;
+ size_t hash_out_size;
+ flb_hash_table_get_by_id(ht, (int)data[i], null_terminated,
+ (const char **)&hash_out_buf, &hash_out_size);
+ }
+
+ flb_hash_table_del(ht, null_terminated1);
+ flb_hash_table_exists(ht, ran_hash);
+ flb_hash_table_del_ptr(ht, null_terminated2, strlen(null_terminated2), NULL);
+ flb_hash_table_get_ptr(ht, null_terminated3, strlen(null_terminated3));
+
+ flb_hash_table_destroy(ht);
+ for (int i =0; i<128; i++) {
+ flb_free(instances1[i]);
+ flb_free(instances2[i]);
+ }
+ }
+
+ /* sds */
+ flb_sds_t fs = flb_sds_create_len((const char*)data, size);
+ if (fs != NULL) {
+ fs = flb_sds_cat_esc(fs, "AAABBBCCC", 9, "ABC", 3);
+ if (fs != NULL) {
+ flb_sds_destroy(fs);
+ }
+ }
+
+ /* Fuzzing of flb_gzip.c */
+ void *str = NULL;
+ size_t len;
+ void *out_data = NULL;
+ size_t out_len;
+ if (flb_gzip_compress((char*)data, size, &str, &len) != -1) {
+ flb_gzip_uncompress(str, len, &out_data, &out_len);
+ }
+ if (str != NULL) {
+ free(str);
+ }
+ if (out_data != NULL) {
+ free(out_data);
+ }
+ void *out_data2 = NULL;
+ size_t out2_len;
+ int uncompress_ret = flb_gzip_uncompress((char*)data, size, &out_data2, &out2_len);
+ if (uncompress_ret != -1 && out_data2 != NULL) {
+ flb_free(out_data2);
+ }
+
+ /* Fuzzing the sha routines */
+ struct flb_hash sha512;
+ uint8_t buf[64];
+
+ flb_hash_init(&sha512, FLB_HASH_SHA512);
+ flb_hash_update(&sha512, (unsigned char *) null_terminated, 32);
+ flb_hash_update(&sha512, (unsigned char *) null_terminated+32, 32);
+ flb_hash_update(&sha512, (unsigned char *) null_terminated+64, 32);
+ flb_hash_finalize(&sha512, buf, sizeof(buf));
+ flb_hash_cleanup(&sha512);
+
+ /* regex */
+ char *pregex = "^(?<INT>[^ ]+) (?<FLOAT>[^ ]+) (?<BOOL>[^ ]+) (?<STRING>.+)$";
+ flb_regex_init();
+ struct flb_regex *freg = flb_regex_create(pregex);
+ if (freg != NULL) {
+ flb_regex_match(freg, (unsigned char*)null_terminated, size);
+ flb_regex_destroy(freg);
+ }
+ flb_regex_exit();
+
+ /* slist */
+ struct mk_list list3;
+ flb_slist_create(&list3);
+ flb_sds_t slist_str = flb_sds_create_len((const char*)data, size);
+ flb_slist_add_sds(&list3, slist_str);
+ flb_slist_entry_get(&list3, 100);
+ flb_slist_dump(&list3);
+ flb_slist_destroy(&list3);
+
+
+ /* General cleanup */
+ flb_free(null_terminated);
+ flb_free(null_terminated1);
+ flb_free(null_terminated2);
+ flb_free(null_terminated3);
+ return 0;
+}
diff --git a/fluent-bit/tests/internal/gelf.c b/fluent-bit/tests/internal/gelf.c
new file mode 100644
index 00000000..0a3b21e6
--- /dev/null
+++ b/fluent-bit/tests/internal/gelf.c
@@ -0,0 +1,101 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_time.h>
+#include <msgpack.h>
+
+#include "flb_tests_internal.h"
+#include <string.h>
+
+#define EXPECTED_OUT \
+ "{\"version\":\"1.1\", \"short_message\":\"true, 2019, str\", \"_t2\":\"false\", \"timestamp\":337647600.1234}"
+
+void test_gelf_pack()
+{
+ msgpack_sbuffer mp_sbuf;
+ msgpack_packer mp_pck;
+ struct flb_time ts;
+ struct flb_gelf_fields fields = {0};
+ flb_sds_t out;
+
+ /* Pack sample msgpack */
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+
+ ts.tm.tv_sec = 337647600;
+ ts.tm.tv_nsec = 1234111111;
+
+ msgpack_pack_map(&mp_pck, 2);
+ msgpack_pack_str(&mp_pck, 2);
+ msgpack_pack_str_body(&mp_pck, "t1", 2);
+ msgpack_pack_array(&mp_pck, 3);
+ msgpack_pack_true(&mp_pck);
+ msgpack_pack_uint64(&mp_pck, 2019);
+ msgpack_pack_str(&mp_pck, 3);
+ msgpack_pack_str_body(&mp_pck, "str", 3);
+ msgpack_pack_str(&mp_pck, 2);
+ msgpack_pack_str_body(&mp_pck, "t2", 2);
+ msgpack_pack_false(&mp_pck);
+
+ fields.short_message_key = flb_sds_create("t1");
+ out = flb_msgpack_raw_to_gelf(mp_sbuf.data, mp_sbuf.size, &ts, &fields);
+ TEST_CHECK(out != NULL);
+
+ TEST_CHECK(strcmp(out, EXPECTED_OUT) == 0);
+ flb_sds_destroy(out);
+ flb_sds_destroy(fields.short_message_key);
+ msgpack_sbuffer_destroy(&mp_sbuf);
+}
+
+#define EXPECTED_OUT_MSEC \
+ "{\"version\":\"1.1\", \"short_message\":\"true, 2019, str\", \"_t2\":\"false\", \"timestamp\":337647600.012}"
+
+/* https://github.com/fluent/fluent-bit/issues/3727 */
+void test_gelf_pack_msec()
+{
+ msgpack_sbuffer mp_sbuf;
+ msgpack_packer mp_pck;
+ struct flb_time ts;
+ struct flb_gelf_fields fields = {0};
+ flb_sds_t out;
+
+ /* Pack sample msgpack */
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+
+ ts.tm.tv_sec = 337647600;
+ ts.tm.tv_nsec = 12341111; /* 12.34msec */
+
+ msgpack_pack_map(&mp_pck, 2);
+ msgpack_pack_str(&mp_pck, 2);
+ msgpack_pack_str_body(&mp_pck, "t1", 2);
+ msgpack_pack_array(&mp_pck, 3);
+ msgpack_pack_true(&mp_pck);
+ msgpack_pack_uint64(&mp_pck, 2019);
+ msgpack_pack_str(&mp_pck, 3);
+ msgpack_pack_str_body(&mp_pck, "str", 3);
+ msgpack_pack_str(&mp_pck, 2);
+ msgpack_pack_str_body(&mp_pck, "t2", 2);
+ msgpack_pack_false(&mp_pck);
+
+ fields.short_message_key = flb_sds_create("t1");
+ out = flb_msgpack_raw_to_gelf(mp_sbuf.data, mp_sbuf.size, &ts, &fields);
+ TEST_CHECK(out != NULL);
+
+ if(!TEST_CHECK(strcmp(out, EXPECTED_OUT_MSEC) == 0)) {
+ TEST_MSG("out=%s", out);
+ }
+ flb_sds_destroy(out);
+ flb_sds_destroy(fields.short_message_key);
+ msgpack_sbuffer_destroy(&mp_sbuf);
+}
+
+TEST_LIST = {
+ {"gelf_pack", test_gelf_pack},
+ {"gelf_pack_msec", test_gelf_pack_msec},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/gzip.c b/fluent-bit/tests/internal/gzip.c
new file mode 100644
index 00000000..a3f82341
--- /dev/null
+++ b/fluent-bit/tests/internal/gzip.c
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_gzip.h>
+
+#include "flb_tests_internal.h"
+
+/* Sample data */
+char *morpheus = "This is your last chance. After this, there is no "
+ "turning back. You take the blue pill - the story ends, you wake up in "
+ "your bed and believe whatever you want to believe. You take the red pill,"
+ "you stay in Wonderland and I show you how deep the rabbit-hole goes.";
+
+void test_compress()
+{
+ int ret;
+ int sample_len;
+ char *in_data = morpheus;
+ size_t in_len;
+ void *str;
+ size_t len;
+
+ sample_len = strlen(morpheus);
+ in_len = sample_len;
+ ret = flb_gzip_compress(in_data, in_len, &str, &len);
+ TEST_CHECK(ret == 0);
+
+ in_data = str;
+ in_len = len;
+
+ ret = flb_gzip_uncompress(in_data, in_len, &str, &len);
+ TEST_CHECK(ret == 0);
+
+ TEST_CHECK(sample_len == len);
+ ret = memcmp(morpheus, str, sample_len);
+ TEST_CHECK(ret == 0);
+
+ flb_free(in_data);
+ flb_free(str);
+}
+
+TEST_LIST = {
+ {"compress", test_compress},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/hash.c b/fluent-bit/tests/internal/hash.c
new file mode 100644
index 00000000..3ba90a65
--- /dev/null
+++ b/fluent-bit/tests/internal/hash.c
@@ -0,0 +1,165 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_hash.h>
+
+#include "flb_tests_internal.h"
+
+#define SHA512_ABCDEF "e32ef19623e8ed9d267f657a81944b3d07adbb768518068e88435745564e8d4150a0a703be2a7d88b61e3d390c2bb97e2d4c311fdc69d6b1267f05f59aa920e7"
+#define SHA512_OFFBYONE "ca8b236e13383f1f2293c9e286376444e99b7f180ba85713f140b55795fd2f8625d8b84201154d7956b74e2a1e0d5fbff1b61c7288c3f45834ad409e7bdfe536"
+
+static size_t get_sample_digest_data(unsigned char ***buffer_list,
+ size_t **length_list,
+ char **precomputed_digest,
+ int *precomputed_digest_type)
+{
+ static const int digest_type = FLB_HASH_SHA512;
+ static size_t lengths[] = {0, 0, 0, 0};
+ static const char *buffers[] = {
+ "This is one ",
+ "of the four ",
+ "buffers that will be passed ",
+ "to the simple batched digest processor function"
+ };
+ static const char *digest = "65cdb3e49288e227eeacc939010f3e98ef5d4f619dfc629c9cd6d2d6c357e534" \
+ "8f69a87a05ad336d7849e8c2ebd6ba68deeed433aa7b876bbaf1bb173366bf88";
+ size_t index;
+
+ if (lengths[0] == 0) {
+ for (index = 0 ; index < 4 ; index++) {
+ lengths[index] = strlen(buffers[index]);
+ }
+ }
+
+ *buffer_list = (unsigned char **) buffers;
+ *length_list = lengths;
+ *precomputed_digest = (char *) digest;
+ *precomputed_digest_type = digest_type;
+
+ return 4;
+}
+
+static void hexlify(uint8_t *hash, char *out)
+{
+ int i;
+ static const char hex[] = "0123456789abcdef";
+ char *buf = out;
+
+ for (i = 0; i < 64; i++) {
+ *buf++ = hex[hash[i] >> 4];
+ *buf++ = hex[hash[i] & 0xf];
+ }
+}
+
+static void test_digest_abcdef()
+{
+ struct flb_hash digest;
+ char dhex[128];
+ uint8_t buf[64];
+
+ flb_hash_init(&digest, FLB_HASH_SHA512);
+ flb_hash_update(&digest, (unsigned char *) "abc", 3);
+ flb_hash_update(&digest, (unsigned char *) "def", 3);
+ flb_hash_finalize(&digest, buf, sizeof(buf));
+ flb_hash_cleanup(&digest);
+
+ hexlify(buf, dhex);
+
+ TEST_CHECK(memcmp(dhex, SHA512_ABCDEF, 128) == 0);
+}
+
+static void test_digest_offbyone()
+{
+ struct flb_hash digest;
+ uint8_t buf[64];
+ char dhex[128];
+
+ flb_hash_init(&digest, FLB_HASH_SHA512);
+ flb_hash_update(&digest, (unsigned char *) "0123456789abcdef0123456789abcdef", 32);
+ flb_hash_update(&digest, (unsigned char *) "0123456789abcdef0123456789abcdef", 32);
+ flb_hash_update(&digest, (unsigned char *) "0123456789abcdef0123456789abcdef", 32);
+ flb_hash_update(&digest, (unsigned char *) "0123456789abcdef0123456789abcde", 31);
+ flb_hash_finalize(&digest, buf, sizeof(buf));
+ flb_hash_cleanup(&digest);
+
+ hexlify(buf, dhex);
+
+ TEST_CHECK(memcmp(dhex, SHA512_OFFBYONE, 128) == 0);
+
+}
+
+static void test_digest_standard()
+{
+ char hex_digest[128];
+ int ref_digest_type;
+ char *ref_hex_digest;
+ uint8_t raw_digest[64];
+ size_t buffer_count;
+ unsigned char **buffer_list;
+ size_t *length_list;
+ struct flb_hash digest;
+ int result;
+ size_t index;
+
+ buffer_count = get_sample_digest_data(&buffer_list, &length_list,
+ &ref_hex_digest, &ref_digest_type);
+
+ TEST_CHECK(buffer_count > 0);
+ TEST_CHECK(ref_digest_type == FLB_HASH_SHA512);
+
+ result = flb_hash_init(&digest, ref_digest_type);
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+
+ for (index = 0 ; index < buffer_count ; index++) {
+ result = flb_hash_update(&digest, buffer_list[index], length_list[index]);
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+ }
+
+ result = flb_hash_finalize(&digest, raw_digest, sizeof(raw_digest));
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+
+ hexlify(raw_digest, hex_digest);
+
+ TEST_CHECK(memcmp(hex_digest, ref_hex_digest, 128) == 0);
+
+ flb_hash_cleanup(&digest);
+}
+
+static void test_digest_simple_batch()
+{
+ char hex_digest[128];
+ int ref_digest_type;
+ char *ref_hex_digest;
+ uint8_t raw_digest[64];
+ size_t buffer_count;
+ unsigned char **buffer_list;
+ size_t *length_list;
+ int result;
+
+ buffer_count = get_sample_digest_data(&buffer_list, &length_list,
+ &ref_hex_digest, &ref_digest_type);
+
+ TEST_CHECK(buffer_count > 0);
+ TEST_CHECK(ref_digest_type == FLB_HASH_SHA512);
+
+ result = flb_hash_simple_batch(ref_digest_type,
+ buffer_count,
+ buffer_list,
+ length_list,
+ raw_digest,
+ sizeof(raw_digest));
+
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+
+ hexlify(raw_digest, hex_digest);
+
+ TEST_CHECK(memcmp(hex_digest, ref_hex_digest, 128) == 0);
+}
+
+TEST_LIST = {
+ { "test_digest_simple_batch", test_digest_simple_batch },
+ { "test_digest_standard", test_digest_standard },
+ { "test_digest_abcdef", test_digest_abcdef },
+ { "test_digest_offbyone", test_digest_offbyone },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/hashtable.c b/fluent-bit/tests/internal/hashtable.c
new file mode 100644
index 00000000..a7066aeb
--- /dev/null
+++ b/fluent-bit/tests/internal/hashtable.c
@@ -0,0 +1,413 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_macros.h>
+#include <fluent-bit/flb_hash_table.h>
+
+#include "flb_tests_internal.h"
+
+struct map {
+ char *key;
+ char *val;
+};
+
+struct map entries[] = {
+ {"key_0", "val_0"},
+ {"key_1", "val_1"}, {"key_2", "val_2"}, {"key_3", "val_3"},
+ {"key_4", "val_4"}, {"key_5", "val_5"}, {"key_6", "val_6"},
+ {"key_7", "val_7"}, {"key_8", "val_8"}, {"key_9", "val_9"},
+ {"key_10", "val_10"}, {"key_11", "val_11"}, {"key_12", "val_12"},
+ {"key_13", "val_13"}, {"key_14", "val_14"}, {"key_15", "val_15"},
+ {"key_16", "val_16"}, {"key_17", "val_17"}, {"key_18", "val_18"},
+ {"key_19", "val_19"}, {"key_20", "val_20"}, {"key_21", "val_21"},
+ {"key_22", "val_22"}, {"key_23", "val_23"}, {"key_24", "val_24"},
+ {"key_25", "val_25"}, {"key_26", "val_26"}, {"key_27", "val_27"},
+ {"key_28", "val_28"}, {"key_29", "val_29"}, {"key_30", "val_30"},
+ {"key_31", "val_31"}, {"key_32", "val_32"}, {"key_33", "val_33"},
+ {"key_34", "val_34"}, {"key_35", "val_35"}, {"key_36", "val_36"},
+ {"key_37", "val_37"}, {"key_38", "val_38"}, {"key_39", "val_39"},
+ {"key_40", "val_40"}, {"key_41", "val_41"}, {"key_42", "val_42"},
+ {"key_43", "val_43"}, {"key_44", "val_44"}, {"key_45", "val_45"},
+ {"key_46", "val_46"}, {"key_47", "val_47"}, {"key_48", "val_48"},
+ {"key_49", "val_49"}, {"key_50", "val_50"}, {"key_51", "val_51"},
+ {"key_52", "val_52"}, {"key_53", "val_53"}, {"key_54", "val_54"},
+ {"key_55", "val_55"}, {"key_56", "val_56"}, {"key_57", "val_57"},
+ {"key_58", "val_58"}, {"key_59", "val_59"}, {"key_60", "val_60"},
+ {"key_61", "val_61"}, {"key_62", "val_62"}, {"key_63", "val_63"},
+ {"key_64", "val_64"}, {"key_65", "val_65"}, {"key_66", "val_66"},
+ {"key_67", "val_67"}, {"key_68", "val_68"}, {"key_69", "val_69"},
+ {"key_70", "val_70"}, {"key_71", "val_71"}, {"key_72", "val_72"},
+ {"key_73", "val_73"}, {"key_74", "val_74"}, {"key_75", "val_75"},
+ {"key_76", "val_76"}, {"key_77", "val_77"}, {"key_78", "val_78"},
+ {"key_79", "val_79"}, {"key_80", "val_80"}, {"key_81", "val_81"},
+ {"key_82", "val_82"}, {"key_83", "val_83"}, {"key_84", "val_84"},
+ {"key_85", "val_85"}, {"key_86", "val_86"}, {"key_87", "val_87"},
+ {"key_88", "val_88"}, {"key_89", "val_89"}, {"key_90", "val_90"},
+ {"key_91", "val_91"}, {"key_92", "val_92"}, {"key_93", "val_93"},
+ {"key_94", "val_94"}, {"key_95", "val_95"}, {"key_96", "val_96"},
+ {"key_97", "val_97"}, {"key_98", "val_98"}, {"key_99", "val_99"},
+
+ /* override some values */
+ {"key_67", "val_AA"}, {"key_68", "val_BB"}, {"key_69", "val_CC"},
+
+};
+
+static int ht_add(struct flb_hash_table *ht, char *key, char *val)
+{
+ int id;
+ int idn;
+ int klen;
+ int vlen;
+ char *out_buf;
+ size_t out_size;
+
+ klen = strlen(key);
+ vlen = strlen(val);
+
+ /* Insert the key value */
+ id = flb_hash_table_add(ht, key, klen, val, vlen);
+ TEST_CHECK(id >=0);
+
+ /* Retrieve the value of the recently added key */
+ idn = flb_hash_table_get(ht, key, klen, (void *) &out_buf, &out_size);
+ TEST_CHECK(idn == id);
+ TEST_CHECK(strcmp(out_buf, val) == 0);
+
+ return id;
+}
+
+void test_create_zero()
+{
+ struct flb_hash_table *ht;
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_NONE, 0, -1);
+ TEST_CHECK(ht == NULL);
+}
+
+/* bug 355 */
+void test_single()
+{
+ int ret;
+ const char *out_buf;
+ size_t out_size;
+ struct flb_hash_table *ht;
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_NONE, 1, -1);
+ TEST_CHECK(ht != NULL);
+
+ ret = ht_add(ht, "key", "value");
+ TEST_CHECK(ret != -1);
+
+ ret = flb_hash_table_get(ht, "key", 3, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ ret = flb_hash_table_get(ht, "NOT", 3, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret == -1);
+
+ flb_hash_table_destroy(ht);
+}
+
+void test_small_table()
+{
+ int i;
+ struct map *m;
+ struct flb_hash_table *ht;
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_NONE, 8, -1);
+ TEST_CHECK(ht != NULL);
+
+ for (i = 0; i < sizeof(entries) / sizeof(struct map); i++) {
+ m = &entries[i];
+ ht_add(ht, m->key, m->val);
+ }
+
+ flb_hash_table_destroy(ht);
+}
+
+void test_medium_table()
+{
+ int i;
+ struct map *m;
+ struct flb_hash_table *ht;
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_NONE, 8, -1);
+ TEST_CHECK(ht != NULL);
+
+ for (i = 0; i < sizeof(entries) / sizeof(struct map); i++) {
+ m = &entries[i];
+ ht_add(ht, m->key, m->val);
+ }
+
+ flb_hash_table_destroy(ht);
+}
+
+void test_chaining()
+{
+ int i;
+ int inserts = 0;
+ int count;
+ int chains = 0;
+ struct map *m;
+ struct mk_list *head;
+ struct flb_hash_table_chain *table;
+ struct flb_hash_table *ht;
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_NONE, 8, -1);
+ TEST_CHECK(ht != NULL);
+
+ for (i = 0; i < 8; i++) {
+ m = &entries[i];
+ ht_add(ht, m->key, m->val);
+ inserts++;
+ }
+
+ for (i = 0; i < ht->size; i++) {
+ table = &ht->table[i];
+ count = 0;
+ mk_list_foreach(head, &table->chains) {
+ count++;
+ }
+ TEST_CHECK(count == table->count);
+
+ if (count > 0) {
+ chains++;
+ }
+ }
+
+ /* Tests diff between total, new minus 3 overrides */
+ TEST_CHECK(chains == inserts - 3);
+ flb_hash_table_destroy(ht);
+}
+
+void test_delete_all()
+{
+ int i;
+ int ret;
+ int count;
+ int not_found = 0;
+ int total = 0;
+ struct map *m;
+ struct flb_hash_table_chain *table;
+ struct flb_hash_table *ht;
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_NONE, 8, -1);
+ TEST_CHECK(ht != NULL);
+
+ total = sizeof(entries) / sizeof(struct map);
+ for (i = 0; i < total; i++) {
+ m = &entries[i];
+ ht_add(ht, m->key, m->val);
+ }
+
+ for (i = total - 1; i >= 0; i--) {
+ m = &entries[i];
+ ret = flb_hash_table_del(ht, m->key);
+ if (ret == -1) {
+ not_found++;
+ }
+ }
+
+ count = 0;
+ for (i = 0; i < ht->size; i++) {
+ table = &ht->table[i];
+ count += table->count;
+ }
+
+ TEST_CHECK(count == 0);
+ flb_hash_table_destroy(ht);
+}
+
+void test_random_eviction()
+{
+ int ret;
+ const char *out_buf;
+ size_t out_size;
+ struct flb_hash_table *ht;
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_RANDOM, 8, 1);
+ TEST_CHECK(ht != NULL);
+
+ ret = ht_add(ht, "key1", "value1");
+ TEST_CHECK(ret != -1);
+
+ ret = ht_add(ht, "key2", "value2");
+ TEST_CHECK(ret != -1);
+
+ ret = flb_hash_table_get(ht, "key1", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret == -1);
+
+ ret = flb_hash_table_get(ht, "key2", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ flb_hash_table_destroy(ht);
+}
+
+void test_less_used_eviction()
+{
+ int ret;
+ const char *out_buf;
+ size_t out_size;
+ struct flb_hash_table *ht;
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_LESS_USED, 8, 2);
+ TEST_CHECK(ht != NULL);
+
+ ret = ht_add(ht, "key1", "value1");
+ TEST_CHECK(ret != -1);
+
+ ret = ht_add(ht, "key2", "value2");
+ TEST_CHECK(ret != -1);
+
+ ret = flb_hash_table_get(ht, "key1", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ ret = flb_hash_table_get(ht, "key2", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ ret = flb_hash_table_get(ht, "key2", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ ret = ht_add(ht, "key3", "value3");
+ TEST_CHECK(ret != -1);
+
+ ret = flb_hash_table_get(ht, "key3", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ ret = flb_hash_table_get(ht, "key2", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ ret = flb_hash_table_get(ht, "key1", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret == -1);
+
+ flb_hash_table_destroy(ht);
+}
+
+void test_older_eviction()
+{
+ int ret;
+ const char *out_buf;
+ size_t out_size;
+ struct flb_hash_table *ht;
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_OLDER, 8, 2);
+ TEST_CHECK(ht != NULL);
+
+ ret = ht_add(ht, "key2", "value2");
+ TEST_CHECK(ret != -1);
+
+ ret = ht_add(ht, "key1", "value1");
+ TEST_CHECK(ret != -1);
+
+ ret = flb_hash_table_get(ht, "key1", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ ret = flb_hash_table_get(ht, "key2", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ ret = flb_hash_table_get(ht, "key2", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ ret = ht_add(ht, "key3", "value3");
+ TEST_CHECK(ret != -1);
+
+ ret = flb_hash_table_get(ht, "key3", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ ret = flb_hash_table_get(ht, "key2", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret == -1);
+
+ ret = flb_hash_table_get(ht, "key1", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+
+ flb_hash_table_destroy(ht);
+}
+
+void test_pointer()
+{
+ int ret;
+ const char *out_buf;
+ size_t out_size;
+ struct flb_hash_table *ht;
+
+ char *val1 = "val1";
+ char *val2 = "val2";
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_NONE, 512, 0);
+ TEST_CHECK(ht != NULL);
+
+ ret = flb_hash_table_add(ht, "key1", 4, (void *) val1, 0);
+ TEST_CHECK(ret >= 0);
+
+ ret = flb_hash_table_add(ht, "key2", 4, (void *) val2, 0);
+ TEST_CHECK(ret >= 0);
+
+ ret = flb_hash_table_get(ht, "key2", 4, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret >= 0);
+ TEST_CHECK((void *) out_buf == (void *) val2);
+
+ out_buf = flb_hash_table_get_ptr(ht, "key2", 4);
+ TEST_CHECK((void *) out_buf == (void *) val2);
+
+ ret = flb_hash_table_del_ptr(ht, "key2", 4, (void *) out_buf);
+ TEST_CHECK(ret == 0);
+
+ TEST_CHECK(ht->total_count == 1);
+ flb_hash_table_destroy(ht);
+}
+
+void test_hash_exists()
+{
+ int i;
+ int id;
+ int ret;
+ int len;
+ struct map *m;
+ uint64_t hash;
+ struct flb_hash_table *ht;
+
+ ht = flb_hash_table_create(FLB_HASH_TABLE_EVICT_NONE, 1000, 0);
+ TEST_CHECK(ht != NULL);
+
+ for (i = 0; i < sizeof(entries) / sizeof(struct map); i++) {
+ m = &entries[i];
+ id = ht_add(ht, m->key, m->val);
+ TEST_CHECK(id >= 0);
+
+ len = strlen(m->key);
+ hash = cfl_hash_64bits(m->key, len);
+
+ ret = flb_hash_table_exists(ht, hash);
+ TEST_CHECK(ret == FLB_TRUE);
+ }
+
+ for (i = 0; i < sizeof(entries) / sizeof(struct map); i++) {
+ m = &entries[i];
+
+ /* get hash */
+ len = strlen(m->key);
+ hash = cfl_hash_64bits(m->key, len);
+
+ /* delete */
+ ret = flb_hash_table_del(ht, m->key);
+
+ ret = flb_hash_table_exists(ht, hash);
+ TEST_CHECK(ret == FLB_FALSE);;
+ }
+
+ flb_hash_table_destroy(ht);
+}
+
+TEST_LIST = {
+ { "zero_size", test_create_zero },
+ { "single", test_single },
+ { "small_table", test_small_table },
+ { "medium_table", test_medium_table },
+ { "chaining_count", test_chaining },
+ { "delete_all", test_delete_all },
+ { "random_eviction", test_random_eviction },
+ { "less_used_eviction", test_less_used_eviction },
+ { "older_eviction", test_older_eviction },
+ { "pointer", test_pointer },
+ { "hash_exists", test_hash_exists},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/hmac.c b/fluent-bit/tests/internal/hmac.c
new file mode 100644
index 00000000..f7de588f
--- /dev/null
+++ b/fluent-bit/tests/internal/hmac.c
@@ -0,0 +1,136 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_hmac.h>
+
+#include "flb_tests_internal.h"
+
+static size_t get_sample_data(unsigned char ***buffer_list,
+ size_t **length_list,
+ char **precomputed_key,
+ char **precomputed_mac,
+ int *precomputed_mac_type)
+{
+ static size_t lengths[] = {0, 0, 0, 0};
+ static const char *buffers[] = {
+ "This is one ",
+ "of the four ",
+ "buffers that will be passed ",
+ "to the simple batched digest processor function"
+ };
+ static const char *signature = "a68b8b3e480e00e0e61b4c097a29e64a261dc2548c608677c2827ea1e5b65d77";
+ static const int mac_type = FLB_HASH_SHA256;
+ size_t index;
+ static const char *key = "Long live the bird thing!";
+
+ if (lengths[0] == 0) {
+ for (index = 0 ; index < 4 ; index++) {
+ lengths[index] = strlen(buffers[index]);
+ }
+ }
+
+ *buffer_list = (unsigned char **) buffers;
+ *length_list = lengths;
+ *precomputed_key = (char *) key;
+ *precomputed_mac = (char *) signature;
+ *precomputed_mac_type = mac_type;
+
+ return 4;
+}
+
+static void hexlify(uint8_t *hash, char *out)
+{
+ int i;
+ static const char hex[] = "0123456789abcdef";
+ char *buf = out;
+
+ for (i = 0; i < 32; i++) {
+ *buf++ = hex[hash[i] >> 4];
+ *buf++ = hex[hash[i] & 0xf];
+ }
+}
+
+static void test_hmac_standard()
+{
+ int ref_signature_type;
+ char hex_signature[64];
+ char *ref_hex_signature;
+ uint8_t raw_signature[32];
+ size_t buffer_count;
+ unsigned char **buffer_list;
+ size_t *length_list;
+ char *ref_key;
+ int result;
+ size_t index;
+ struct flb_hmac hmac;
+
+ buffer_count = get_sample_data(&buffer_list, &length_list,
+ &ref_key,
+ &ref_hex_signature,
+ &ref_signature_type);
+
+ TEST_CHECK(buffer_count > 0);
+ TEST_CHECK(ref_signature_type == FLB_HASH_SHA256);
+
+ result = flb_hmac_init(&hmac,
+ ref_signature_type,
+ (unsigned char *) ref_key,
+ strlen(ref_key));
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+
+ for (index = 0 ; index < buffer_count ; index++) {
+ result = flb_hmac_update(&hmac, buffer_list[index], length_list[index]);
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+ }
+
+ result = flb_hmac_finalize(&hmac, raw_signature, sizeof(raw_signature));
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+
+ hexlify(raw_signature, hex_signature);
+
+ TEST_CHECK(memcmp(hex_signature, ref_hex_signature, 64) == 0);
+
+ flb_hmac_cleanup(&hmac);
+}
+
+
+static void test_hmac_simple_batch()
+{
+ int ref_signature_type;
+ char hex_signature[64];
+ char *ref_hex_signature;
+ uint8_t raw_signature[32];
+ size_t buffer_count;
+ unsigned char **buffer_list;
+ size_t *length_list;
+ char *ref_key;
+ int result;
+
+ buffer_count = get_sample_data(&buffer_list, &length_list,
+ &ref_key,
+ &ref_hex_signature,
+ &ref_signature_type);
+
+ TEST_CHECK(buffer_count > 0);
+ TEST_CHECK(ref_signature_type == FLB_HASH_SHA256);
+
+ result = flb_hmac_simple_batch(ref_signature_type,
+ (unsigned char *) ref_key,
+ strlen(ref_key),
+ buffer_count,
+ buffer_list,
+ length_list,
+ raw_signature,
+ sizeof(raw_signature));
+ TEST_CHECK(result == FLB_CRYPTO_SUCCESS);
+
+ hexlify(raw_signature, hex_signature);
+
+ TEST_CHECK(memcmp(hex_signature, ref_hex_signature, 64) == 0);
+}
+
+TEST_LIST = {
+ { "test_hmac_simple_batch", test_hmac_simple_batch },
+ { "test_hmac_standard", test_hmac_standard },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/http_client.c b/fluent-bit/tests/internal/http_client.c
new file mode 100644
index 00000000..a37daa35
--- /dev/null
+++ b/fluent-bit/tests/internal/http_client.c
@@ -0,0 +1,478 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_error.h>
+#include <fluent-bit/flb_socket.h>
+#include <fluent-bit/flb_http_client.h>
+
+#include "flb_tests_internal.h"
+
+struct test_ctx {
+ struct flb_upstream *u;
+ struct flb_connection *u_conn;
+ struct flb_config *config;
+};
+
+struct test_ctx* test_ctx_create()
+{
+ struct test_ctx *ret_ctx = NULL;
+
+ ret_ctx = flb_malloc(sizeof(struct test_ctx));
+ if (!TEST_CHECK(ret_ctx != NULL)) {
+ flb_errno();
+ TEST_MSG("flb_malloc(test_ctx) failed");
+ return NULL;
+ }
+ ret_ctx->u = NULL;
+ ret_ctx->u_conn = NULL;
+
+ ret_ctx->config = flb_config_init();
+ if(!TEST_CHECK(ret_ctx->config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ flb_free(ret_ctx);
+ return NULL;
+ }
+
+ ret_ctx->u = flb_upstream_create(ret_ctx->config, "127.0.0.1", 80, 0, NULL);
+ if (!TEST_CHECK(ret_ctx->u != NULL)) {
+ TEST_MSG("flb_upstream_create failed");
+ flb_config_exit(ret_ctx->config);
+ flb_free(ret_ctx);
+ return NULL;
+ }
+
+ ret_ctx->u_conn = flb_calloc(1, sizeof(struct flb_connection));
+ if(!TEST_CHECK(ret_ctx->u_conn != NULL)) {
+ flb_errno();
+ TEST_MSG("flb_malloc(flb_connection) failed");
+ flb_upstream_destroy(ret_ctx->u);
+ flb_config_exit(ret_ctx->config);
+ flb_free(ret_ctx);
+ return NULL;
+ }
+
+ ret_ctx->u_conn->upstream = ret_ctx->u;
+
+ return ret_ctx;
+}
+
+int test_ctx_destroy(struct test_ctx* ctx)
+{
+ if (!TEST_CHECK(ctx != NULL)) {
+ return -1;
+ }
+ if (ctx->u_conn) {
+ flb_free(ctx->u_conn);
+ }
+ if (ctx->u) {
+ flb_upstream_destroy(ctx->u);
+ }
+ if (ctx->config) {
+ flb_config_exit(ctx->config);
+ }
+
+ flb_free(ctx);
+ return 0;
+}
+
+void test_http_buffer_increase()
+{
+ int ret;
+ size_t s;
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ struct flb_http_response *resp;
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create HTTP client instance */
+ c = flb_http_client(ctx->u_conn, FLB_HTTP_GET, "/", NULL, 0,
+ "127.0.0.1", 80, NULL, 0);
+ TEST_CHECK(c != NULL);
+
+ /* Do buffer tests */
+ resp = &c->resp;
+ TEST_CHECK(resp->data_len == 0);
+ TEST_CHECK(resp->data_size == FLB_HTTP_DATA_SIZE_MAX);
+ TEST_CHECK(resp->data_size_max == FLB_HTTP_DATA_SIZE_MAX);
+
+ /* Invalid size */
+ ret = flb_http_buffer_size(c, 1);
+ TEST_CHECK(ret == -1);
+
+ /* Increase max size to 8KB */
+ flb_http_buffer_size(c, 8192);
+
+ /* Request to allocate +4KB */
+ ret = flb_http_buffer_increase(c, 4096, &s);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(s == 4096);
+
+ /* Request to allocate 1 byte, it should fail */
+ ret = flb_http_buffer_increase(c, 1, &s);
+ TEST_CHECK(ret == -1);
+
+ /* Test unlimited */
+ flb_http_buffer_size(c, 0);
+ ret = flb_http_buffer_increase(c, 1, &s);
+ TEST_CHECK(ret == 0 && s == 1);
+
+ /* Payload test */
+ memcpy(c->resp.data, "00abc11def22ghi__PAYLOAD__8yz9900", 33);
+ c->resp.data[33] = '\0';
+ c->resp.data_len = 33;
+ c->resp.payload = c->resp.data + 15;
+ c->resp.payload_size = 11;
+
+ ret = flb_http_buffer_increase(c, 819200, &s);
+ TEST_CHECK(ret == 0);
+
+ ret = strncmp(c->resp.payload, "__PAYLOAD__", 11);
+ TEST_CHECK(ret == 0);
+
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+}
+
+void test_http_add_get_header()
+{
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ flb_sds_t ret_str;
+ char *ua = "Fluent-Bit";
+ char *host = "127.0.0.1:80";
+ int ret;
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create HTTP client instance */
+ c = flb_http_client(ctx->u_conn, FLB_HTTP_GET, "/", NULL, 0,
+ "127.0.0.1", 80, NULL, 0);
+ if(!TEST_CHECK(c != NULL)) {
+ TEST_MSG("flb_http_client failed");
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check User-Agent */
+ ret = flb_http_add_header(c, "User-Agent", 10, ua, strlen(ua));
+ TEST_CHECK(ret == 0);
+
+ ret_str = flb_http_get_header(c, "User-Agent", 10);
+ if (!TEST_CHECK(ret_str != NULL)) {
+ TEST_MSG("flb_http_get_header failed");
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(flb_sds_cmp(ret_str, ua, strlen(ua)) == 0)) {
+ TEST_MSG("strcmp failed. got=%s expect=%s", ret_str, ua);
+ }
+ flb_sds_destroy(ret_str);
+
+
+ /* Check Host */
+ ret_str = flb_http_get_header(c, "Host", 4);
+ if (!TEST_CHECK(ret_str != NULL)) {
+ TEST_MSG("flb_http_get_header failed");
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(flb_sds_cmp(ret_str, host, strlen(host)) == 0)) {
+ TEST_MSG("strcmp failed. got=%s expect=%s", ret_str, host);
+ }
+
+ flb_sds_destroy(ret_str);
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+}
+
+void test_http_set_keepalive()
+{
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ flb_sds_t ret_str;
+ int ret;
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create HTTP client instance */
+ c = flb_http_client(ctx->u_conn, FLB_HTTP_GET, "/", NULL, 0,
+ "127.0.0.1", 80, NULL, 0);
+ if(!TEST_CHECK(c != NULL)) {
+ TEST_MSG("flb_http_client failed");
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Set keepalive header */
+ ret = flb_http_set_keepalive(c);
+ TEST_CHECK(ret == 0);
+
+ ret_str = flb_http_get_header(c, FLB_HTTP_HEADER_CONNECTION,
+ strlen(FLB_HTTP_HEADER_CONNECTION));
+ if (!TEST_CHECK(ret_str != NULL)) {
+ TEST_MSG("flb_get_header failed");
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Compare */
+ if (!TEST_CHECK(flb_sds_cmp(ret_str, FLB_HTTP_HEADER_KA, strlen(FLB_HTTP_HEADER_KA)) == 0)) {
+ TEST_MSG("strcmp failed. got=%s expect=%s", ret_str, FLB_HTTP_HEADER_KA);
+ }
+ flb_sds_destroy(ret_str);
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+}
+
+void test_http_strip_port_from_host()
+{
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ flb_sds_t ret_str;
+ char *host_port = "127.0.0.1:80";
+ char *host = "127.0.0.1";
+ int ret;
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create HTTP client instance */
+ c = flb_http_client(ctx->u_conn, FLB_HTTP_GET, "/", NULL, 0,
+ "127.0.0.1", 80, NULL, 0);
+ if(!TEST_CHECK(c != NULL)) {
+ TEST_MSG("flb_http_client failed");
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check Host. It should contain port number */
+ ret_str = flb_http_get_header(c, "Host", 4);
+ if (!TEST_CHECK(ret_str != NULL)) {
+ TEST_MSG("flb_http_get_header failed");
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(flb_sds_cmp(ret_str, host_port, strlen(host_port)) == 0)) {
+ TEST_MSG("strcmp failed. got=%s expect=%s", ret_str, host_port);
+ }
+ flb_sds_destroy(ret_str);
+
+
+ ret = flb_http_strip_port_from_host(c);
+ TEST_CHECK(ret == 0);
+
+ /* Check Host. Port number should be removed. */
+ ret_str = flb_http_get_header(c, "Host", 4);
+ if (!TEST_CHECK(ret_str != NULL)) {
+ TEST_MSG("flb_http_get_header failed");
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(flb_sds_cmp(ret_str, host, strlen(host)) == 0)) {
+ TEST_MSG("strcmp failed. got=%s expect=%s", ret_str, host);
+ }
+
+ flb_sds_destroy(ret_str);
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+}
+
+void test_http_encoding_gzip()
+{
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ flb_sds_t ret_str;
+ char *gzip = "gzip";
+ int ret;
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create HTTP client instance */
+ c = flb_http_client(ctx->u_conn, FLB_HTTP_GET, "/", NULL, 0,
+ "127.0.0.1", 80, NULL, 0);
+ if(!TEST_CHECK(c != NULL)) {
+ TEST_MSG("flb_http_client failed");
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check encoding. It should be error */
+ ret_str = flb_http_get_header(c, FLB_HTTP_HEADER_CONTENT_ENCODING,
+ strlen(FLB_HTTP_HEADER_CONTENT_ENCODING));
+ if (!TEST_CHECK(ret_str == NULL)) {
+ TEST_MSG("Got encoding? Header:%s", ret_str);
+ flb_sds_destroy(ret_str);
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_set_content_encoding_gzip(c);
+ TEST_CHECK(ret == 0);
+
+ /* Check Encoding */
+ ret_str = flb_http_get_header(c, FLB_HTTP_HEADER_CONTENT_ENCODING,
+ strlen(FLB_HTTP_HEADER_CONTENT_ENCODING));
+ if (!TEST_CHECK(ret_str != NULL)) {
+ TEST_MSG("flb_http_get_header failed");
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(flb_sds_cmp(ret_str, gzip, strlen(gzip)) == 0)) {
+ TEST_MSG("strcmp failed. got=%s expect=%s", ret_str, gzip);
+ }
+
+ flb_sds_destroy(ret_str);
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+}
+
+void test_http_add_basic_auth_header()
+{
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ flb_sds_t ret_str;
+ char *expect = "Basic dXNlcjpwYXNzd29yZA=="; /* user:password in base64 */
+ char *auth = FLB_HTTP_HEADER_AUTH;
+ const char *user = "user";
+ char *passwd = "password";
+ int ret;
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create HTTP client instance */
+ c = flb_http_client(ctx->u_conn, FLB_HTTP_GET, "/", NULL, 0,
+ "127.0.0.1", 80, NULL, 0);
+ if(!TEST_CHECK(c != NULL)) {
+ TEST_MSG("flb_http_client failed");
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check Autholization. It should be error. */
+ ret_str = flb_http_get_header(c, auth, strlen(auth));
+ if (!TEST_CHECK(ret_str == NULL)) {
+ TEST_MSG("Got auth? Header:%s", ret_str);
+ flb_sds_destroy(ret_str);
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_basic_auth(c, user, passwd);
+ TEST_CHECK(ret == 0);
+
+ /* Check Autholization. */
+ ret_str = flb_http_get_header(c, auth, strlen(auth));
+ if (!TEST_CHECK(ret_str != NULL)) {
+ TEST_MSG("flb_http_get_header failed");
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(flb_sds_cmp(ret_str, expect, strlen(expect)) == 0)) {
+ TEST_MSG("strcmp failed. got=%s expect=%s", ret_str, expect);
+ }
+
+ flb_sds_destroy(ret_str);
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+}
+
+void test_http_add_proxy_auth_header()
+{
+ struct test_ctx *ctx;
+ struct flb_http_client *c;
+ flb_sds_t ret_str;
+ char *expect = "Basic dXNlcjpwYXNzd29yZA=="; /* user:password in base64 */
+ char *auth = FLB_HTTP_HEADER_PROXY_AUTH;
+ const char *user = "user";
+ char *passwd = "password";
+ int ret;
+
+ ctx = test_ctx_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create HTTP client instance */
+ c = flb_http_client(ctx->u_conn, FLB_HTTP_GET, "/", NULL, 0,
+ "127.0.0.1", 80, NULL, 0);
+ if(!TEST_CHECK(c != NULL)) {
+ TEST_MSG("flb_http_client failed");
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check autholization header. It should be error. */
+ ret_str = flb_http_get_header(c, auth, strlen(auth));
+ if (!TEST_CHECK(ret_str == NULL)) {
+ TEST_MSG("Got auth? Header:%s", ret_str);
+ flb_sds_destroy(ret_str);
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_http_proxy_auth(c, user, passwd);
+ TEST_CHECK(ret == 0);
+
+ /* Check autholization header. */
+ ret_str = flb_http_get_header(c, auth, strlen(auth));
+ if (!TEST_CHECK(ret_str != NULL)) {
+ TEST_MSG("flb_http_get_header failed");
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(flb_sds_cmp(ret_str, expect, strlen(expect)) == 0)) {
+ TEST_MSG("strcmp failed. got=%s expect=%s", ret_str, expect);
+ }
+
+ flb_sds_destroy(ret_str);
+ flb_http_client_destroy(c);
+ test_ctx_destroy(ctx);
+}
+
+TEST_LIST = {
+ { "http_buffer_increase" , test_http_buffer_increase},
+ { "add_get_header" , test_http_add_get_header},
+ { "set_keepalive" , test_http_set_keepalive},
+ { "strip_port_from_host" , test_http_strip_port_from_host},
+ { "encoding_gzip" , test_http_encoding_gzip},
+ { "add_basic_auth_header" , test_http_add_basic_auth_header},
+ { "add_proxy_auth_header" , test_http_add_proxy_auth_header},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/include/sp_cb_functions.h b/fluent-bit/tests/internal/include/sp_cb_functions.h
new file mode 100644
index 00000000..aee3fb39
--- /dev/null
+++ b/fluent-bit/tests/internal/include/sp_cb_functions.h
@@ -0,0 +1,655 @@
+#include "sp_helpers.h"
+
+#ifndef FLB_TEST_CP_FUNCTIONS
+#define FLB_TEST_CP_FUNCTIONS
+
+/* Callback functions to perform checks over results */
+static void cb_select_all(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect all 11 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 11);
+}
+
+/* Callback test: expect one key per record */
+static void cb_select_id(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect all 11 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 11);
+
+ ret = mp_count_keys(buf, size);
+ TEST_CHECK(ret == 13);
+}
+
+static void cb_select_cond_1(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+}
+
+static void cb_select_cond_2(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 2 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 2);
+}
+
+static void cb_select_cond_not_null(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+}
+
+static void cb_select_cond_null(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+}
+
+static void cb_select_not_equal_1(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 2);
+}
+
+static void cb_select_not_equal_2(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 2 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 2);
+}
+
+static void cb_select_aggr_count(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ /* COUNT(*) is 11 */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "COUNT(*)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 11, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_func_time_now(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+ char tmp[32];
+ struct tm *local;
+ time_t now = time(NULL);
+
+ local = localtime(&now);
+ strftime(tmp, sizeof(tmp) - 1, "%Y-%m-%d %H:%M:%S", local);
+
+ /* Expect 2 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 2);
+
+ /* NOW() */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "NOW()",
+ MSGPACK_OBJECT_STR,
+ tmp, 0, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* tnow */
+ ret = mp_record_key_cmp(buf, size,
+ 1, "tnow",
+ MSGPACK_OBJECT_STR,
+ tmp, 0, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_func_time_unix_timestamp(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+ time_t now = time(NULL);
+
+ /* Expect 2 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 2);
+
+ /* UNIX_TIMESTAMP() */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "UNIX_TIMESTAMP()",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, now, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* tnow */
+ ret = mp_record_key_cmp(buf, size,
+ 1, "ts",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, now, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+/* No records must be selected */
+static void cb_select_tag_error(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ TEST_CHECK(buf == NULL && size == 0);
+
+ /* no records expected */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 0);
+}
+
+/* No records must be selected */
+static void cb_select_tag_ok(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ TEST_CHECK(buf != NULL && size > 0);
+
+ /* 2 records expected */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 2);
+}
+
+static void cb_record_contains(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 2 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 2);
+}
+
+static void cb_record_not_contains(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 0 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 0);
+}
+
+/* Callback functions to perform checks over results */
+static void cb_select_sub_blue(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+}
+
+static void cb_select_sub_num(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 2 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 2);
+}
+
+static void cb_select_sub_colors(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 3 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 3);
+}
+
+static void cb_select_sub_record_contains(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 5 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 5);
+}
+
+static void cb_select_sub_keys(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ ret = mp_record_key_cmp(buf, size,
+ 0, "map['sub1']['sub2']['color']",
+ MSGPACK_OBJECT_STR,
+ "blue", 0, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_select_sum_sub_keys(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ ret = mp_record_key_cmp(buf, size,
+ 0, "SUM(map['sub1']['sub2'])",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 246, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_select_avg_sub_keys(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ ret = mp_record_key_cmp(buf, size,
+ 0, "AVG(map['sub1']['sub2'])",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 123.0);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_select_count_sub_keys(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ ret = mp_record_key_cmp(buf, size,
+ 0, "COUNT(map['sub1']['sub2'])",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 2, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_select_min_sub_keys(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ ret = mp_record_key_cmp(buf, size,
+ 0, "MIN(map['sub1']['sub2'])",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 123, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_select_max_sub_keys(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ ret = mp_record_key_cmp(buf, size,
+ 0, "MAX(map['sub1']['sub3'])",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 100);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_select_sum_sub_keys_group_by(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 3 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 3);
+
+ ret = mp_record_key_cmp(buf, size,
+ 0, "SUM(map['sub1']['sub3'])",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 105.5);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_select_sum_sub_keys_group_by_2(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 3 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 3);
+
+ ret = mp_record_key_cmp(buf, size,
+ 0, "SUM(map['sub1']['sub3'])",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 105.5);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_select_sum_sub_keys_group_by_3(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 3 rows */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 3);
+
+ ret = mp_record_key_cmp(buf, size,
+ 0, "SUM(map['sub1']['sub3'])",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 100, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_record_key_cmp(buf, size,
+ 1, "SUM(map['sub1']['sub3'])",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 11);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ ret = mp_record_key_cmp(buf, size,
+ 2, "SUM(map['sub1']['sub3'])",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 5.5);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+
+static void cb_forecast_tumbling_window(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect one record only */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ /* Check SUM value result */
+ ret = mp_record_key_cmp(buf, size, 0, "TIMESERIES_FORECAST(usage)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 310.0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* Check AVG value result */
+ ret = mp_record_key_cmp(buf, size, 0, "AVG(usage)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 60.0);
+
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+/* Callback functions to perform checks over results */
+static void cb_snapshot_create(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ ret = mp_count_rows(buf, size);
+ /* Snapshot doesn't return anything */
+ TEST_CHECK(ret == 0);
+};
+
+static void cb_snapshot_purge(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 5 rows, as set in snapshot query */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 5);
+};
+
+static void cb_snapshot_purge_time(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 11 rows, as set in snapshot query */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 11);
+};
+
+static void cb_window_5_second(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect one record only */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ /* Check SUM value result */
+ ret = mp_record_key_cmp(buf, size, 0, "SUM(id)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 225, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* Check AVG value result */
+ ret = mp_record_key_cmp(buf, size, 0, "AVG(id)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 4.5);
+
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_select_aggr(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ /* MIN(id) is 0 */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "MIN(id)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 0, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* MAX(id) is 10 */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "MAX(id)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 10, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* COUNT(*) is 11 */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "COUNT(*)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 11, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* SUM(bytes) is 110.50 */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "SUM(bytes)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 110.50);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* AVG(bytes) is 10.04545 */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "AVG(bytes)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 10.045455);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_select_groupby(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect 1 row */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 2);
+
+ /* MIN(id) is 0 for record 0 (bool=true) */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "MIN(id)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 0, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* MIN(id) is 6 for record 1 (bool=false) */
+ ret = mp_record_key_cmp(buf, size,
+ 1, "MIN(id)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 6, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* MAX(id) is 8 for record 0 (bool=true) */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "MAX(id)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 8, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* MAX(id) is i9 for record 1 (bool=false) */
+ ret = mp_record_key_cmp(buf, size,
+ 1, "MAX(id)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 9, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* COUNT(*) is 8 for record 0 (bool=true) */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "COUNT(*)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 8, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* COUNT(*) is 2 for record 1 (bool=false) */
+ ret = mp_record_key_cmp(buf, size,
+ 1, "COUNT(*)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 2, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* SUM(bytes) is 80.0 for record 0 (bool=true) */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "SUM(bytes)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 80.0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* SUM(bytes) is 20.50 for record 1 (bool=false) */
+ ret = mp_record_key_cmp(buf, size,
+ 1, "SUM(bytes)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 20.50);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* AVG(bytes) is 10.0 for record 0 (bool=true) */
+ ret = mp_record_key_cmp(buf, size,
+ 0, "AVG(bytes)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 10.0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* AVG(bytes) is 10.25 for record 1 (bool=false) */
+ ret = mp_record_key_cmp(buf, size,
+ 1, "AVG(bytes)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 10.25);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_hopping_window_5_second(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect one record only */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ /* Check SUM value result */
+ ret = mp_record_key_cmp(buf, size, 0, "SUM(id)",
+ MSGPACK_OBJECT_POSITIVE_INTEGER,
+ NULL, 266, 0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* Check AVG value result */
+ ret = mp_record_key_cmp(buf, size, 0, "AVG(id)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 16.625);
+
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+static void cb_forecast_hopping_window(int id, struct task_check *check,
+ char *buf, size_t size)
+{
+ int ret;
+
+ /* Expect one record only */
+ ret = mp_count_rows(buf, size);
+ TEST_CHECK(ret == 1);
+
+ /* Check SUM value result */
+ ret = mp_record_key_cmp(buf, size, 0, "TIMESERIES_FORECAST(usage)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 460.0);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ /* Check AVG value result */
+ ret = mp_record_key_cmp(buf, size, 0, "AVG(usage)",
+ MSGPACK_OBJECT_FLOAT,
+ NULL, 0, 175.0);
+
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+#endif
diff --git a/fluent-bit/tests/internal/include/sp_helpers.h b/fluent-bit/tests/internal/include/sp_helpers.h
new file mode 100644
index 00000000..2efaaeff
--- /dev/null
+++ b/fluent-bit/tests/internal/include/sp_helpers.h
@@ -0,0 +1,158 @@
+#ifndef FLB_TEST_SP_HELPERS
+#define FLB_TEST_SP_HELPERS
+
+#define MP_UOK MSGPACK_UNPACK_SUCCESS
+
+struct task_check {
+ int id; /* Test identifier */
+ int window_type; /* Type of window in window-based queries */
+ int window_size_sec; /* Size of the window in window-based queries */
+ int window_hop_sec; /* Hopping size in hopping window */
+ char *name; /* Name of the test */
+ char *exec; /* Query */
+ void (*cb_check)(int, struct task_check *, char *, size_t);
+ /* Callback function:
+ id, test_check, output buffer, buffer_size */
+};
+
+/* Helper functions */
+static int mp_count_rows(char *buf, size_t size)
+{
+ int total = 0;
+ size_t off = 0;
+ msgpack_unpacked result;
+
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, buf, size, &off) == MP_UOK) {
+ total++;
+ }
+
+ msgpack_unpacked_destroy(&result);
+ return total;
+}
+
+/* Count total number of keys considering all rows */
+static int mp_count_keys(char *buf, size_t size)
+{
+ int keys = 0;
+ size_t off = 0;
+ msgpack_unpacked result;
+ msgpack_object root;
+ msgpack_object map;
+
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, buf, size, &off) == MP_UOK) {
+ root = result.data;
+ map = root.via.array.ptr[1];
+ keys += map.via.map.size;
+ }
+ msgpack_unpacked_destroy(&result);
+
+ return keys;
+}
+
+static inline int float_cmp(double f1, double f2)
+{
+ double precision = 0.00001;
+
+ if (((f1 - precision) < f2) &&
+ ((f1 + precision) > f2)) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+/* Lookup record/row number 'id' and check that 'key' matches 'val' */
+static int mp_record_key_cmp(char *buf, size_t size,
+ int record_id, char *key,
+ int val_type, char *val_str, int64_t val_int64,
+ double val_f64)
+{
+ int i;
+ int ret = FLB_FALSE;
+ int id = 0;
+ int k_len;
+ int v_len;
+ int keys = 0;
+ size_t off = 0;
+ msgpack_unpacked result;
+ msgpack_object root;
+ msgpack_object map;
+ msgpack_object k;
+ msgpack_object v;
+
+ k_len = strlen(key);
+
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, buf, size, &off) == MP_UOK) {
+ if (id != record_id) {
+ id++;
+ continue;
+ }
+
+ root = result.data;
+ map = root.via.array.ptr[1];
+ keys += map.via.map.size;
+
+ for (i = 0; i < keys; i++) {
+ k = map.via.map.ptr[i].key;
+ v = map.via.map.ptr[i].val;
+
+ if (k.type != MSGPACK_OBJECT_STR) {
+ continue;
+ }
+
+ if (k.via.str.size != k_len) {
+ continue;
+ }
+
+ if (strncmp(k.via.str.ptr, key, k_len) != 0) {
+ continue;
+ }
+
+ /* at this point the key matched, now validate the expected value */
+ if (val_type == MSGPACK_OBJECT_FLOAT) {
+ if (v.type != MSGPACK_OBJECT_FLOAT32 &&
+ v.type != MSGPACK_OBJECT_FLOAT) {
+ msgpack_unpacked_destroy(&result);
+ return FLB_FALSE;
+ }
+ }
+ else if (v.type != val_type) {
+ msgpack_unpacked_destroy(&result);
+ return FLB_FALSE;
+ }
+
+ switch (val_type) {
+ case MSGPACK_OBJECT_STR:
+ v_len = strlen(val_str);
+ if (strncmp(v.via.str.ptr, val_str, v_len) == 0) {
+ ret = FLB_TRUE;
+ }
+ goto exit;
+ case MSGPACK_OBJECT_POSITIVE_INTEGER:
+ if (v.via.i64 == val_int64) {
+ ret = FLB_TRUE;
+ }
+ goto exit;
+ case MSGPACK_OBJECT_FLOAT:
+ if (float_cmp(v.via.f64, val_f64)) {
+ ret = FLB_TRUE;
+ }
+ else {
+ printf("double mismatch: %f exp %f\n",
+ v.via.f64, val_f64);
+ }
+ goto exit;
+ };
+ }
+ }
+
+exit:
+ msgpack_unpacked_destroy(&result);
+ return ret;
+}
+
+#endif
diff --git a/fluent-bit/tests/internal/include/sp_invalid_queries.h b/fluent-bit/tests/internal/include/sp_invalid_queries.h
new file mode 100644
index 00000000..e299e0fd
--- /dev/null
+++ b/fluent-bit/tests/internal/include/sp_invalid_queries.h
@@ -0,0 +1,20 @@
+#ifndef FLB_TEST_INVALID_QUERIES
+#define FLB_TEST_INVALID_QUERIES
+
+/* Tests to check if syntactically invalid queries return error */
+char *invalid_query_checks[] = {
+ "SELECT id, MIN(id) FROM STREAM:FLB;",
+ "SELECT *, COUNT(id) FROM STREAM:FLB;",
+ "SELECT * FROM TAG:FLB WHERE bool = NULL ;",
+ "SELECT * FROM TAG:FLB WHERE @record.some_random_func() ;",
+ "SELECT id, MIN(id) FROM STREAM:FLB WINDOW TUMBLING (1 SECOND)" \
+ " GROUP BY bool;",
+ "SELECT *, COUNT(id) FROM STREAM:FLB WINDOW TUMBLING (1 SECOND)" \
+ " GROUP BY bool;",
+ "SELECT *, COUNT(bool) FROM STREAM:FLB WINDOW TUMBLING (1 SECOND)" \
+ " GROUP BY bool;",
+ "SELECT *, bool, COUNT(bool) FROM STREAM:FLB WINDOW TUMBLING (1 SECOND)" \
+ " GROUP BY bool;"
+};
+
+#endif
diff --git a/fluent-bit/tests/internal/include/sp_select_keys.h b/fluent-bit/tests/internal/include/sp_select_keys.h
new file mode 100644
index 00000000..ba3c809f
--- /dev/null
+++ b/fluent-bit/tests/internal/include/sp_select_keys.h
@@ -0,0 +1,131 @@
+#include "sp_cb_functions.h"
+
+#ifndef FLB_TEST_SP_SELECT_KEYS
+#define FLB_TEST_SP_SELECT_KEYS
+
+/* Tests for 'test_select_keys' */
+struct task_check select_keys_checks[] = {
+ {
+ 0, 0, 0, 0,
+ "select_all",
+ "SELECT * FROM STREAM:FLB;",
+ cb_select_all
+ },
+ {
+ 1, 0, 0, 0,
+ "select_id",
+ "SELECT id, word2 FROM STREAM:FLB;",
+ cb_select_id
+ },
+
+ /* Conditionals */
+ {
+ 2, 0, 0, 0,
+ "select_cond_1",
+ "SELECT * FROM STREAM:FLB WHERE bytes > 10.290;",
+ cb_select_cond_1
+ },
+ {
+ 3, 0, 0, 0,
+ "select_cond_2",
+ "SELECT * FROM STREAM:FLB WHERE word2 = 'rlz' or word3 = 'rlz';",
+ cb_select_cond_2
+ },
+ {
+ 4, 0, 0, 0,
+ "select_cond_not_null",
+ "SELECT * FROM STREAM:FLB WHERE word2 = 'rlz' and word3 IS NOT NULL;",
+ cb_select_cond_not_null
+ },
+ {
+ 5, 0, 0, 0,
+ "select_cond_null",
+ "SELECT * FROM STREAM:FLB WHERE word3 IS NULL;",
+ cb_select_cond_null
+ },
+ {
+ 6, 0, 0, 0,
+ "select_not_equal_1",
+ "SELECT * FROM STREAM:FLB WHERE bool != true;",
+ cb_select_not_equal_1
+ },
+ {
+ 7, 0, 0, 0,
+ "select_not_equal_2",
+ "SELECT * FROM STREAM:FLB WHERE bytes <> 10;",
+ cb_select_not_equal_2
+ },
+
+
+ /* Aggregation functions */
+ {
+ 8, 0, 0, 0,
+ "select_aggr",
+ "SELECT MIN(id), MAX(id), COUNT(*), SUM(bytes), AVG(bytes) " \
+ "FROM STREAM:FLB;",
+ cb_select_aggr,
+ },
+ {
+ 9, 0, 0, 0,
+ "select_aggr_coount",
+ "SELECT COUNT(*) " \
+ "FROM STREAM:FLB;",
+ cb_select_aggr_count,
+ },
+ {
+ 10, 0, 0, 0,
+ "select_aggr_window_tumbling",
+ "SELECT MIN(id), MAX(id), COUNT(*), SUM(bytes), AVG(bytes) FROM STREAM:FLB;",
+ cb_select_aggr,
+ },
+ {
+ 11, 0, 0, 0,
+ "select_aggr_window_tumbling_groupby",
+ "SELECT bool, MIN(id), MAX(id), COUNT(*), SUM(bytes), AVG(bytes) " \
+ "FROM STREAM:FLB WHERE word3 IS NOT NULL GROUP BY bool;",
+ cb_select_groupby,
+ },
+
+ /* Time functions */
+ {
+ 12, 0, 0, 0,
+ "func_time_now",
+ "SELECT NOW(), NOW() as tnow FROM STREAM:FLB WHERE bytes > 10;",
+ cb_func_time_now,
+ },
+ {
+ 13, 0, 0, 0,
+ "func_time_unix_timestamp",
+ "SELECT UNIX_TIMESTAMP(), UNIX_TIMESTAMP() as ts " \
+ "FROM STREAM:FLB WHERE bytes > 10;",
+ cb_func_time_unix_timestamp,
+ },
+
+ /* Stream selection using Tag rules */
+ {
+ 14, 0, 0, 0,
+ "select_from_tag_error",
+ "SELECT id FROM TAG:'no-matches' WHERE bytes > 10;",
+ cb_select_tag_error,
+ },
+ {
+ 15, 0, 0, 0,
+ "select_from_tag",
+ "SELECT id FROM TAG:'samples' WHERE bytes > 10;",
+ cb_select_tag_ok,
+ },
+ {
+ 16, 0, 0, 0,
+ "@recond.contains",
+ "SELECT id FROM TAG:'samples' WHERE bytes = 10 AND @record.contains(word2);",
+ cb_record_contains,
+ },
+ {
+ 17, 0, 0, 0,
+ "@recond.contains",
+ "SELECT id FROM TAG:'samples' WHERE @record.contains(x);",
+ cb_record_not_contains,
+ },
+};
+
+#endif
diff --git a/fluent-bit/tests/internal/include/sp_select_subkeys.h b/fluent-bit/tests/internal/include/sp_select_subkeys.h
new file mode 100644
index 00000000..a5feebf0
--- /dev/null
+++ b/fluent-bit/tests/internal/include/sp_select_subkeys.h
@@ -0,0 +1,84 @@
+#include "sp_cb_functions.h"
+
+#ifndef FLB_TEST_SP_SELECT_SUBKEYS
+#define FLB_TEST_SP_SELECT_SUBKEYS
+
+/* Tests for 'test_select_subkeys' */
+struct task_check select_subkeys_checks[] = {
+ {
+ 0, 0, 0, 0,
+ "select_sub_blue",
+ "SELECT * FROM STREAM:FLB WHERE map['sub1']['sub2']['color'] = 'blue';",
+ cb_select_sub_blue
+ },
+ {
+ 1, 0, 0, 0,
+ "select_sub_num",
+ "SELECT * FROM STREAM:FLB WHERE map['sub1']['sub2'] = 123;",
+ cb_select_sub_num
+ },
+ {
+ 2, 0, 0, 0,
+ "select_sub_colors",
+ "SELECT * FROM STREAM:FLB WHERE " \
+ "map['sub1']['sub2']['color'] = 'blue' OR " \
+ "map['sub1']['sub2']['color'] = 'red' OR " \
+ "map['color'] = 'blue'; ",
+ cb_select_sub_colors
+ },
+ {
+ 3, 0, 0, 0,
+ "cb_select_sub_record_contains",
+ "SELECT * FROM STREAM:FLB WHERE " \
+ "@record.contains(map['sub1']['sub3']) OR " \
+ "@record.contains(map['color']); ",
+ cb_select_sub_record_contains
+ },
+ { 4, 0, 0, 0,
+ "cb_select_sub_keys",
+ "SELECT map['sub1']['sub2']['color'] FROM STREAM:FLB WHERE " \
+ "map['sub1']['sub2']['color'] = 'blue';",
+ cb_select_sub_keys},
+ { 5, 0, 0, 0,
+ "cb_select_sum_sub_keys",
+ "SELECT SUM(map['sub1']['sub2']) FROM STREAM:FLB WHERE " \
+ "map['sub1']['sub2'] = 123;",
+ cb_select_sum_sub_keys},
+ { 6, 0, 0, 0,
+ "cb_select_avg_sub_keys",
+ "SELECT AVG(map['sub1']['sub2']) FROM STREAM:FLB WHERE " \
+ "map['sub1']['sub2'] = 123;",
+ cb_select_avg_sub_keys},
+ { 7, 0, 0, 0,
+ "cb_select_count_sub_keys",
+ "SELECT COUNT(map['sub1']['sub2']) FROM STREAM:FLB WHERE " \
+ "map['sub1']['sub2'] = 123;",
+ cb_select_count_sub_keys},
+ { 8, 0, 0, 0,
+ "cb_select_min_sub_keys",
+ "SELECT MIN(map['sub1']['sub2']) FROM STREAM:FLB WHERE " \
+ "map['sub1']['sub2'] > 0;",
+ cb_select_min_sub_keys},
+ { 9, 0, 0, 0,
+ "cb_select_max_sub_keys",
+ "SELECT MAX(map['sub1']['sub3']) FROM STREAM:FLB WHERE " \
+ "map['sub1']['sub3'] > 0;",
+ cb_select_max_sub_keys},
+ { 10, 0, 0, 0,
+ "cb_select_sum_sub_keys_group_by",
+ "SELECT SUM(map['sub1']['sub3']) FROM STREAM:FLB " \
+ "GROUP BY map['mtype'];",
+ cb_select_sum_sub_keys_group_by},
+ { 11, 0, 0, 0,
+ "cb_select_sum_sub_keys_group_by_2",
+ "SELECT map['sub1']['stype'], map['mtype'], SUM(map['sub1']['sub3']) " \
+ "FROM STREAM:FLB GROUP BY map['mtype'], map['sub1']['stype'];",
+ cb_select_sum_sub_keys_group_by_2},
+ { 12, 0, 0, 0,
+ "cb_select_sum_sub_keys_group_by_3",
+ "SELECT map['sub1']['stype'], map['sub1']['sub4'], SUM(map['sub1']['sub3']) " \
+ "FROM STREAM:FLB GROUP BY map['sub1']['stype'], map['sub1']['sub4'];",
+ cb_select_sum_sub_keys_group_by_3}
+};
+
+#endif
diff --git a/fluent-bit/tests/internal/include/sp_snapshot.h b/fluent-bit/tests/internal/include/sp_snapshot.h
new file mode 100644
index 00000000..e8ca6fad
--- /dev/null
+++ b/fluent-bit/tests/internal/include/sp_snapshot.h
@@ -0,0 +1,38 @@
+#include "sp_cb_functions.h"
+
+#ifndef FLB_TEST_SP_SNAPSHOT
+#define FLB_TEST_SP_SNAPSHOT
+
+/* Tests for 'test_snapshot' */
+struct task_check snapshot_checks[][2] = {
+ {
+ { // Snapshot
+ 0, 0, 0, 0,
+ "snapshot_create",
+ "SELECT * FROM STREAM:FLB LIMIT 5;",
+ cb_snapshot_create
+ },
+ { // Flush
+ 1, 0, 0, 0,
+ "snapshot_purge",
+ "SELECT * FROM STREAM:FLB;",
+ cb_snapshot_purge
+ },
+ },
+ {
+ { // Snapshot
+ 2, 0, 5, 0,
+ "snapshot_create",
+ "SELECT * FROM STREAM:FLB;",
+ cb_snapshot_create
+ },
+ { // Flush
+ 3, 0, 0, 0,
+ "snapshot_purge",
+ "SELECT * FROM STREAM:FLB;",
+ cb_snapshot_purge_time
+ },
+ },
+};
+
+#endif
diff --git a/fluent-bit/tests/internal/include/sp_window.h b/fluent-bit/tests/internal/include/sp_window.h
new file mode 100644
index 00000000..117b1014
--- /dev/null
+++ b/fluent-bit/tests/internal/include/sp_window.h
@@ -0,0 +1,53 @@
+#include "sp_cb_functions.h"
+
+#ifndef FLB_TEST_SP_WINDOW
+#define FLB_TEST_SP_WINDOW
+
+/* Tests for test_window */
+struct task_check window_checks[] = {
+ {
+ 0, FLB_SP_WINDOW_TUMBLING, 5, 0,
+ "window_5_seconds",
+ "SELECT SUM(id), AVG(id) FROM STREAM:FLB WINDOW TUMBLING (5 SECOND) " \
+ "WHERE word3 IS NOT NULL;",
+ cb_window_5_second
+ },
+ {
+ 1, FLB_SP_WINDOW_TUMBLING, 1, 0,
+ "select_aggr_window_tumbling",
+ "SELECT MIN(id), MAX(id), COUNT(*), SUM(bytes), AVG(bytes) " \
+ "FROM STREAM:FLB WINDOW TUMBLING (1 SECOND);",
+ cb_select_aggr,
+ },
+ {
+ 2, FLB_SP_WINDOW_TUMBLING, 1, 0,
+ "select_aggr_window_tumbling_groupby",
+ "SELECT bool, MIN(id), MAX(id), COUNT(*), SUM(bytes), AVG(bytes) " \
+ "FROM STREAM:FLB WINDOW TUMBLING (1 SECOND) WHERE word3 IS NOT NULL " \
+ "GROUP BY bool;",
+ cb_select_groupby,
+ },
+ {
+ 3, FLB_SP_WINDOW_HOPPING, 5, 2,
+ "hopping_window_5_seconds",
+ "SELECT SUM(id), AVG(id) FROM STREAM:FLB WINDOW HOPPING (5 SECOND, " \
+ "ADVANCE BY 2 SECOND) WHERE word3 IS NOT NULL;",
+ cb_hopping_window_5_second
+ },
+ { /* FORECAST */
+ 4, FLB_SP_WINDOW_TUMBLING, 1, 0,
+ "timeseries_forecast_window_tumbling",
+ "SELECT AVG(usage), TIMESERIES_FORECAST(usage, 20) FROM " \
+ "STREAM:FLB WINDOW TUMBLING (5 SECOND);",
+ cb_forecast_tumbling_window
+ },
+ {
+ 5, FLB_SP_WINDOW_HOPPING, 5, 2,
+ "timeseries_forecast_window_hopping",
+ "SELECT AVG(usage), TIMESERIES_FORECAST(usage, 20) FROM " \
+ "STREAM:FLB WINDOW HOPPING (5 SECOND, ADVANCE BY 2 SECOND);",
+ cb_forecast_hopping_window
+ },
+};
+
+#endif
diff --git a/fluent-bit/tests/internal/input_chunk.c b/fluent-bit/tests/internal/input_chunk.c
new file mode 100644
index 00000000..7154b324
--- /dev/null
+++ b/fluent-bit/tests/internal/input_chunk.c
@@ -0,0 +1,516 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fluent-bit/flb_input_chunk.h>
+#include <fluent-bit/flb_storage.h>
+#include <fluent-bit/flb_router.h>
+#include <fluent-bit/flb_time.h>
+#include "flb_tests_internal.h"
+#include "chunkio/chunkio.h"
+#include "data/input_chunk/log/test_buffer_drop_chunks.h"
+
+#define DPATH FLB_TESTS_DATA_PATH "data/input_chunk/"
+#define MAX_LINES 32
+
+int64_t result_time;
+struct tail_test_result {
+ const char *target;
+ int nMatched;
+};
+
+struct tail_file_lines {
+ char *lines[MAX_LINES];
+ int lines_c;
+};
+
+static inline int64_t set_result(int64_t v)
+{
+ int64_t old = __sync_lock_test_and_set(&result_time, v);
+ return old;
+}
+
+static int file_to_buf(const char *path, char **out_buf, size_t *out_size)
+{
+ int ret;
+ long bytes;
+ char *buf;
+ FILE *fp;
+ struct stat st;
+
+ ret = stat(path, &st);
+ if (ret == -1) {
+ return -1;
+ }
+
+ fp = fopen(path, "r");
+ if (!fp) {
+ return -1;
+ }
+
+ buf = flb_malloc(st.st_size);
+ if (!buf) {
+ flb_errno();
+ fclose(fp);
+ return -1;
+ }
+
+ bytes = fread(buf, st.st_size, 1, fp);
+ if (bytes != 1) {
+ flb_errno();
+ flb_free(buf);
+ fclose(fp);
+ return -1;
+ }
+
+ fclose(fp);
+ *out_buf = buf;
+ *out_size = st.st_size;
+
+ return 0;
+}
+
+/* Given a target, lookup the .out file and return it content in a tail_file_lines structure */
+static struct tail_file_lines *get_out_file_content(const char *target)
+{
+ int i;
+ int ret;
+ char file[PATH_MAX];
+ char *p;
+ char *out_buf;
+ size_t out_size;
+ struct tail_file_lines *file_lines = flb_malloc(sizeof (struct tail_file_lines));
+ file_lines->lines_c = 0;
+
+ snprintf(file, sizeof(file) - 1, DPATH "out/%s.out", target);
+
+ ret = file_to_buf(file, &out_buf, &out_size);
+ TEST_CHECK_(ret == 0, "getting output file content: %s", file);
+ if (ret != 0) {
+ file_lines->lines_c = 0;
+ return file_lines;
+ }
+
+ file_lines->lines[file_lines->lines_c++] = out_buf;
+
+ for (i = 0; i < out_size; i++) {
+ // Nullify \n and \r characters
+ p = (char *)(out_buf + i);
+ if (*p == '\n' || *p == '\r') {
+ *p = '\0';
+
+ if (i == out_size - 1) {
+ break;
+ }
+
+ if (*++p != '\0' && *p != '\n' && *p != '\r' && file_lines->lines_c < MAX_LINES) {
+ file_lines->lines[file_lines->lines_c++] = p;
+ }
+ }
+ }
+
+ // printf("Just before return: %s\n", file_lines.lines[0]);
+ return file_lines;
+}
+
+static int cb_check_result(void *record, size_t size, void *data)
+{
+ int i;
+ struct tail_test_result *result;
+ struct tail_file_lines *out;
+
+ result = (struct tail_test_result *) data;
+
+ char *check;
+
+ out = get_out_file_content(result->target);
+ // printf("What we got from function: %s\n", out.lines[0]);
+ if (!out->lines_c) {
+ goto exit;
+ }
+ /*
+ * Our validation is: check that the one of the output lines
+ * in the output record.
+ */
+ for (i = 0; i<out->lines_c; i++) {
+ check = strstr(record, out->lines[i]);
+ if (check != NULL) {
+ result->nMatched++;
+ goto exit;
+ }
+ }
+
+exit:
+ if (size > 0) {
+ flb_free(record);
+ }
+ if (out->lines_c) {
+ flb_free(out->lines[0]);
+ flb_free(out);
+ }
+ return 0;
+}
+
+void do_test(char *system, const char *target, ...)
+{
+ int64_t ret;
+ flb_ctx_t *ctx = NULL;
+ int in_ffd;
+ int out_ffd;
+ char path[PATH_MAX];
+ struct tail_test_result result = {0};
+ char storage_path[PATH_MAX];
+
+ result.nMatched = 0;
+ result.target = target;
+
+ struct flb_lib_out_cb cb;
+ cb.cb = cb_check_result;
+ cb.data = &result;
+
+ /* initialize */
+ set_result(0);
+
+ ctx = flb_create();
+
+ snprintf(storage_path, sizeof(storage_path) - 1, "/tmp/input-chunk-test-%s", target);
+
+ /* create chunks in /tmp folder */
+ ret = flb_service_set(ctx,
+ "Parsers_File", DPATH "parser.conf",
+ "storage.path", storage_path,
+ "Log_Level", "error",
+ NULL);
+ TEST_CHECK_(ret == 0, "setting service options");
+
+ in_ffd = flb_input(ctx, (char *) system, NULL);
+ TEST_CHECK(in_ffd >= 0);
+ TEST_CHECK(flb_input_set(ctx, in_ffd, "tag", "test", NULL) == 0);
+
+ /* Compose path based on target */
+ snprintf(path, sizeof(path) - 1, DPATH "log/%s.log", target);
+ TEST_CHECK_(access(path, R_OK) == 0, "accessing log file: %s", path);
+
+ TEST_CHECK(flb_input_set(ctx, in_ffd,
+ "Path", path,
+ "storage.type", "filesystem",
+ "Parser", "docker",
+ NULL) == 0);
+
+ out_ffd = flb_output(ctx, (char *) "lib", &cb);
+ TEST_CHECK(out_ffd >= 0);
+ TEST_CHECK(flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "format", "json",
+ "storage.total_limit_size", "1K",
+ NULL) == 0);
+
+ TEST_CHECK(flb_service_set(ctx, "Flush", "0.5",
+ "Grace", "1",
+ NULL) == 0);
+
+ /* Start the engine */
+ ret = flb_start(ctx);
+ TEST_CHECK_(ret == 0, "starting engine");
+
+ sleep(1);
+
+ ret = flb_stop(ctx);
+ TEST_CHECK_(ret == 0, "stopping engine");
+
+ if (ctx) {
+ flb_destroy(ctx);
+ }
+}
+
+void flb_test_input_chunk_exceed_limit()
+{
+ /*
+ * For this test, the input is a log file with more than 1000 bytes.
+ * However we set the limit of storage.total_limit_size to be 1K, no
+ * data should be flushed to the destination as we don't have enough
+ * space to buffer the data.
+ */
+ do_test("tail", "a_thousand_plus_one_bytes",
+ NULL);
+}
+
+void flb_test_input_chunk_buffer_valid()
+{
+ do_test("tail", "test_buffer_valid",
+ NULL);
+}
+
+void flb_test_input_chunk_dropping_chunks()
+{
+ int i;
+ int ret;
+ int in_ffd;
+ int out_ffd;
+ int size = sizeof(TEST_BUFFER_DROP_CHUNKS) - 1;
+ flb_ctx_t *ctx;
+ size_t total_bytes;
+ struct flb_input_instance *i_ins;
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct flb_input_chunk *ic;
+ struct flb_task *task;
+
+ /* Create context, flush every second (some checks omitted here) */
+ ctx = flb_create();
+
+ /* create chunks in /tmp folder */
+ ret = flb_service_set(ctx,
+ "flush", "2", "grace", "1",
+ "storage.path", "/tmp/input-chunk-test/",
+ "Log_Level", "error",
+ NULL);
+
+ TEST_CHECK_(ret == 0, "setting service options");
+
+ /* Lib input mode */
+ in_ffd = flb_input(ctx, (char *) "lib", NULL);
+ TEST_CHECK(flb_input_set(ctx, in_ffd,
+ "tag", "test",
+ "storage.type", "filesystem",
+ NULL) == 0);
+
+ /* an invalid output destination */
+ out_ffd = flb_output(ctx, (char *) "http", NULL);
+ flb_output_set(ctx, out_ffd,
+ "match", "test",
+ "Host", "127.0.0.1",
+ "Port", "1",
+ "storage.total_limit_size", "1K",
+ NULL);
+
+ /* Start */
+ ret = flb_start(ctx);
+ TEST_CHECK(ret == 0);
+
+ i_ins = mk_list_entry_first(&ctx->config->inputs,
+ struct flb_input_instance,
+ _head);
+
+ /* Ingest data sample */
+ for (i = 0; i < 10; ++i) {
+ flb_lib_push(ctx, in_ffd, (char *) TEST_BUFFER_DROP_CHUNKS, size);
+ sleep(1);
+ total_bytes = flb_input_chunk_total_size(i_ins);
+ ret = total_bytes > 1000 ? -1 : 0;
+ TEST_CHECK(ret == 0);
+ }
+
+ /* FORCE clean up test tasks*/
+ mk_list_foreach_safe(head, tmp, &i_ins->tasks) {
+ task = mk_list_entry(head, struct flb_task, _head);
+ flb_info("[task] cleanup test task");
+ flb_task_destroy(task, FLB_TRUE);
+ }
+
+ /* clean up test chunks */
+ mk_list_foreach_safe(head, tmp, &i_ins->chunks) {
+ ic = mk_list_entry(head, struct flb_input_chunk, _head);
+ flb_input_chunk_destroy(ic, FLB_TRUE);
+ }
+
+ flb_time_msleep(2100);
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/*
+ * When chunk is set to DOWN from memory, data_size is set to 0 and
+ * cio_chunk_get_content_size(1) returns the data_size. fs_chunks_size
+ * is used to track the size of chunks in filesystem so we need to call
+ * cio_chunk_get_real_size to return the original size in the file system
+ */
+static ssize_t flb_input_chunk_get_real_size(struct flb_input_chunk *ic)
+{
+ ssize_t meta_size;
+ ssize_t size;
+
+ size = cio_chunk_get_real_size(ic->chunk);
+
+ if (size != 0) {
+ return size;
+ }
+
+ // Real size is not synced to chunk yet
+ size = flb_input_chunk_get_size(ic);
+ if (size == 0) {
+ flb_debug("[input chunk] no data in the chunk %s",
+ flb_input_chunk_get_name(ic));
+ return -1;
+ }
+
+ meta_size = cio_meta_size(ic->chunk);
+ size += meta_size
+ /* See https://github.com/edsiper/chunkio#file-layout for more details */
+ + 2 /* HEADER BYTES */
+ + 4 /* CRC32 */
+ + 16 /* PADDING */
+ + 2; /* METADATA LENGTH BYTES */
+
+ return size;
+}
+
+static int gen_buf(msgpack_sbuffer *mp_sbuf, char *buf, size_t buf_size)
+{
+ msgpack_unpacked result;
+ msgpack_packer mp_pck;
+
+ msgpack_unpacked_init(&result);
+
+ /* Initialize local msgpack buffer */
+ msgpack_packer_init(&mp_pck, mp_sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_str_body(&mp_pck, buf, buf_size);
+ msgpack_unpacked_destroy(&result);
+
+ return 0;
+}
+
+static int log_cb(struct cio_ctx *data, int level, const char *file, int line,
+ char *str)
+{
+ if (level == CIO_LOG_ERROR) {
+ flb_error("[fstore] %s", str);
+ }
+ else if (level == CIO_LOG_WARN) {
+ flb_warn("[fstore] %s", str);
+ }
+ else if (level == CIO_LOG_INFO) {
+ flb_info("[fstore] %s", str);
+ }
+ else if (level == CIO_LOG_DEBUG) {
+ flb_debug("[fstore] %s", str);
+ }
+
+ return 0;
+}
+
+/* This tests uses the subsystems of the engine directly
+ * to avoid threading issues when submitting chunks.
+ */
+void flb_test_input_chunk_fs_chunks_size_real()
+{
+ int records;
+ bool have_size_discrepancy = FLB_FALSE;
+ bool has_checked_size = FLB_FALSE;
+ struct flb_input_instance *i_ins;
+ struct flb_output_instance *o_ins;
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct flb_input_chunk *ic;
+ struct flb_task *task;
+ size_t chunk_size = 0;
+ struct flb_config *cfg;
+ struct cio_ctx *cio;
+ msgpack_sbuffer mp_sbuf;
+ char buf[262144];
+ struct mk_event_loop *evl;
+ struct cio_options opts = {0};
+
+ flb_init_env();
+ cfg = flb_config_init();
+ evl = mk_event_loop_create(256);
+
+ TEST_CHECK(evl != NULL);
+ cfg->evl = evl;
+
+ flb_log_create(cfg, FLB_LOG_STDERR, FLB_LOG_DEBUG, NULL);
+
+ i_ins = flb_input_new(cfg, "dummy", NULL, FLB_TRUE);
+ i_ins->storage_type = CIO_STORE_FS;
+
+ cio_options_init(&opts);
+
+ opts.root_path = "/tmp/input-chunk-fs_chunks-size_real";
+ opts.log_cb = log_cb;
+ opts.log_level = CIO_LOG_DEBUG;
+ opts.flags = CIO_OPEN;
+
+ cio = cio_create(&opts);
+ flb_storage_input_create(cio, i_ins);
+ flb_input_init_all(cfg);
+
+ o_ins = flb_output_new(cfg, "http", NULL, FLB_TRUE);
+ // not the right way to do this
+ o_ins->id = 1;
+ TEST_CHECK_(o_ins != NULL, "unable to instance output");
+ flb_output_set_property(o_ins, "match", "*");
+ flb_output_set_property(o_ins, "storage.total_limit_size", "1M");
+
+ TEST_CHECK_((flb_router_io_set(cfg) != -1), "unable to router");
+
+ /* fill up the chunk ... */
+ memset((void *)buf, 0x41, sizeof(buf));
+ msgpack_sbuffer_init(&mp_sbuf);
+ gen_buf(&mp_sbuf, buf, sizeof(buf));
+
+ records = flb_mp_count(buf, sizeof(buf));
+ flb_input_chunk_append_raw(i_ins, FLB_INPUT_LOGS, records, "dummy", 4, (void *)buf, sizeof(buf));
+ msgpack_sbuffer_destroy(&mp_sbuf);
+
+ /* then force a realloc? */
+ memset((void *)buf, 0x42, 256);
+ msgpack_sbuffer_init(&mp_sbuf);
+ gen_buf(&mp_sbuf, buf, 256);
+ flb_input_chunk_append_raw(i_ins, FLB_INPUT_LOGS, 256, "dummy", 4, (void *)buf, 256);
+ msgpack_sbuffer_destroy(&mp_sbuf);
+
+ /* clean up test chunks */
+ mk_list_foreach_safe(head, tmp, &i_ins->chunks) {
+ ic = mk_list_entry(head, struct flb_input_chunk, _head);
+ if (cio_chunk_get_real_size(ic->chunk) != cio_chunk_get_content_size(ic->chunk)) {
+ have_size_discrepancy = FLB_TRUE;
+ }
+ chunk_size += flb_input_chunk_get_real_size(ic);
+ }
+
+ TEST_CHECK_(have_size_discrepancy == FLB_TRUE, "need a size discrepancy");
+
+ /* check fs_chunks_size for output plugins against logical and
+ * physical size
+ */
+ mk_list_foreach_safe(head, tmp, &ic->in->config->outputs) {
+ o_ins = mk_list_entry(head, struct flb_output_instance, _head);
+ flb_info("[input chunk test] chunk_size=%zu fs_chunk_size=%zu", chunk_size,
+ o_ins->fs_chunks_size);
+ has_checked_size = FLB_TRUE;
+ TEST_CHECK_(chunk_size == o_ins->fs_chunks_size, "fs_chunks_size must match total real size");
+ }
+ TEST_CHECK_(has_checked_size == FLB_TRUE, "need to check size discrepancy");
+
+ /* FORCE clean up test tasks*/
+ mk_list_foreach_safe(head, tmp, &i_ins->tasks) {
+ task = mk_list_entry(head, struct flb_task, _head);
+ flb_info("[task] cleanup test task");
+ flb_task_destroy(task, FLB_TRUE);
+ }
+
+ /* clean up test chunks */
+ mk_list_foreach_safe(head, tmp, &i_ins->chunks) {
+ ic = mk_list_entry(head, struct flb_input_chunk, _head);
+ flb_input_chunk_destroy(ic, FLB_TRUE);
+ }
+
+ cio_destroy(cio);
+ flb_router_exit(cfg);
+ flb_input_exit_all(cfg);
+ flb_output_exit(cfg);
+ flb_config_exit(cfg);
+}
+
+/* Test list */
+TEST_LIST = {
+ {"input_chunk_exceed_limit", flb_test_input_chunk_exceed_limit},
+ {"input_chunk_buffer_valid", flb_test_input_chunk_buffer_valid},
+ {"input_chunk_dropping_chunks", flb_test_input_chunk_dropping_chunks},
+ {"input_chunk_fs_chunk_size_real", flb_test_input_chunk_fs_chunks_size_real},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/internal/log.c b/fluent-bit/tests/internal/log.c
new file mode 100644
index 00000000..662df98e
--- /dev/null
+++ b/fluent-bit/tests/internal/log.c
@@ -0,0 +1,154 @@
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_log.h>
+
+#include "flb_tests_internal.h"
+
+#define TIMEOUT 5
+#define TEST_RECORD_01 "this is a test message"
+#define TEST_RECORD_01_SIZE sizeof(TEST_RECORD_01) - 1
+
+#define TEST_RECORD_02 "other type of message"
+#define TEST_RECORD_02_SIZE sizeof(TEST_RECORD_02) - 1
+
+static int check_interval(int timeout, int *interval)
+{
+ if (!TEST_CHECK( (*interval >= timeout - 1) && *interval <= timeout) ) {
+ TEST_MSG("interval error. got=%d expect=%d-%d", *interval, timeout -1 ,timeout);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int update_and_check_interval(int timeout, int ret, int *interval)
+{
+ int ret_val;
+
+ if (ret == FLB_TRUE) {
+ *interval += 1;
+ return 0;
+ }
+
+ /* false means timeout. check interval. */
+ ret_val = check_interval(timeout, interval);
+ *interval = 0; /* reset interval */
+
+ return ret_val;
+}
+
+static void cache_basic_timeout()
+{
+ int i;
+ int ret;
+ int ret_1;
+ int ret_2;
+ int timeout = 5;
+ int interval1 = 0;
+ int interval2 = 0;
+ struct flb_log_cache *cache;
+ struct flb_log_cache_entry *entry;
+
+ printf("\n");
+
+ cache = flb_log_cache_create(10, 0);
+ TEST_CHECK(cache == NULL);
+
+ cache = flb_log_cache_create(5, 4);
+ TEST_CHECK(cache != NULL);
+
+ /* cache must be empty */
+ entry = flb_log_cache_exists(cache, TEST_RECORD_01, TEST_RECORD_01_SIZE);
+ TEST_CHECK(entry == NULL);
+
+ /* upon trying to check for a suppress and if not found, it must be added */
+ ret_1 = flb_log_cache_check_suppress(cache, TEST_RECORD_01, TEST_RECORD_01_SIZE);
+ TEST_CHECK(ret_1 == FLB_FALSE);
+
+ /* double check that it was added */
+ entry = flb_log_cache_exists(cache, TEST_RECORD_01, TEST_RECORD_01_SIZE);
+ TEST_CHECK(entry != NULL);
+
+ printf("------------------------\n");
+
+ /* reset */
+ flb_log_cache_destroy(cache);
+
+ /* create a new cache */
+ cache = flb_log_cache_create(timeout, 4);
+ TEST_CHECK(cache != NULL);
+
+ ret_1 = flb_log_cache_check_suppress(cache, TEST_RECORD_01, TEST_RECORD_01_SIZE);
+ ret_2 = flb_log_cache_check_suppress(cache, TEST_RECORD_02, TEST_RECORD_02_SIZE);
+ TEST_CHECK(ret_1 == FLB_FALSE);
+ TEST_CHECK(ret_2 == FLB_FALSE);
+ sleep(1);
+ interval1++;
+ interval2++;
+
+ for (i = 1; i < 10; i++) {
+ ret_1 = flb_log_cache_check_suppress(cache, TEST_RECORD_01, TEST_RECORD_01_SIZE);
+ ret = update_and_check_interval(timeout, ret_1, &interval1);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("update_and_check_interval for TEST_RECORD_01 failed. i=%d", i);
+ }
+
+ ret_2 = flb_log_cache_check_suppress(cache, TEST_RECORD_02, TEST_RECORD_02_SIZE);
+ ret = update_and_check_interval(timeout, ret_2, &interval2);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("update_and_check_interval for TEST_RECORD_02 failed. i=%d", i);
+ }
+
+ sleep(1);
+ }
+ ret_1 = flb_log_cache_check_suppress(cache, TEST_RECORD_01, TEST_RECORD_01_SIZE);
+ ret = update_and_check_interval(timeout, ret_1, &interval1);
+ TEST_CHECK(ret == 0);
+
+ ret_2 = flb_log_cache_check_suppress(cache, TEST_RECORD_02, TEST_RECORD_02_SIZE);
+ ret = update_and_check_interval(timeout, ret_2, &interval2);
+ TEST_CHECK(ret == 0);
+
+ flb_log_cache_destroy(cache);
+}
+
+static void cache_one_slot()
+{
+ int i;
+ int ret_1;
+ int ret_2;
+ struct flb_log_cache *cache;
+
+ printf("\n");
+
+ cache = flb_log_cache_create(2, 1);
+ TEST_CHECK(cache != NULL);
+
+ for (i = 0; i < 10; i++) {
+
+ if (i == 0) {
+ ret_1 = flb_log_cache_check_suppress(cache, TEST_RECORD_01, TEST_RECORD_01_SIZE);
+ TEST_CHECK(ret_1 == FLB_FALSE);
+
+ ret_1 = flb_log_cache_check_suppress(cache, TEST_RECORD_01, TEST_RECORD_01_SIZE);
+ TEST_CHECK(ret_1 == FLB_TRUE);
+ }
+ else {
+ ret_2 = flb_log_cache_check_suppress(cache, TEST_RECORD_02, TEST_RECORD_02_SIZE);
+ ret_1 = flb_log_cache_check_suppress(cache, TEST_RECORD_01, TEST_RECORD_01_SIZE);
+
+ TEST_CHECK(ret_1 == FLB_FALSE);
+ TEST_CHECK(ret_2 == FLB_FALSE);
+ }
+
+ sleep(1);
+ }
+
+ flb_log_cache_destroy(cache);
+}
+
+TEST_LIST = {
+ { "cache_basic_timeout" , cache_basic_timeout },
+ { "cache_one_slot" , cache_one_slot },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/log_event_decoder.c b/fluent-bit/tests/internal/log_event_decoder.c
new file mode 100644
index 00000000..6f93edcf
--- /dev/null
+++ b/fluent-bit/tests/internal/log_event_decoder.c
@@ -0,0 +1,281 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2023 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_time.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_log_event_decoder.h>
+#include <msgpack.h>
+#include <string.h>
+
+#include "flb_tests_internal.h"
+
+static int pack_event_time(msgpack_packer *pck, struct flb_time *tm)
+{
+ char ext_data[8] = {0};
+ uint32_t tmp;
+
+ /* event time */
+ tmp = htonl((uint32_t)tm->tm.tv_sec); /* second from epoch */
+ memcpy(&ext_data, &tmp, 4);
+ tmp = htonl((uint32_t)tm->tm.tv_nsec);/* nanosecond */
+ memcpy(&ext_data[4], &tmp, 4);
+
+ msgpack_pack_ext(pck, 8, 0);
+ msgpack_pack_ext_body(pck, ext_data, sizeof(ext_data));
+
+ return 0;
+}
+
+void create_destroy()
+{
+ struct flb_log_event_decoder *dec = NULL;
+ char buf[256] = {0};
+
+ dec = flb_log_event_decoder_create(&buf[0], sizeof(buf));
+ if (!TEST_CHECK(dec != NULL)) {
+ TEST_MSG("flb_log_event_decoder_create failed");
+ return;
+ }
+
+ flb_log_event_decoder_destroy(dec);
+}
+
+void init_destroy()
+{
+ struct flb_log_event_decoder dec;
+ char buf[256] = {0};
+ int ret;
+
+ ret = flb_log_event_decoder_init(&dec, &buf[0], sizeof(buf));
+ if (!TEST_CHECK(ret == FLB_EVENT_DECODER_SUCCESS)) {
+ TEST_MSG("flb_log_event_decoder_init failed. ret=%s",
+ flb_log_event_decoder_get_error_description(ret));
+ return;
+ }
+
+ flb_log_event_decoder_destroy(&dec);
+}
+
+void decode_timestamp()
+{
+ struct flb_time tm;
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ msgpack_unpacked result;
+ size_t offset = 0;
+
+ int ret;
+
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+ msgpack_pack_int64(&pck, 123456);
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, sbuf.data, sbuf.size, &offset);
+
+ ret = flb_log_event_decoder_decode_timestamp(&result.data, &tm);
+ if (!TEST_CHECK(ret == FLB_EVENT_DECODER_SUCCESS)) {
+ TEST_MSG("flb_log_event_decoder_timestamp failed. ret=%s",
+ flb_log_event_decoder_get_error_description(ret));
+ return;
+ }
+ if (!TEST_CHECK(tm.tm.tv_sec == 123456 && tm.tm.tv_nsec == 0)) {
+ TEST_MSG("timestamp error. tv_sec=%ld tv_nsec=%lu", tm.tm.tv_sec, tm.tm.tv_nsec);
+ return;
+ }
+
+ msgpack_unpacked_init(&result);
+ msgpack_sbuffer_clear(&sbuf);
+
+ /* event time */
+ flb_time_set(&tm, 123456, 123456);
+ pack_event_time(&pck, &tm);
+
+ offset = 0;
+ msgpack_unpack_next(&result, sbuf.data, sbuf.size, &offset);
+
+ flb_time_zero(&tm);
+ ret = flb_log_event_decoder_decode_timestamp(&result.data, &tm);
+ if (!TEST_CHECK(ret == FLB_EVENT_DECODER_SUCCESS)) {
+ TEST_MSG("flb_log_event_decoder_timestamp failed. ret=%s",
+ flb_log_event_decoder_get_error_description(ret));
+ return;
+ }
+ if (!TEST_CHECK(tm.tm.tv_sec == 123456 && tm.tm.tv_nsec == 123456)) {
+ TEST_MSG("timestamp error. tv_sec=%ld tv_nsec=%lu", tm.tm.tv_sec, tm.tm.tv_nsec);
+ return;
+ }
+
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&sbuf);
+}
+
+void decode_object()
+{
+ struct flb_log_event_decoder dec;
+ struct flb_log_event event;
+ int ret;
+ struct flb_time tm;
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ msgpack_unpacked result;
+ size_t offset = 0;
+ char *json = NULL;
+
+ flb_time_set(&tm, 123456, 123456);
+
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+
+ /* [[123456.123456, {}], {"key1":"val1", "key2":"val2"}] */
+ msgpack_pack_array(&pck, 2);
+ msgpack_pack_array(&pck, 2);
+ pack_event_time(&pck, &tm);
+ msgpack_pack_map(&pck, 0);
+ msgpack_pack_map(&pck, 2);
+
+ msgpack_pack_str(&pck, 4);
+ msgpack_pack_str_body(&pck, "key1", 4);
+ msgpack_pack_str(&pck, 4);
+ msgpack_pack_str_body(&pck, "val1", 4);
+
+ msgpack_pack_str(&pck, 4);
+ msgpack_pack_str_body(&pck, "key2", 4);
+ msgpack_pack_str(&pck, 4);
+ msgpack_pack_str_body(&pck, "val2", 4);
+
+ msgpack_unpacked_init(&result);
+ ret = msgpack_unpack_next(&result, sbuf.data, sbuf.size, &offset);
+ if (!TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS)) {
+ TEST_MSG("msgpack_unpack_next failed");
+ return;
+ }
+
+ ret = flb_event_decoder_decode_object(&dec, &event, &result.data);
+ if (!TEST_CHECK(ret == FLB_EVENT_DECODER_SUCCESS)) {
+ TEST_MSG("flb_log_event_decoder_decode_object failed. ret=%s",
+ flb_log_event_decoder_get_error_description(ret));
+ return;
+ }
+
+ if (!TEST_CHECK(flb_time_equal(&tm, &event.timestamp))) {
+ TEST_MSG("timestamp mismatch");
+ return;
+ }
+
+ json = flb_msgpack_to_json_str(4096, event.body);
+ if (!TEST_CHECK(json != NULL)) {
+ TEST_MSG("flb_msgpack_to_json_str error");
+ return;
+ }
+ if (!TEST_CHECK(strstr(json, "\"key1\":\"val1\"") != NULL)) {
+ TEST_MSG("\"key1\":\"val1\" is missing. json=%s", json);
+ return;
+ }
+ if (!TEST_CHECK(strstr(json, "\"key2\":\"val2\"") != NULL)) {
+ TEST_MSG("\"key2\":\"val2\" is missing. json=%s", json);
+ return;
+ }
+
+ flb_free(json);
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&sbuf);
+}
+
+void decoder_next()
+{
+ struct flb_log_event_decoder dec;
+ struct flb_log_event event;
+ int ret;
+ struct flb_time tm;
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ char *json = NULL;
+
+ flb_time_set(&tm, 123456, 123456);
+
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+
+ /* [[123456.123456, {}], {"key1":"val1", "key2":"val2"}] */
+ msgpack_pack_array(&pck, 2);
+ msgpack_pack_array(&pck, 2);
+ pack_event_time(&pck, &tm);
+ msgpack_pack_map(&pck, 0);
+ msgpack_pack_map(&pck, 2);
+
+ msgpack_pack_str(&pck, 4);
+ msgpack_pack_str_body(&pck, "key1", 4);
+ msgpack_pack_str(&pck, 4);
+ msgpack_pack_str_body(&pck, "val1", 4);
+
+ msgpack_pack_str(&pck, 4);
+ msgpack_pack_str_body(&pck, "key2", 4);
+ msgpack_pack_str(&pck, 4);
+ msgpack_pack_str_body(&pck, "val2", 4);
+
+
+ ret = flb_log_event_decoder_init(&dec, (char *)sbuf.data, sbuf.size);
+ if (!TEST_CHECK(ret == FLB_EVENT_DECODER_SUCCESS)) {
+ TEST_MSG("flb_log_event_decoder_init failed. ret=%s",
+ flb_log_event_decoder_get_error_description(ret));
+ return;
+ }
+
+ ret = flb_log_event_decoder_next(&dec, &event);
+ if (!TEST_CHECK(ret == FLB_EVENT_DECODER_SUCCESS)) {
+ TEST_MSG("flb_log_event_decoder_next failed. ret=%s",
+ flb_log_event_decoder_get_error_description(ret));
+ return;
+ }
+ if (!TEST_CHECK(flb_time_equal(&tm, &event.timestamp))) {
+ TEST_MSG("timestamp mismatch");
+ return;
+ }
+
+ json = flb_msgpack_to_json_str(4096, event.body);
+ if (!TEST_CHECK(json != NULL)) {
+ TEST_MSG("flb_msgpack_to_json_str error");
+ return;
+ }
+ if (!TEST_CHECK(strstr(json, "\"key1\":\"val1\"") != NULL)) {
+ TEST_MSG("\"key1\":\"val1\" is missing. json=%s", json);
+ return;
+ }
+ if (!TEST_CHECK(strstr(json, "\"key2\":\"val2\"") != NULL)) {
+ TEST_MSG("\"key2\":\"val2\" is missing. json=%s", json);
+ return;
+ }
+
+ flb_free(json);
+ flb_log_event_decoder_destroy(&dec);
+ msgpack_sbuffer_destroy(&sbuf);
+}
+
+
+
+TEST_LIST = {
+ { "create_destroy", create_destroy },
+ { "init_destroy", init_destroy },
+ { "decode_timestamp", decode_timestamp },
+ { "decode_object", decode_object },
+ { "decoder_next", decoder_next },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/lua.c b/fluent-bit/tests/internal/lua.c
new file mode 100644
index 00000000..122748fb
--- /dev/null
+++ b/fluent-bit/tests/internal/lua.c
@@ -0,0 +1,325 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_lua.h>
+#include <lauxlib.h>
+#include <lua.h>
+
+#include "flb_tests_internal.h"
+#include "mpack/mpack.h"
+#include "msgpack/object.h"
+#include "msgpack/pack.h"
+#include "msgpack/sbuffer.h"
+
+/* Helper lua function which returns a string representation of lua objects.
+ * Tables are stringified in key order (lua table iteration order is not deterministic. */
+const char lua_stringify_object_helper[] = (""
+"function stringify(o)\n"
+" if type(o) == 'table' then\n"
+" local keys = {}\n"
+" for k in pairs(o) do table.insert(keys, k) end\n"
+" table.sort(keys)\n"
+" local s = '{ '\n"
+" for _,k in ipairs(keys) do\n"
+" local v = o[k]\n"
+" s = s .. '['..k..'] = ' .. stringify(v) .. ' '\n"
+" end\n"
+" return s .. '}'\n"
+" else\n"
+" return tostring(o)\n"
+" end\n"
+"end\n");
+
+static lua_State *lua_setup(const char *script)
+{
+ lua_State *ret = luaL_newstate();
+ if (!ret) {
+ flb_error("[lua] error creating new context");
+ return NULL;
+ }
+ luaL_openlibs(ret);
+ luaL_loadstring(ret, lua_stringify_object_helper);
+ if (lua_pcall(ret, 0, 0, 0)) {
+ flb_error("[lua] error executing stringify helper script");
+ lua_close(ret);
+ return NULL;
+ }
+ if (script) {
+ luaL_loadstring(ret, script);
+ if (lua_pcall(ret, 0, 0, 0)) {
+ flb_error("[lua] error executing test script");
+ lua_close(ret);
+ return NULL;
+ }
+ }
+ return ret;
+}
+
+static void check_equals(lua_State *l, const char *expected)
+{
+ /* push the stringify function on the stack */
+ lua_getglobal(l, "stringify");
+ /* swap the top two elements of the stack, so that the function is below the arg */
+ lua_insert(l, -2);
+ /* call the function */
+ if (lua_pcall(l, 1, 1, 0)) {
+ flb_error("[lua] error calling stringify helper function");
+ return;
+ }
+ const char *result = lua_tostring(l, -1);
+ TEST_CHECK(strcmp(result, expected) == 0);
+ TEST_MSG("Expected: %s", expected);
+ TEST_MSG("Actual: %s", result);
+ /* remove the result */
+ lua_pop(l, 1);
+}
+
+static void test_is_valid_func()
+{
+ lua_State *l = lua_setup(NULL);
+ TEST_CHECK(flb_lua_is_valid_func(l, "invalid_function") == false);
+ TEST_CHECK(flb_lua_is_valid_func(l, "stringify") == true);
+ lua_close(l);
+}
+
+static void test_pushtimetable()
+{
+ lua_State *l = lua_setup(NULL);
+ struct flb_time t = {{ 5, 6 }};
+ flb_lua_pushtimetable(l, &t);
+ check_equals(l, "{ [nsec] = 6 [sec] = 5 }");
+ t.tm.tv_nsec = 7;
+ t.tm.tv_sec = 8;
+ flb_lua_pushtimetable(l, &t);
+ check_equals(l, "{ [nsec] = 7 [sec] = 8 }");
+ lua_close(l);
+}
+
+static void test_pushmsgpack()
+{
+ msgpack_packer pck;
+ msgpack_sbuffer sbuf;
+ msgpack_unpacked msg;
+ lua_State *l = lua_setup(NULL);
+
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+ msgpack_pack_array(&pck, 3);
+ msgpack_pack_map(&pck, 1);
+ msgpack_pack_str_with_body(&pck, "key", 3);
+ msgpack_pack_str_with_body(&pck, "value", 5);
+ msgpack_pack_str_with_body(&pck, "msgpack-str", 11);
+ msgpack_pack_int(&pck, 4);
+
+ msgpack_unpacked_init(&msg);
+ msgpack_unpack_next(&msg, sbuf.data, sbuf.size, NULL);
+ flb_lua_pushmsgpack(l, &msg.data);
+ check_equals(l, "{ [1] = { [key] = value } [2] = msgpack-str [3] = 4 }");
+
+ msgpack_unpacked_destroy(&msg);
+ msgpack_sbuffer_destroy(&sbuf);
+ lua_close(l);
+}
+
+static void test_pushmpack()
+{
+ msgpack_packer pck;
+ msgpack_sbuffer sbuf;
+ mpack_reader_t reader;
+ lua_State *l = lua_setup(NULL);
+
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+ msgpack_pack_array(&pck, 3);
+ msgpack_pack_map(&pck, 1);
+ msgpack_pack_str_with_body(&pck, "key", 3);
+ msgpack_pack_str_with_body(&pck, "value", 5);
+ msgpack_pack_str_with_body(&pck, "msgpack-str", 11);
+ msgpack_pack_int(&pck, 4);
+
+ mpack_reader_init_data(&reader, sbuf.data, sbuf.size);
+ flb_lua_pushmpack(l, &reader);
+ check_equals(l, "{ [1] = { [key] = value } [2] = msgpack-str [3] = 4 }");
+
+ msgpack_sbuffer_destroy(&sbuf);
+ lua_close(l);
+}
+
+static void test_tomsgpack()
+{
+ const char expected[] = "[{\"key\"=>\"value\"}, \"msgpack-str\", 4]";
+ char buf[256];
+ msgpack_packer pck;
+ msgpack_sbuffer sbuf;
+ msgpack_unpacked msg;
+ struct flb_lua_l2c_config l2cc;
+ lua_State *l = lua_setup("obj = {{['key']='value'},'msgpack-str',4}");
+
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+ mk_list_init(&l2cc.l2c_types);
+ l2cc.l2c_types_num = 0;
+
+ lua_getglobal(l, "obj");
+ flb_lua_tomsgpack(l, &pck, 0, &l2cc);
+
+ msgpack_unpacked_init(&msg);
+ msgpack_unpack_next(&msg, sbuf.data, sbuf.size, NULL);
+ msgpack_object_print_buffer(buf, sizeof(buf), msg.data);
+
+ TEST_CHECK(strcmp(buf, expected) == 0);
+ TEST_MSG("Expected: %s", expected);
+ TEST_MSG("Actual: %s", buf);
+
+ msgpack_unpacked_destroy(&msg);
+ msgpack_sbuffer_destroy(&sbuf);
+ lua_close(l);
+}
+
+static void test_tompack()
+{
+ const char expected[] = "[{\"key\"=>\"value\"}, \"msgpack-str\", 4]";
+ char buf[256];
+ char printbuf[256];
+ mpack_writer_t writer;
+ msgpack_unpacked msg;
+ struct flb_lua_l2c_config l2cc;
+ lua_State *l = lua_setup("obj = {{['key']='value'},'msgpack-str',4}");
+
+ mpack_writer_init(&writer, buf, sizeof(buf));
+ mk_list_init(&l2cc.l2c_types);
+ l2cc.l2c_types_num = 0;
+
+ lua_getglobal(l, "obj");
+ flb_lua_tompack(l, &writer, 0, &l2cc);
+
+ msgpack_unpacked_init(&msg);
+ msgpack_unpack_next(&msg, writer.buffer, writer.position - writer.buffer, NULL);
+ msgpack_object_print_buffer(printbuf, sizeof(printbuf), msg.data);
+
+ TEST_CHECK(strcmp(printbuf, expected) == 0);
+ TEST_MSG("Expected: %s", expected);
+ TEST_MSG("Actual: %s", buf);
+
+ msgpack_unpacked_destroy(&msg);
+ lua_close(l);
+}
+
+
+static void test_lua_arraylength()
+{
+ lua_State *l;
+ int i;
+ int len;
+ int size = 10;
+
+ l = luaL_newstate();
+ if (!TEST_CHECK(l != NULL)) {
+ TEST_MSG("luaL_newstate faild");
+ return;
+ }
+ luaL_openlibs(l);
+
+ /* create array. */
+ lua_createtable(l, size, 0);
+
+ for (i=1; i<=size; i++) {
+ lua_pushinteger(l, i); /* set an index of array */
+ lua_pushinteger(l, 3+i); /* set a value */
+ lua_settable(l, -3); /* points created table */
+ }
+
+ len = flb_lua_arraylength(l, -1);
+ if (!TEST_CHECK(len == size)) {
+ TEST_MSG("size error. got=%d expect=%d", len, size);
+ }
+ lua_pop(l, 1);
+
+ lua_close(l);
+}
+
+static void test_lua_arraylength_with_index()
+{
+ lua_State *l;
+ int i;
+ int len;
+ int size = 10;
+
+ l = luaL_newstate();
+ if (!TEST_CHECK(l != NULL)) {
+ TEST_MSG("luaL_newstate faild");
+ return;
+ }
+ luaL_openlibs(l);
+
+ /* create array. */
+ lua_createtable(l, size, 0);
+
+ for (i=1; i<=size; i++) {
+ lua_pushinteger(l, i); /* set an index of array */
+ lua_pushinteger(l, 3+i); /* set a value */
+ lua_settable(l, -3); /* points created table */
+ }
+
+ /* push 2 values */
+ lua_pushinteger(l, 100);
+ lua_pushinteger(l, 101);
+
+ len = flb_lua_arraylength(l, -3); /* points array. -1 points 101, -2 points 100 */
+ if (!TEST_CHECK(len == size)) {
+ TEST_MSG("size error. got=%d expect=%d", len, size);
+ }
+ lua_pop(l, 1);
+
+ lua_close(l);
+}
+
+static void test_lua_arraylength_for_array_contains_nil()
+{
+ lua_State *l;
+ int i;
+ int len;
+ int size = 10;
+
+ l = luaL_newstate();
+ if (!TEST_CHECK(l != NULL)) {
+ TEST_MSG("luaL_newstate faild");
+ return;
+ }
+ luaL_openlibs(l);
+
+ /* create array. */
+ lua_createtable(l, size, 0);
+
+ for (i=1; i<=size; i++) {
+ lua_pushinteger(l, i); /* set an index of array */
+ if (i == 7) {
+ lua_pushnil(l);
+ }
+ else {
+ lua_pushinteger(l, 3+i); /* set a value */
+ }
+ lua_settable(l, -3); /* points created table */
+ }
+
+ len = flb_lua_arraylength(l, -1);
+ if (!TEST_CHECK(len == size)) {
+ TEST_MSG("size error. got=%d expect=%d", len, size);
+ }
+ lua_pop(l, 1);
+
+ lua_close(l);
+}
+
+
+TEST_LIST = {
+ { "lua_is_valid_func" , test_is_valid_func},
+ { "lua_pushtimetable" , test_pushtimetable},
+ { "lua_pushmsgpack" , test_pushmsgpack },
+ { "lua_pushmpack" , test_pushmpack },
+ { "lua_tomsgpack" , test_tomsgpack },
+ { "lua_tompack" , test_tompack },
+ { "lua_arraylength" , test_lua_arraylength },
+ { "lua_arraylength_with_index" , test_lua_arraylength_with_index },
+ { "lua_arraylength_for_array_contains_nil", test_lua_arraylength_for_array_contains_nil},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/metrics.c b/fluent-bit/tests/internal/metrics.c
new file mode 100644
index 00000000..08f8bc70
--- /dev/null
+++ b/fluent-bit/tests/internal/metrics.c
@@ -0,0 +1,83 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_error.h>
+#include <fluent-bit/flb_metrics.h>
+
+#include "flb_tests_internal.h"
+
+static void test_create_usage()
+{
+ int ret;
+ int id_1;
+ int id_2;
+ int id_3;
+ struct flb_metric *m;
+ struct flb_metrics *ctx;
+
+ /* Create and destroy */
+ ctx = flb_metrics_create("metrics");
+ TEST_CHECK(ctx != NULL);
+ ret = flb_metrics_destroy(ctx);
+ TEST_CHECK(ret == 0);
+
+ /* Register one metric */
+ ctx = flb_metrics_create("metrics");
+ id_1 = flb_metrics_add(-1, "sample", ctx);
+ TEST_CHECK(id_1 == 0);
+ ret = flb_metrics_destroy(ctx);
+ TEST_CHECK(ret == 1);
+
+ /* Duplicate metric ID, it should fail */
+ ctx = flb_metrics_create("metrics");
+ id_1 = flb_metrics_add(-1, "sample 1", ctx);
+ id_2 = flb_metrics_add(0, "sample 2", ctx);
+ TEST_CHECK(id_2 == -1);
+ ret = flb_metrics_destroy(ctx);
+ TEST_CHECK(ret == 1);
+
+ /* Auto ID */
+ ctx = flb_metrics_create("ctx");
+ id_1 = flb_metrics_add(-1, "sample 1", ctx);
+ id_2 = flb_metrics_add(-1, "sample 2", ctx);
+ id_3 = flb_metrics_add(-1, "sample 3", ctx);
+ TEST_CHECK(id_1 == 0);
+ TEST_CHECK(id_2 == 1);
+ TEST_CHECK(id_3 == 2);
+ ret = flb_metrics_destroy(ctx);
+ TEST_CHECK(ret == 3);
+
+ /* Update values */
+ ctx = flb_metrics_create("ctx");
+ id_1 = flb_metrics_add(-1, "sample 1", ctx);
+ id_2 = flb_metrics_add(-1, "sample 2", ctx);
+ id_3 = flb_metrics_add(5, "sample 3", ctx);
+ TEST_CHECK(id_1 == 0);
+ TEST_CHECK(id_2 == 1);
+ TEST_CHECK(id_3 == 5);
+
+ ret = flb_metrics_sum(id_1, 2001, ctx);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_metrics_sum(id_2, 2017, ctx);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_metrics_sum(1234, 0, ctx);
+ TEST_CHECK(ret == -1);
+
+ ret = flb_metrics_sum(id_3, 1, ctx);
+ TEST_CHECK(ret == 0);
+
+ m = flb_metrics_get_id(id_3, ctx);
+ TEST_CHECK(m != NULL);
+ TEST_CHECK(m->val == 1);
+
+ ret = flb_metrics_destroy(ctx);
+ TEST_CHECK(ret == 3);
+}
+
+TEST_LIST = {
+ { "create_usage", test_create_usage},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/mp.c b/fluent-bit/tests/internal/mp.c
new file mode 100644
index 00000000..5c1e17c2
--- /dev/null
+++ b/fluent-bit/tests/internal/mp.c
@@ -0,0 +1,353 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_compat.h>
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_mp.h>
+#include <msgpack.h>
+
+#include "flb_tests_internal.h"
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define APACHE_10K FLB_TESTS_DATA_PATH "/data/mp/apache_10k.mp"
+
+void test_count()
+{
+ int ret;
+ int count;
+ char *data;
+ size_t len;
+ struct stat st;
+
+ ret = stat(APACHE_10K, &st);
+ if (ret == -1) {
+ exit(1);
+ }
+ len = st.st_size;
+
+ data = mk_file_to_buffer(APACHE_10K);
+ TEST_CHECK(data != NULL);
+
+ count = flb_mp_count(data, len);
+ TEST_CHECK(count == 10000);
+ flb_free(data);
+}
+
+void test_map_header()
+{
+ int i;
+ int ret;
+ size_t off = 0;
+ msgpack_packer mp_pck;
+ msgpack_object root;
+ msgpack_sbuffer mp_sbuf;
+ msgpack_unpacked result;
+ struct flb_mp_map_header mh;
+
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ /* Init map header */
+ flb_mp_map_header_init(&mh, &mp_pck);
+
+ /* Append 1000 items */
+ for (i = 0; i < 100; i++) {
+ flb_mp_map_header_append(&mh);
+ msgpack_pack_str(&mp_pck, 3);
+ msgpack_pack_str_body(&mp_pck, "key", 3);
+ msgpack_pack_uint64(&mp_pck, i);
+ }
+ flb_mp_map_header_end(&mh);
+
+ /* Unpack and check */
+ msgpack_unpacked_init(&result);
+ ret = msgpack_unpack_next(&result, mp_sbuf.data, mp_sbuf.size, &off);
+ TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS);
+
+ root = result.data;
+ TEST_CHECK(root.type == MSGPACK_OBJECT_MAP);
+ TEST_CHECK(root.via.array.size == 100);
+
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&mp_sbuf);
+}
+
+void test_accessor_keys_remove()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *buf;
+ size_t size;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_mp_accessor *mpa;
+ struct mk_list patterns;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\","
+ " \"extra\": false\""
+ "}}]}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &buf, &size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack the content */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, buf, size, &off);
+ map = result.data;
+
+ /* Create list of patterns */
+ flb_slist_create(&patterns);
+ flb_slist_add(&patterns, "$kubernetes[2]['annotations']['fluentbit.io/tag']");
+ flb_slist_add(&patterns, "$key1");
+
+ /* Create mp accessor */
+ mpa = flb_mp_accessor_create(&patterns);
+ TEST_CHECK(mpa != NULL);
+
+ /* Remove the entry that matches the pattern(s) */
+ ret = flb_mp_accessor_keys_remove(mpa, &map, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ printf("\n=== ORIGINAL ===\n");
+ flb_pack_print(buf, size);
+ flb_free(buf);
+
+ printf("=== FINAL MAP ===\n");
+ if (ret == FLB_TRUE) {
+ flb_pack_print(out_buf, out_size);
+ flb_free(out_buf);
+ }
+
+ flb_mp_accessor_destroy(mpa);
+ flb_slist_destroy(&patterns);
+ msgpack_unpacked_destroy(&result);
+}
+
+/* https://github.com/fluent/fluent-bit/issues/5546 */
+void test_keys_remove_subkey_key()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *buf;
+ size_t size;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char final_json[2048] = {0};
+ msgpack_unpacked result;
+ msgpack_unpacked result_final;
+ msgpack_object map;
+ struct flb_mp_accessor *mpa;
+ struct mk_list patterns;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\","
+ " \"extra\": false\""
+ "}}]}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &buf, &size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack the content */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, buf, size, &off);
+ map = result.data;
+
+ /* Create list of patterns */
+ flb_slist_create(&patterns);
+
+ /* sub key -> key */
+ flb_slist_add(&patterns, "$kubernetes[2]['annotations']['fluentbit.io/tag']");
+ flb_slist_add(&patterns, "$kubernetes");
+
+
+ /* Create mp accessor */
+ mpa = flb_mp_accessor_create(&patterns);
+ TEST_CHECK(mpa != NULL);
+
+ /* Remove the entry that matches the pattern(s) */
+ ret = flb_mp_accessor_keys_remove(mpa, &map, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ printf("\n=== ORIGINAL ===\n");
+ flb_pack_print(buf, size);
+ flb_free(buf);
+
+ printf("=== FINAL MAP ===\n");
+ if (ret == FLB_TRUE) {
+ flb_pack_print(out_buf, out_size);
+ }
+ msgpack_unpacked_destroy(&result);
+
+ off = 0;
+ msgpack_unpacked_init(&result_final);
+ msgpack_unpack_next(&result_final, out_buf, out_size, &off);
+ flb_msgpack_to_json(&final_json[0], sizeof(final_json), &result_final.data);
+
+ if (!TEST_CHECK(strstr(&final_json[0] ,"kubernetes") == NULL)) {
+ TEST_MSG("kubernetes field should be removed");
+ }
+
+ msgpack_unpacked_destroy(&result_final);
+
+ flb_free(out_buf);
+ flb_mp_accessor_destroy(mpa);
+ flb_slist_destroy(&patterns);
+
+}
+
+void remove_subkey_keys(char *list[], int list_size, int index_start)
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *buf;
+ size_t size;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char final_json[2048] = {0};
+ msgpack_unpacked result;
+ msgpack_unpacked result_final;
+ msgpack_object map;
+ struct flb_mp_accessor *mpa;
+ struct mk_list patterns;
+ int i;
+ int count = 0;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\","
+ " \"extra\": false\""
+ "}}]}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &buf, &size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack the content */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, buf, size, &off);
+ map = result.data;
+
+ /* Create list of patterns */
+ flb_slist_create(&patterns);
+
+ /* sub keys */
+ for (i=index_start; count<list_size; i++) {
+ if (i>=list_size) {
+ i = 0;
+ }
+ flb_slist_add(&patterns, list[i]);
+ count++;
+ }
+
+ /* Create mp accessor */
+ mpa = flb_mp_accessor_create(&patterns);
+ TEST_CHECK(mpa != NULL);
+
+ /* Remove the entry that matches the pattern(s) */
+ ret = flb_mp_accessor_keys_remove(mpa, &map, (void *) &out_buf, &out_size);
+ TEST_CHECK(ret == FLB_TRUE);
+
+ printf("\n=== ORIGINAL ===\n");
+ flb_pack_print(buf, size);
+ flb_free(buf);
+
+ printf("=== FINAL MAP ===\n");
+ if (ret == FLB_TRUE) {
+ flb_pack_print(out_buf, out_size);
+ }
+ msgpack_unpacked_destroy(&result);
+
+ off = 0;
+ msgpack_unpacked_init(&result_final);
+ msgpack_unpack_next(&result_final, out_buf, out_size, &off);
+ flb_msgpack_to_json(&final_json[0], sizeof(final_json), &result_final.data);
+
+ if (!TEST_CHECK(strstr(&final_json[0] ,"kubernetes") == NULL)) {
+ TEST_MSG("kubernetes field should be removed");
+ }
+
+ msgpack_unpacked_destroy(&result_final);
+
+ flb_free(out_buf);
+ flb_mp_accessor_destroy(mpa);
+ flb_slist_destroy(&patterns);
+}
+
+void test_keys_remove_subkey_keys()
+{
+ char *list[] = {"$kubernetes[2]['annotations']['fluentbit.io/tag']",
+ "$kubernetes[2]['a']",
+ "$kubernetes"};
+ char *list2[] = {"$kubernetes[2]['annotations']['fluentbit.io/tag']",
+ "$kubernetes",
+ "$kubernetes[2]['a']"};
+
+ int size = sizeof(list)/sizeof(char*);
+ int i;
+
+ for (i=0; i<size; i++) {
+ remove_subkey_keys(list, size, i);
+ }
+ for (i=0; i<size; i++) {
+ remove_subkey_keys(list2, size, i);
+ }
+}
+
+TEST_LIST = {
+ {"count" , test_count},
+ {"map_header" , test_map_header},
+ {"accessor_keys_remove" , test_accessor_keys_remove},
+ {"accessor_keys_remove_subkey_key" , test_keys_remove_subkey_key},
+ {"accessor_keys_remove_subkey_keys" , test_keys_remove_subkey_keys},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/multiline.c b/fluent-bit/tests/internal/multiline.c
new file mode 100644
index 00000000..e175e117
--- /dev/null
+++ b/fluent-bit/tests/internal/multiline.c
@@ -0,0 +1,1478 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/multiline/flb_ml.h>
+#include <fluent-bit/multiline/flb_ml_rule.h>
+#include <fluent-bit/multiline/flb_ml_parser.h>
+
+#include "flb_tests_internal.h"
+
+struct record_check {
+ char *buf;
+};
+
+struct expected_result {
+ int current_record;
+ char *key;
+ struct record_check *out_records;
+};
+
+/* Docker */
+struct record_check docker_input[] = {
+ {"{\"log\": \"aa\\n\", \"stream\": \"stdout\", \"time\": \"2021-02-01T16:45:03.01231z\"}"},
+ {"{\"log\": \"aa\\n\", \"stream\": \"stderr\", \"time\": \"2021-02-01T16:45:03.01231z\"}"},
+ {"{\"log\": \"bb\", \"stream\": \"stdout\", \"time\": \"2021-02-01T16:45:03.01232z\"}"},
+ {"{\"log\": \"cc\n\", \"stream\": \"stdout\", \"time\": \"2021-02-01T16:45:03.01233z\"}"},
+ {"{\"log\": \"dd\", \"stream\": \"stderr\", \"time\": \"2021-02-01T16:45:03.01233z\"}"},
+ {"single line to force pending flush of the previous line"},
+ {"{\"log\": \"ee\\n\", \"stream\": \"stderr\", \"time\": \"2021-02-01T16:45:03.01234z\"}"},
+};
+
+struct record_check docker_output[] = {
+ {"aa\n"},
+ {"aa\n"},
+ {"bbcc\n"},
+ {"dd"},
+ {"single line to force pending flush of the previous line"},
+ {"ee\n"},
+};
+
+/* CRI */
+struct record_check cri_input[] = {
+ {"2019-05-07T18:57:50.904275087+00:00 stdout P 1a. some "},
+ {"2019-05-07T18:57:51.904275088+00:00 stdout P multiline "},
+ {"2019-05-07T18:57:52.904275089+00:00 stdout F log"},
+ {"2019-05-07T18:57:50.904275087+00:00 stderr P 1b. some "},
+ {"2019-05-07T18:57:51.904275088+00:00 stderr P multiline "},
+ {"2019-05-07T18:57:52.904275089+00:00 stderr F log"},
+ {"2019-05-07T18:57:53.904275090+00:00 stdout P 2a. another "},
+ {"2019-05-07T18:57:54.904275091+00:00 stdout P multiline "},
+ {"2019-05-07T18:57:55.904275092+00:00 stdout F log"},
+ {"2019-05-07T18:57:53.904275090+00:00 stderr P 2b. another "},
+ {"2019-05-07T18:57:54.904275091+00:00 stderr P multiline "},
+ {"2019-05-07T18:57:55.904275092+00:00 stderr F log"},
+ {"2019-05-07T18:57:56.904275093+00:00 stdout F 3a. non multiline 1"},
+ {"2019-05-07T18:57:57.904275094+00:00 stdout F 4a. non multiline 2"},
+ {"2019-05-07T18:57:56.904275093+00:00 stderr F 3b. non multiline 1"},
+ {"2019-05-07T18:57:57.904275094+00:00 stderr F 4b. non multiline 2"}
+};
+
+struct record_check cri_output[] = {
+ {"1a. some multiline log"},
+ {"1b. some multiline log"},
+ {"2a. another multiline log"},
+ {"2b. another multiline log"},
+ {"3a. non multiline 1"},
+ {"4a. non multiline 2"},
+ {"3b. non multiline 1"},
+ {"4b. non multiline 2"}
+};
+
+/* ENDSWITH */
+struct record_check endswith_input[] = {
+ {"1a. some multiline log \\"},
+ {"1b. some multiline log"},
+ {"2a. another multiline log\\"},
+ {"2b. another multiline log"},
+ {"3a. non multiline 1"},
+ {"4a. non multiline 2"}
+};
+
+struct record_check endswith_output[] = {
+ {"1a. some multiline log \\\n1b. some multiline log\n"},
+ {"2a. another multiline log\\\n2b. another multiline log\n"},
+ {"3a. non multiline 1\n"},
+ {"4a. non multiline 2\n"}
+};
+
+/* Mixed lines of Docker and CRI logs in different streams (stdout/stderr) */
+struct record_check container_mix_input[] = {
+ {"{\"log\": \"a1\\n\", \"stream\": \"stdout\", \"time\": \"2021-02-01T16:45:03.01231z\"}"},
+ {"{\"log\": \"a2\\n\", \"stream\": \"stderr\", \"time\": \"2021-02-01T16:45:03.01231z\"}"},
+ {"{\"log\": \"bb\", \"stream\": \"stdout\", \"time\": \"2021-02-01T16:45:03.01232z\"}"},
+ {"{\"log\": \"cc\", \"stream\": \"stdout\", \"time\": \"2021-02-01T16:45:03.01233z\"}"},
+ {"{\"log\": \"dd\", \"stream\": \"stderr\", \"time\": \"2021-02-01T16:45:03.01232z\"}"},
+ {"{\"log\": \"ee\n\", \"stream\": \"stderr\", \"time\": \"2021-02-01T16:45:03.01233z\"}"},
+ {"2019-05-07T18:57:52.904275089+00:00 stdout F single full"},
+ {"2019-05-07T18:57:50.904275087+00:00 stdout P 1a. some "},
+ {"2019-05-07T18:57:51.904275088+00:00 stdout P multiline "},
+ {"2019-05-07T18:57:52.904275089+00:00 stdout F log"},
+ {"2019-05-07T18:57:50.904275087+00:00 stderr P 1b. some "},
+ {"2019-05-07T18:57:51.904275088+00:00 stderr P multiline "},
+ {"2019-05-07T18:57:52.904275089+00:00 stderr F log"},
+ {"{\"log\": \"dd-out\\n\", \"stream\": \"stdout\", \"time\": \"2021-02-01T16:45:03.01234z\"}"},
+ {"{\"log\": \"dd-err\\n\", \"stream\": \"stderr\", \"time\": \"2021-02-01T16:45:03.01234z\"}"},
+};
+
+struct record_check container_mix_output[] = {
+ {"a1\n"},
+ {"a2\n"},
+ {"ddee\n"},
+ {"bbcc"},
+ {"single full"},
+ {"1a. some multiline log"},
+ {"1b. some multiline log"},
+ {"dd-out\n"},
+ {"dd-err\n"},
+};
+
+/* Java stacktrace detection */
+struct record_check java_input[] = {
+ {"Exception in thread \"main\" java.lang.IllegalStateException: ..null property\n"},
+ {" at com.example.myproject.Author.getBookIds(xx.java:38)\n"},
+ {" at com.example.myproject.Bootstrap.main(Bootstrap.java:14)\n"},
+ {"Caused by: java.lang.NullPointerException\n"},
+ {" at com.example.myproject.Book.getId(Book.java:22)\n"},
+ {" at com.example.myproject.Author.getBookIds(Author.java:35)\n"},
+ {" ... 1 more\n"},
+ {"single line\n"}
+};
+
+struct record_check java_output[] = {
+ {
+ "Exception in thread \"main\" java.lang.IllegalStateException: ..null property\n"
+ " at com.example.myproject.Author.getBookIds(xx.java:38)\n"
+ " at com.example.myproject.Bootstrap.main(Bootstrap.java:14)\n"
+ "Caused by: java.lang.NullPointerException\n"
+ " at com.example.myproject.Book.getId(Book.java:22)\n"
+ " at com.example.myproject.Author.getBookIds(Author.java:35)\n"
+ " ... 1 more\n"
+ },
+ {
+ "single line\n"
+ }
+};
+
+struct record_check ruby_input[] = {
+ {"/app/config/routes.rb:6:in `/': divided by 0 (ZeroDivisionError)"},
+ {" from /app/config/routes.rb:6:in `block in <main>'"},
+ {" from /var/lib/gems/3.0.0/gems/actionpack-7.0.4/lib/action_dispatch/routing/route_set.rb:428:in `instance_exec'"},
+ {" from /var/lib/gems/3.0.0/gems/actionpack-7.0.4/lib/action_dispatch/routing/route_set.rb:428:in `eval_block'"},
+ {" from /var/lib/gems/3.0.0/gems/actionpack-7.0.4/lib/action_dispatch/routing/route_set.rb:410:in `draw'"},
+ {" from /app/config/routes.rb:1:in `<main>'"},
+ {"hello world, not multiline\n"}
+};
+
+struct record_check ruby_output[] = {
+ {
+ "/app/config/routes.rb:6:in `/': divided by 0 (ZeroDivisionError)\n"
+ " from /app/config/routes.rb:6:in `block in <main>'\n"
+ " from /var/lib/gems/3.0.0/gems/actionpack-7.0.4/lib/action_dispatch/routing/route_set.rb:428:in `instance_exec'\n"
+ " from /var/lib/gems/3.0.0/gems/actionpack-7.0.4/lib/action_dispatch/routing/route_set.rb:428:in `eval_block'\n"
+ " from /var/lib/gems/3.0.0/gems/actionpack-7.0.4/lib/action_dispatch/routing/route_set.rb:410:in `draw'\n"
+ " from /app/config/routes.rb:1:in `<main>'\n"
+ },
+ {"hello world, not multiline\n"}
+};
+
+/* Python stacktrace detection */
+struct record_check python_input[] = {
+ {"Traceback (most recent call last):\n"},
+ {" File \"/base/data/home/runtimes/python27/python27_lib/versions/third_party/webapp2-2.5.2/webapp2.py\", line 1535, in __call__\n"},
+ {" rv = self.handle_exception(request, response, e)\n"},
+ {" File \"/base/data/home/apps/s~nearfieldspy/1.378705245900539993/nearfieldspy.py\", line 17, in start\n"},
+ {" return get()\n"},
+ {" File \"/base/data/home/apps/s~nearfieldspy/1.378705245900539993/nearfieldspy.py\", line 5, in get\n"},
+ {" raise Exception('spam', 'eggs')\n"},
+ {"Exception: ('spam', 'eggs')\n"},
+ {"hello world, not multiline\n"}
+};
+
+struct record_check python_output[] = {
+ {
+ "Traceback (most recent call last):\n"
+ " File \"/base/data/home/runtimes/python27/python27_lib/versions/third_party/webapp2-2.5.2/webapp2.py\", line 1535, in __call__\n"
+ " rv = self.handle_exception(request, response, e)\n"
+ " File \"/base/data/home/apps/s~nearfieldspy/1.378705245900539993/nearfieldspy.py\", line 17, in start\n"
+ " return get()\n"
+ " File \"/base/data/home/apps/s~nearfieldspy/1.378705245900539993/nearfieldspy.py\", line 5, in get\n"
+ " raise Exception('spam', 'eggs')\n"
+ "Exception: ('spam', 'eggs')\n"
+ },
+ {"hello world, not multiline\n"}
+};
+
+/* Custom example for Elasticsearch stacktrace */
+struct record_check elastic_input[] = {
+ {"[some weird test] IndexNotFoundException[no such index]\n"},
+ {" at org.elasticsearch.cluster.metadata.IndexNameExpressionResolver....\n"},
+ {" at org.elasticsearch.cluster.metadata.IndexNameExpressionResolver.java:133)\n"},
+ {" at org.elasticsearch.action.admin.indices.delete.java:75)\n"},
+ {"another separate log line\n"}
+};
+
+struct record_check elastic_output[] = {
+ {
+ "[some weird test] IndexNotFoundException[no such index]\n"
+ " at org.elasticsearch.cluster.metadata.IndexNameExpressionResolver....\n"
+ " at org.elasticsearch.cluster.metadata.IndexNameExpressionResolver.java:133)\n"
+ " at org.elasticsearch.action.admin.indices.delete.java:75)\n"
+ },
+ {
+ "another separate log line\n"
+ }
+};
+
+/* Go */
+struct record_check go_input[] = {
+ {"panic: my panic\n"},
+ {"\n"},
+ {"goroutine 4 [running]:\n"},
+ {"panic(0x45cb40, 0x47ad70)\n"},
+ {" /usr/local/go/src/runtime/panic.go:542 +0x46c fp=0xc42003f7b8 sp=0xc42003f710 pc=0x422f7c\n"},
+ {"main.main.func1(0xc420024120)\n"},
+ {" foo.go:6 +0x39 fp=0xc42003f7d8 sp=0xc42003f7b8 pc=0x451339\n"},
+ {"runtime.goexit()\n"},
+ {" /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003f7e0 sp=0xc42003f7d8 pc=0x44b4d1\n"},
+ {"created by main.main\n"},
+ {" foo.go:5 +0x58\n"},
+ {"\n"},
+ {"goroutine 1 [chan receive]:\n"},
+ {"runtime.gopark(0x4739b8, 0xc420024178, 0x46fcd7, 0xc, 0xc420028e17, 0x3)\n"},
+ {" /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc420053e30 sp=0xc420053e00 pc=0x42503c\n"},
+ {"runtime.goparkunlock(0xc420024178, 0x46fcd7, 0xc, 0x1000f010040c217, 0x3)\n"},
+ {" /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc420053e70 sp=0xc420053e30 pc=0x42512e\n"},
+ {"runtime.chanrecv(0xc420024120, 0x0, 0xc420053f01, 0x4512d8)\n"},
+ {" /usr/local/go/src/runtime/chan.go:506 +0x304 fp=0xc420053f20 sp=0xc420053e70 pc=0x4046b4\n"},
+ {"runtime.chanrecv1(0xc420024120, 0x0)\n"},
+ {" /usr/local/go/src/runtime/chan.go:388 +0x2b fp=0xc420053f50 sp=0xc420053f20 pc=0x40439b\n"},
+ {"main.main()\n"},
+ {" foo.go:9 +0x6f fp=0xc420053f80 sp=0xc420053f50 pc=0x4512ef\n"},
+ {"runtime.main()\n"},
+ {" /usr/local/go/src/runtime/proc.go:185 +0x20d fp=0xc420053fe0 sp=0xc420053f80 pc=0x424bad\n"},
+ {"runtime.goexit()\n"},
+ {" /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc420053fe8 sp=0xc420053fe0 pc=0x44b4d1\n"},
+ {"\n"},
+ {"goroutine 2 [force gc (idle)]:\n"},
+ {"runtime.gopark(0x4739b8, 0x4ad720, 0x47001e, 0xf, 0x14, 0x1)\n"},
+ {" /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc42003e768 sp=0xc42003e738 pc=0x42503c\n"},
+ {"runtime.goparkunlock(0x4ad720, 0x47001e, 0xf, 0xc420000114, 0x1)\n"},
+ {" /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc42003e7a8 sp=0xc42003e768 pc=0x42512e\n"},
+ {"runtime.forcegchelper()\n"},
+ {" /usr/local/go/src/runtime/proc.go:238 +0xcc fp=0xc42003e7e0 sp=0xc42003e7a8 pc=0x424e5c\n"},
+ {"runtime.goexit()\n"},
+ {" /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003e7e8 sp=0xc42003e7e0 pc=0x44b4d1\n"},
+ {"created by runtime.init.4\n"},
+ {" /usr/local/go/src/runtime/proc.go:227 +0x35\n"},
+ {"\n"},
+ {"goroutine 3 [GC sweep wait]:\n"},
+ {"runtime.gopark(0x4739b8, 0x4ad7e0, 0x46fdd2, 0xd, 0x419914, 0x1)\n"},
+ {" /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc42003ef60 sp=0xc42003ef30 pc=0x42503c\n"},
+ {"runtime.goparkunlock(0x4ad7e0, 0x46fdd2, 0xd, 0x14, 0x1)\n"},
+ {" /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc42003efa0 sp=0xc42003ef60 pc=0x42512e\n"},
+ {"runtime.bgsweep(0xc42001e150)\n"},
+ {" /usr/local/go/src/runtime/mgcsweep.go:52 +0xa3 fp=0xc42003efd8 sp=0xc42003efa0 pc=0x419973\n"},
+ {"runtime.goexit()\n"},
+ {" /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003efe0 sp=0xc42003efd8 pc=0x44b4d1\n"},
+ {"created by runtime.gcenable\n"},
+ {" /usr/local/go/src/runtime/mgc.go:216 +0x58\n"},
+ {"one more line, no multiline"}
+};
+
+struct record_check go_output[] = {
+ {
+ "panic: my panic\n"
+ "\n"
+ "goroutine 4 [running]:\n"
+ "panic(0x45cb40, 0x47ad70)\n"
+ " /usr/local/go/src/runtime/panic.go:542 +0x46c fp=0xc42003f7b8 sp=0xc42003f710 pc=0x422f7c\n"
+ "main.main.func1(0xc420024120)\n"
+ " foo.go:6 +0x39 fp=0xc42003f7d8 sp=0xc42003f7b8 pc=0x451339\n"
+ "runtime.goexit()\n"
+ " /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003f7e0 sp=0xc42003f7d8 pc=0x44b4d1\n"
+ "created by main.main\n"
+ " foo.go:5 +0x58\n"
+ "\n"
+ "goroutine 1 [chan receive]:\n"
+ "runtime.gopark(0x4739b8, 0xc420024178, 0x46fcd7, 0xc, 0xc420028e17, 0x3)\n"
+ " /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc420053e30 sp=0xc420053e00 pc=0x42503c\n"
+ "runtime.goparkunlock(0xc420024178, 0x46fcd7, 0xc, 0x1000f010040c217, 0x3)\n"
+ " /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc420053e70 sp=0xc420053e30 pc=0x42512e\n"
+ "runtime.chanrecv(0xc420024120, 0x0, 0xc420053f01, 0x4512d8)\n"
+ " /usr/local/go/src/runtime/chan.go:506 +0x304 fp=0xc420053f20 sp=0xc420053e70 pc=0x4046b4\n"
+ "runtime.chanrecv1(0xc420024120, 0x0)\n"
+ " /usr/local/go/src/runtime/chan.go:388 +0x2b fp=0xc420053f50 sp=0xc420053f20 pc=0x40439b\n"
+ "main.main()\n"
+ " foo.go:9 +0x6f fp=0xc420053f80 sp=0xc420053f50 pc=0x4512ef\n"
+ "runtime.main()\n"
+ " /usr/local/go/src/runtime/proc.go:185 +0x20d fp=0xc420053fe0 sp=0xc420053f80 pc=0x424bad\n"
+ "runtime.goexit()\n"
+ " /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc420053fe8 sp=0xc420053fe0 pc=0x44b4d1\n"
+ "\n"
+ "goroutine 2 [force gc (idle)]:\n"
+ "runtime.gopark(0x4739b8, 0x4ad720, 0x47001e, 0xf, 0x14, 0x1)\n"
+ " /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc42003e768 sp=0xc42003e738 pc=0x42503c\n"
+ "runtime.goparkunlock(0x4ad720, 0x47001e, 0xf, 0xc420000114, 0x1)\n"
+ " /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc42003e7a8 sp=0xc42003e768 pc=0x42512e\n"
+ "runtime.forcegchelper()\n"
+ " /usr/local/go/src/runtime/proc.go:238 +0xcc fp=0xc42003e7e0 sp=0xc42003e7a8 pc=0x424e5c\n"
+ "runtime.goexit()\n"
+ " /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003e7e8 sp=0xc42003e7e0 pc=0x44b4d1\n"
+ "created by runtime.init.4\n"
+ " /usr/local/go/src/runtime/proc.go:227 +0x35\n"
+ "\n"
+ "goroutine 3 [GC sweep wait]:\n"
+ "runtime.gopark(0x4739b8, 0x4ad7e0, 0x46fdd2, 0xd, 0x419914, 0x1)\n"
+ " /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc42003ef60 sp=0xc42003ef30 pc=0x42503c\n"
+ "runtime.goparkunlock(0x4ad7e0, 0x46fdd2, 0xd, 0x14, 0x1)\n"
+ " /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc42003efa0 sp=0xc42003ef60 pc=0x42512e\n"
+ "runtime.bgsweep(0xc42001e150)\n"
+ " /usr/local/go/src/runtime/mgcsweep.go:52 +0xa3 fp=0xc42003efd8 sp=0xc42003efa0 pc=0x419973\n"
+ "runtime.goexit()\n"
+ " /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003efe0 sp=0xc42003efd8 pc=0x44b4d1\n"
+ "created by runtime.gcenable\n"
+ " /usr/local/go/src/runtime/mgc.go:216 +0x58\n"
+ },
+ {"one more line, no multiline\n"}
+};
+
+/*
+ * Issue 3817 (case: 1)
+ * --------------------
+ * Source CRI messages (need first CRI multiline parsing) + a custom multiline
+ * parser.
+ *
+ * - https://github.com/fluent/fluent-bit/issues/3817
+ *
+ * The 'case 1' represents the problems of identifying two consecutive multiline
+ * messages within the same stream.
+ */
+struct record_check issue_3817_1_input[] = {
+ {"2021-05-17T17:35:01.184675702Z stdout F [DEBUG] 1 start multiline - "},
+ {"2021-05-17T17:35:01.184747208Z stdout F 1 cont A"},
+ {"2021-05-17T17:35:01.184675702Z stdout F [DEBUG] 2 start multiline - "},
+ {"2021-05-17T17:35:01.184747208Z stdout F 2 cont B"},
+ {"another isolated line"}
+};
+
+struct record_check issue_3817_1_output[] = {
+ {
+ "[DEBUG] 1 start multiline - \n"
+ "1 cont A"
+ },
+
+ {
+ "[DEBUG] 2 start multiline - \n"
+ "2 cont B"
+ },
+
+ {
+ "another isolated line"
+ }
+};
+
+/*
+ * Flush callback is invoked every time a multiline stream has completed a multiline
+ * message or a message is not multiline.
+ */
+static int flush_callback(struct flb_ml_parser *parser,
+ struct flb_ml_stream *mst,
+ void *data, char *buf_data, size_t buf_size)
+{
+ int i;
+ int ret;
+ int len;
+ int found = FLB_FALSE;
+ size_t off = 0;
+ msgpack_unpacked result;
+ msgpack_object *map;
+ msgpack_object key;
+ msgpack_object val;
+ struct flb_time tm;
+ struct expected_result *res = data;
+ struct record_check *exp;
+
+ fprintf(stdout, "\n%s----- MULTILINE FLUSH -----%s\n", ANSI_YELLOW, ANSI_RESET);
+
+ /* Print incoming flush buffer */
+ flb_pack_print(buf_data, buf_size);
+
+ fprintf(stdout, "%s----------- EOF -----------%s\n",
+ ANSI_YELLOW, ANSI_RESET);
+
+ /* Validate content */
+ msgpack_unpacked_init(&result);
+ off = 0;
+ ret = msgpack_unpack_next(&result, buf_data, buf_size, &off);
+ TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS);
+
+ flb_time_pop_from_msgpack(&tm, &result, &map);
+
+ TEST_CHECK(flb_time_to_nanosec(&tm) != 0L);
+
+ exp = &res->out_records[res->current_record];
+ len = strlen(res->key);
+ for (i = 0; i < map->via.map.size; i++) {
+ key = map->via.map.ptr[i].key;
+ val = map->via.map.ptr[i].val;
+
+ if (key.via.str.size != len) {
+ continue;
+ }
+
+ if (strncmp(key.via.str.ptr, res->key, len) == 0) {
+ found = FLB_TRUE;
+ break;
+ }
+ }
+ TEST_CHECK(found == FLB_TRUE);
+
+ len = strlen(exp->buf);
+ TEST_CHECK(val.via.str.size == len);
+ if (val.via.str.size != len) {
+ printf("expected length: %i, received: %i\n", len, val.via.str.size);
+ printf("== received ==\n");
+ msgpack_object_print(stdout, val);
+ printf("\n\n");
+ printf("== expected ==\n%s\n", exp->buf);
+ exit(1);
+ }
+ TEST_CHECK(memcmp(val.via.str.ptr, exp->buf, len) == 0);
+ res->current_record++;
+
+ msgpack_unpacked_destroy(&result);
+ return 0;
+}
+
+static void test_parser_docker()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ uint64_t stream_id;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = docker_output;
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create docker multiline mode */
+ ml = flb_ml_create(config, "test-docker");
+ TEST_CHECK(ml != NULL);
+
+ /* Load instances of the parsers for current 'ml' context */
+ mlp_i = flb_ml_parser_instance_create(ml, "cri");
+ TEST_CHECK(mlp_i != NULL);
+
+ /* Generate an instance of multiline docker parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "docker");
+ TEST_CHECK(mlp_i != NULL);
+
+ ret = flb_ml_stream_create(ml, "docker", -1, flush_callback, (void *) &res,
+ &stream_id);
+ TEST_CHECK(ret == 0);
+
+ entries = sizeof(docker_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &docker_input[i];
+ len = strlen(r->buf);
+
+ flb_time_get(&tm);
+
+ /* Package as msgpack */
+ flb_ml_append_text(ml, stream_id, &tm, r->buf, len);
+ }
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static void test_parser_cri()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ uint64_t stream_id;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = cri_output;
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create docker multiline mode */
+ ml = flb_ml_create(config, "cri-test");
+ TEST_CHECK(ml != NULL);
+
+ /* Generate an instance of multiline docker parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "docker");
+ TEST_CHECK(mlp_i != NULL);
+
+ /* Load instances of the parsers for current 'ml' context */
+ mlp_i = flb_ml_parser_instance_create(ml, "cri");
+ TEST_CHECK(mlp_i != NULL);
+
+ ret = flb_ml_stream_create(ml, "cri", -1, flush_callback, (void *) &res,
+ &stream_id);
+ TEST_CHECK(ret == 0);
+
+ entries = sizeof(cri_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &cri_input[i];
+ len = strlen(r->buf);
+ flb_time_get(&tm);
+
+ /* Package as msgpack */
+ flb_ml_append_text(ml, stream_id, &tm, r->buf, len);
+ }
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static void test_container_mix()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ uint64_t stream_id;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = container_mix_output;
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create docker multiline mode */
+ ml = flb_ml_create(config, "container-mix-test");
+ TEST_CHECK(ml != NULL);
+
+ /* Generate an instance of multiline docker parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "docker");
+ TEST_CHECK(mlp_i != NULL);
+
+ /* Load instances of the parsers for current 'ml' context */
+ mlp_i = flb_ml_parser_instance_create(ml, "cri");
+ TEST_CHECK(mlp_i != NULL);
+
+ ret = flb_ml_stream_create(ml, "container-mix", -1, flush_callback, (void *) &res,
+ &stream_id);
+ TEST_CHECK(ret == 0);
+
+ entries = sizeof(container_mix_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &container_mix_input[i];
+ len = strlen(r->buf);
+ flb_time_get(&tm);
+
+ /* Package as msgpack */
+ flb_ml_append_text(ml, stream_id, &tm, r->buf, len);
+ }
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static void test_parser_java()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ uint64_t stream_id;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = java_output;
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create docker multiline mode */
+ ml = flb_ml_create(config, "java-test");
+ TEST_CHECK(ml != NULL);
+
+ /* Generate an instance of multiline java parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "java");
+ TEST_CHECK(mlp_i != NULL);
+
+ flb_ml_parser_instance_set(mlp_i, "key_content", "log");
+
+ ret = flb_ml_stream_create(ml, "java", -1, flush_callback, (void *) &res,
+ &stream_id);
+ TEST_CHECK(ret == 0);
+
+ /* initialize buffers */
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ size_t off = 0;
+ msgpack_unpacked result;
+ msgpack_object root;
+ msgpack_object *map;
+
+ entries = sizeof(java_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &java_input[i];
+ len = strlen(r->buf);
+
+ /* Package as msgpack */
+ flb_time_get(&tm);
+
+ /* initialize buffers */
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_array(&mp_pck, 2);
+ flb_time_append_to_msgpack(&tm, &mp_pck, 0);
+
+ msgpack_pack_map(&mp_pck, 1);
+ msgpack_pack_str(&mp_pck, 3);
+ msgpack_pack_str_body(&mp_pck, "log", 3);
+ msgpack_pack_str(&mp_pck, len);
+ msgpack_pack_str_body(&mp_pck, r->buf, len);
+
+ /* Unpack and lookup the content map */
+ msgpack_unpacked_init(&result);
+ off = 0;
+ ret = msgpack_unpack_next(&result, mp_sbuf.data, mp_sbuf.size, &off);
+
+ flb_pack_print(mp_sbuf.data, mp_sbuf.size);
+
+ root = result.data;
+ map = &root.via.array.ptr[1];
+
+ /* Package as msgpack */
+ ret = flb_ml_append_object(ml, stream_id, &tm, NULL, map);
+
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&mp_sbuf);
+ }
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static void test_parser_python()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ uint64_t stream_id;
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = python_output;
+
+ /* initialize buffers */
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create docker multiline mode */
+ ml = flb_ml_create(config, "python-test");
+ TEST_CHECK(ml != NULL);
+
+ /* Generate an instance of multiline python parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "python");
+ TEST_CHECK(mlp_i != NULL);
+
+ ret = flb_ml_stream_create(ml, "python", -1, flush_callback, (void *) &res,
+ &stream_id);
+ TEST_CHECK(ret == 0);
+
+ flb_time_get(&tm);
+
+ printf("\n");
+ entries = sizeof(python_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &python_input[i];
+ len = strlen(r->buf);
+
+ /* Package as msgpack */
+ flb_time_get(&tm);
+ flb_ml_append_text(ml, stream_id, &tm, r->buf, len);
+ }
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static void test_parser_ruby()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ uint64_t stream_id;
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = ruby_output;
+
+ /* initialize buffers */
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create docker multiline mode */
+ ml = flb_ml_create(config, "ruby-test");
+ TEST_CHECK(ml != NULL);
+
+ /* Generate an instance of multiline ruby parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "ruby");
+ TEST_CHECK(mlp_i != NULL);
+
+ ret = flb_ml_stream_create(ml, "ruby", -1, flush_callback, (void *) &res,
+ &stream_id);
+ TEST_CHECK(ret == 0);
+
+ flb_time_get(&tm);
+
+ printf("\n");
+ entries = sizeof(ruby_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &ruby_input[i];
+ len = strlen(r->buf);
+
+ /* Package as msgpack */
+ flb_time_get(&tm);
+ flb_ml_append_text(ml, stream_id, &tm, r->buf, len);
+ }
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static void test_issue_4949()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ uint64_t stream_id;
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = python_output;
+
+ /* initialize buffers */
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create docker multiline mode */
+ ml = flb_ml_create(config, "python-test");
+ TEST_CHECK(ml != NULL);
+
+ /* Generate an instance of multiline python parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "python");
+ TEST_CHECK(mlp_i != NULL);
+
+ ret = flb_ml_stream_create(ml, "python", -1, flush_callback, (void *) &res,
+ &stream_id);
+ TEST_CHECK(ret == 0);
+
+ /* Generate an instance of multiline java parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "java");
+ TEST_CHECK(mlp_i != NULL);
+
+ ret = flb_ml_stream_create(ml, "java", -1, flush_callback, (void *) &res,
+ &stream_id);
+ TEST_CHECK(ret == 0);
+
+ flb_time_get(&tm);
+
+ printf("\n");
+ entries = sizeof(python_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &python_input[i];
+ len = strlen(r->buf);
+
+ /* Package as msgpack */
+ flb_time_get(&tm);
+ flb_ml_append_text(ml, stream_id, &tm, r->buf, len);
+ }
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static void test_parser_elastic()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ size_t off = 0;
+ uint64_t stream_id;
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+ msgpack_unpacked result;
+ msgpack_object root;
+ msgpack_object *map;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser *mlp;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = elastic_output;
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ ml = flb_ml_create(config, "test-elastic");
+ TEST_CHECK(ml != NULL);
+
+ mlp = flb_ml_parser_create(config,
+ "elastic", /* name */
+ FLB_ML_REGEX, /* type */
+ NULL, /* match_str */
+ FLB_FALSE, /* negate */
+ 1000, /* flush_ms */
+ "log", /* key_content */
+ NULL, /* key_pattern */
+ NULL, /* key_group */
+ NULL, /* parser ctx */
+ NULL); /* parser name */
+ TEST_CHECK(mlp != NULL);
+
+ mlp_i = flb_ml_parser_instance_create(ml, "elastic");
+ TEST_CHECK(mlp_i != NULL);
+
+ ret = flb_ml_rule_create(mlp, "start_state", "/^\\[/", "elastic_cont", NULL);
+ if (ret != 0) {
+ fprintf(stderr, "error creating rule 1");
+ }
+
+ ret = flb_ml_rule_create(mlp, "elastic_cont", "/^\\s+/", "elastic_cont", NULL);
+ if (ret != 0) {
+ fprintf(stderr, "error creating rule 2");
+ }
+
+ ret = flb_ml_stream_create(ml, "elastic", -1, flush_callback, (void *) &res,
+ &stream_id);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_ml_parser_init(mlp);
+ if (ret != 0) {
+ fprintf(stderr, "error initializing multiline\n");
+ flb_ml_destroy(ml);
+ return;
+ }
+
+ printf("\n");
+ entries = sizeof(elastic_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &elastic_input[i];
+ len = strlen(r->buf);
+
+ /* initialize buffers */
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ /* Package raw text as a msgpack record */
+ msgpack_pack_array(&mp_pck, 2);
+
+ flb_time_get(&tm);
+ flb_time_append_to_msgpack(&tm, &mp_pck, 0);
+
+ msgpack_pack_map(&mp_pck, 1);
+ msgpack_pack_str(&mp_pck, 3);
+ msgpack_pack_str_body(&mp_pck, "log", 3);
+ msgpack_pack_str(&mp_pck, len);
+ msgpack_pack_str_body(&mp_pck, r->buf, len);
+
+ /* Unpack and lookup the content map */
+ msgpack_unpacked_init(&result);
+ off = 0;
+ ret = msgpack_unpack_next(&result, mp_sbuf.data, mp_sbuf.size, &off);
+ TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS);
+
+ root = result.data;
+ map = &root.via.array.ptr[1];
+
+ /* Package as msgpack */
+ ret = flb_ml_append_object(ml, stream_id, &tm, NULL, map);
+
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&mp_sbuf);
+ }
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static void test_endswith()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ uint64_t stream_id = 0;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser *mlp;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = endswith_output;
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create docker multiline mode */
+ ml = flb_ml_create(config, "raw-endswith");
+ TEST_CHECK(ml != NULL);
+
+ mlp = flb_ml_parser_create(config,
+ "endswith", /* name */
+ FLB_ML_ENDSWITH, /* type */
+ "\\", /* match_str */
+ FLB_TRUE, /* negate */
+ 1000, /* flush_ms */
+ NULL, /* key_content */
+ NULL, /* key_pattern */
+ NULL, /* key_group */
+ NULL, /* parser ctx */
+ NULL); /* parser name */
+ TEST_CHECK(mlp != NULL);
+
+ /* Generate an instance of 'endswith' custom parser parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "endswith");
+ TEST_CHECK(mlp_i != NULL);
+
+ ret = flb_ml_stream_create(ml, "test", -1, flush_callback, (void *) &res, &stream_id);
+ TEST_CHECK(ret == 0);
+
+ entries = sizeof(endswith_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &endswith_input[i];
+ len = strlen(r->buf);
+
+ /* Package as msgpack */
+ flb_time_get(&tm);
+ flb_ml_append_text(ml, stream_id, &tm, r->buf, len);
+ }
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static void test_parser_go()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ uint64_t stream_id = 0;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = go_output;
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create docker multiline mode */
+ ml = flb_ml_create(config, "go-test");
+ TEST_CHECK(ml != NULL);
+
+ /* Generate an instance of multiline java parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "go");
+ TEST_CHECK(mlp_i != NULL);
+
+ ret = flb_ml_stream_create(ml, "go", -1, flush_callback, (void *) &res, &stream_id);
+ TEST_CHECK(ret == 0);
+
+ entries = sizeof(go_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &go_input[i];
+ len = strlen(r->buf);
+
+ /* Package as msgpack */
+ flb_time_get(&tm);
+ flb_ml_append_text(ml, stream_id, &tm, r->buf, len);
+ }
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static int flush_callback_to_buf(struct flb_ml_parser *parser,
+ struct flb_ml_stream *mst,
+ void *data, char *buf_data, size_t buf_size)
+{
+ msgpack_sbuffer *mp_sbuf = data;
+ msgpack_sbuffer_write(mp_sbuf, buf_data, buf_size);
+
+ return 0;
+}
+
+static void run_test(struct flb_config *config, char *test_name,
+ struct record_check *in, int in_len,
+ struct record_check *out, int out_len,
+ char *parser1, char *parser2)
+
+{
+ int i;
+ int ret;
+ int len;
+ size_t off = 0;
+ uint64_t stream1 = 0;
+ uint64_t stream2 = 0;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *p1 = NULL;
+ struct record_check *r;
+ msgpack_sbuffer mp_sbuf1;
+ msgpack_packer mp_pck1;
+ msgpack_sbuffer mp_sbuf2;
+ msgpack_packer mp_pck2;
+ msgpack_object *map;
+ struct flb_time tm;
+ struct expected_result res = {0};
+ msgpack_unpacked result;
+
+ /* init buffers */
+ msgpack_sbuffer_init(&mp_sbuf1);
+ msgpack_packer_init(&mp_pck1, &mp_sbuf1, msgpack_sbuffer_write);
+ msgpack_sbuffer_init(&mp_sbuf2);
+ msgpack_packer_init(&mp_pck2, &mp_sbuf2, msgpack_sbuffer_write);
+
+ /* Create docker multiline mode */
+ ml = flb_ml_create(config, test_name);
+ TEST_CHECK(ml != NULL);
+
+ if (!parser1) {
+ fprintf(stderr, "run_test(): parser1 is NULL\n");
+ exit(1);
+ }
+
+ /* Parser 1 */
+ p1 = flb_ml_parser_instance_create(ml, parser1);
+ TEST_CHECK(p1 != NULL);
+
+
+ /* Stream 1: use parser name (test_name) to generate the stream id */
+ ret = flb_ml_stream_create(ml, test_name, -1,
+ flush_callback_to_buf,
+ (void *) &mp_sbuf1, &stream1);
+ TEST_CHECK(ret == 0);
+
+ /* Ingest input records into parser 1 */
+ for (i = 0; i < in_len; i++) {
+ r = &in[i];
+ len = strlen(r->buf);
+
+ flb_time_get(&tm);
+
+ /* Package as msgpack */
+ flb_ml_append_text(ml, stream1, &tm, r->buf, len);
+ }
+
+ flb_ml_destroy(ml);
+ ml = flb_ml_create(config, test_name);
+
+ flb_ml_parser_instance_create(ml, parser2);
+
+ /*
+ * After flb_ml_append above(), mp_sbuf1 has been populated with the
+ * output results as structured messages. Now this data needs to be
+ * passed to the next parser.
+ */
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = out;
+
+ /* Stream 2 */
+ ret = flb_ml_stream_create(ml, "filter_multiline", -1,
+ flush_callback,
+ (void *) &res, &stream2);
+
+ /* Ingest input records into parser 2 */
+ off = 0;
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, mp_sbuf1.data, mp_sbuf1.size, &off)) {
+ flb_time_pop_from_msgpack(&tm, &result, &map);
+
+ /* Package as msgpack */
+ ret = flb_ml_append_object(ml, stream2, &tm, NULL, map);
+ }
+ flb_ml_flush_pending_now(ml);
+
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&mp_sbuf1);
+ flb_ml_destroy(ml);
+}
+
+void test_issue_3817_1()
+{
+ int ret;
+ int in_len = sizeof(issue_3817_1_input) / sizeof(struct record_check);
+ int out_len = sizeof(issue_3817_1_output) / sizeof(struct record_check);
+ struct flb_config *config;
+ struct flb_ml_parser *mlp;
+
+ /*
+ * Parser definition for a file:
+ *
+ * [MULTILINE_PARSER]
+ * name parser_3817
+ * type regex
+ * key_content log
+ * #
+ * # Regex rules for multiline parsing
+ * # ---------------------------------
+ * #
+ * # rules | state name | regex pattern | next state
+ * # ------|---------------|------------------------------------
+ * rule "start_state" "/- $/" "cont"
+ * rule "cont" "/^([1-9].*$/" "cont"
+ *
+ */
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Register custom parser */
+ mlp = flb_ml_parser_create(config,
+ "parser_3817", /* name */
+ FLB_ML_REGEX, /* type */
+ NULL, /* match_str */
+ FLB_FALSE, /* negate */
+ 1000, /* flush_ms */
+ "log", /* key_content */
+ NULL, /* key_pattern */
+ NULL, /* key_group */
+ NULL, /* parser ctx */
+ NULL); /* parser name */
+ TEST_CHECK(mlp != NULL);
+
+ /* rule: start_state */
+ ret = flb_ml_rule_create(mlp, "start_state", "/- $/", "cont", NULL);
+ if (ret != 0) {
+ fprintf(stderr, "error creating rule 1");
+ }
+
+ /* rule: cont */
+ ret = flb_ml_rule_create(mlp, "cont", "/^([1-9]).*$/", "cont", NULL);
+ if (ret != 0) {
+ fprintf(stderr, "error creating rule 2");
+ }
+
+ /* initiaze the parser configuration */
+ ret = flb_ml_parser_init(mlp);
+ TEST_CHECK(ret == 0);
+
+ /* Run the test */
+ run_test(config, "issue_3817_1",
+ issue_3817_1_input, in_len,
+ issue_3817_1_output, out_len,
+ "cri", "parser_3817");
+
+ flb_config_exit(config);
+}
+
+static void test_issue_4034()
+{
+ int i;
+ int len;
+ int ret;
+ int entries;
+ uint64_t stream_id;
+ struct record_check *r;
+ struct flb_config *config;
+ struct flb_time tm;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *mlp_i;
+ struct expected_result res = {0};
+ msgpack_packer mp_pck;
+ msgpack_sbuffer mp_sbuf;
+
+ /* Expected results context */
+ res.key = "log";
+ res.out_records = cri_output;
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create cri multiline mode */
+ ml = flb_ml_create(config, "cri-test");
+ TEST_CHECK(ml != NULL);
+
+ /* Generate an instance of multiline cri parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "cri");
+ TEST_CHECK(mlp_i != NULL);
+
+ flb_ml_parser_instance_set(mlp_i, "key_content", "log");
+
+ ret = flb_ml_stream_create(ml, "cri", -1, flush_callback, (void *) &res,
+ &stream_id);
+ TEST_CHECK(ret == 0);
+
+ /* initialize buffers */
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ size_t off = 0;
+ msgpack_unpacked result;
+ msgpack_object root;
+ msgpack_object *map;
+
+ entries = sizeof(cri_input) / sizeof(struct record_check);
+ for (i = 0; i < entries; i++) {
+ r = &cri_input[i];
+ len = strlen(r->buf);
+
+ /* Package as msgpack */
+ flb_time_get(&tm);
+
+ /* initialize buffers */
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_array(&mp_pck, 2);
+ flb_time_append_to_msgpack(&tm, &mp_pck, 0);
+
+ msgpack_pack_map(&mp_pck, 1);
+ msgpack_pack_str(&mp_pck, 3);
+ msgpack_pack_str_body(&mp_pck, "log", 3);
+ msgpack_pack_str(&mp_pck, len);
+ msgpack_pack_str_body(&mp_pck, r->buf, len);
+
+ /* Unpack and lookup the content map */
+ msgpack_unpacked_init(&result);
+ off = 0;
+ ret = msgpack_unpack_next(&result, mp_sbuf.data, mp_sbuf.size, &off);
+
+ flb_pack_print(mp_sbuf.data, mp_sbuf.size);
+
+ root = result.data;
+ map = &root.via.array.ptr[1];
+
+ /* Package as msgpack */
+ ret = flb_ml_append_object(ml, stream_id, &tm, NULL, map);
+
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&mp_sbuf);
+ }
+ flb_ml_flush_pending_now(ml);
+
+ if (ml) {
+ flb_ml_destroy(ml);
+ }
+
+ flb_config_exit(config);
+}
+
+static void test_issue_5504()
+{
+ uint64_t last_flush;
+ struct flb_config *config;
+ struct flb_ml *ml;
+ struct flb_ml_parser_ins *mlp_i;
+ struct mk_event_loop *evl;
+ struct flb_sched *sched;
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct flb_sched_timer *timer;
+ void (*cb)(struct flb_config *, void *);
+ int timeout = 500;
+
+#ifdef _WIN32
+ WSADATA wsa_data;
+ WSAStartup(0x0201, &wsa_data);
+#endif
+
+ /* Initialize environment */
+ config = flb_config_init();
+
+ /* Create the event loop */
+ evl = config->evl;
+ config->evl = mk_event_loop_create(32);
+ TEST_CHECK(config->evl != NULL);
+
+ /* Initialize the scheduler */
+ sched = config->sched;
+ config->sched = flb_sched_create(config, config->evl);
+ TEST_CHECK(config->sched != NULL);
+
+ /* Set the thread local scheduler */
+ flb_sched_ctx_init();
+ flb_sched_ctx_set(config->sched);
+
+ ml = flb_ml_create(config, "5504-test");
+ TEST_CHECK(ml != NULL);
+
+ /* Generate an instance of any multiline parser */
+ mlp_i = flb_ml_parser_instance_create(ml, "cri");
+ TEST_CHECK(mlp_i != NULL);
+
+ flb_ml_parser_instance_set(mlp_i, "key_content", "log");
+
+ /* Set the flush timeout */
+ ml->flush_ms = timeout;
+
+ /* Initialize the auto flush */
+ flb_ml_auto_flush_init(ml);
+
+ /* Store the initial last_flush time */
+ last_flush = ml->last_flush;
+
+ /* Find the cb_ml_flush_timer callback from the timers */
+ mk_list_foreach_safe(head, tmp, &((struct flb_sched *)config->sched)->timers) {
+ timer = mk_list_entry(head, struct flb_sched_timer, _head);
+ if (timer->type == FLB_SCHED_TIMER_CB_PERM) {
+ cb = timer->cb;
+ }
+ }
+ TEST_CHECK(cb != NULL);
+
+ /* Trigger the callback without delay */
+ cb(config, ml);
+ /* This should not update the last_flush since it is before the timeout */
+ TEST_CHECK(ml->last_flush == last_flush);
+
+ /* Sleep just enough time to pass the timeout */
+ flb_time_msleep(timeout + 1);
+
+ /* Retrigger the callback */
+ cb(config, ml);
+ /* Ensure this time the last_flush has been updated */
+ TEST_CHECK(ml->last_flush > last_flush);
+
+ /* Cleanup */
+ flb_sched_destroy(config->sched);
+ config->sched = sched;
+ mk_event_loop_destroy(config->evl);
+ config->evl = evl;
+ flb_ml_destroy(ml);
+ flb_config_exit(config);
+
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+TEST_LIST = {
+ /* Normal features tests */
+ { "parser_docker", test_parser_docker},
+ { "parser_cri", test_parser_cri},
+ { "parser_java", test_parser_java},
+ { "parser_python", test_parser_python},
+ { "parser_ruby", test_parser_ruby},
+ { "parser_elastic", test_parser_elastic},
+ { "parser_go", test_parser_go},
+ { "container_mix", test_container_mix},
+ { "endswith", test_endswith},
+
+ /* Issues reported on Github */
+ { "issue_3817_1" , test_issue_3817_1},
+ { "issue_4034" , test_issue_4034},
+ { "issue_4949" , test_issue_4949},
+ { "issue_5504" , test_issue_5504},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/network.c b/fluent-bit/tests/internal/network.c
new file mode 100644
index 00000000..50683f18
--- /dev/null
+++ b/fluent-bit/tests/internal/network.c
@@ -0,0 +1,164 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_error.h>
+#include <fluent-bit/flb_network.h>
+#include <fluent-bit/flb_socket.h>
+#include <fluent-bit/flb_time.h>
+
+#include <time.h>
+#include "flb_tests_internal.h"
+
+#define TEST_HOSTv4 "127.0.0.1"
+#define TEST_HOSTv6 "::1"
+#define TEST_PORT "41322"
+
+#define TEST_EV_CLIENT MK_EVENT_NOTIFICATION
+#define TEST_EV_SERVER MK_EVENT_CUSTOM
+
+static int socket_check_ok(flb_sockfd_t fd)
+{
+ int ret;
+ int error = 0;
+ socklen_t len = sizeof(error);
+
+ ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len);
+ if (ret == -1) {
+ return -1;
+ }
+
+ if (error != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static void test_client_server(int is_ipv6)
+{
+ int ret;
+ int loops = 0;
+ int client_OK = FLB_FALSE;
+ int server_OK = FLB_FALSE;
+ int family;
+ char *host;
+ flb_sockfd_t fd_client;
+ flb_sockfd_t fd_server;
+ flb_sockfd_t fd_remote = -1;
+ struct mk_event e_client = {0};
+ struct mk_event e_server = {0};
+ struct mk_event *e_item;
+ struct mk_event_loop *evl;
+
+ if (is_ipv6 == FLB_TRUE) {
+ family = AF_INET6;
+ host = TEST_HOSTv6;
+ }
+ else {
+ family = AF_INET;
+ host = TEST_HOSTv4;
+ }
+
+ /* Create client and server sockets */
+ fd_client = flb_net_socket_create(family, FLB_TRUE);
+ if (errno == EAFNOSUPPORT) {
+ TEST_MSG("This protocol is not supported in this platform");
+ return;
+ }
+ TEST_CHECK(fd_client != -1);
+
+ fd_server = flb_net_server(TEST_PORT, host);
+ TEST_CHECK(fd_server != -1);
+
+ /* Create Event loop */
+ evl = mk_event_loop_create(8);
+ TEST_CHECK(evl != NULL);
+
+ /* Register client/server sockets into the event loop */
+ MK_EVENT_NEW(&e_client);
+ ret = mk_event_add(evl, fd_client, TEST_EV_CLIENT, MK_EVENT_WRITE, &e_client);
+ TEST_CHECK(ret != -1);
+
+ MK_EVENT_NEW(&e_server);
+ ret = mk_event_add(evl, fd_server, TEST_EV_SERVER, MK_EVENT_READ, &e_server);
+ TEST_CHECK(ret != -1);
+
+ /* Test an async connection, we expect -1 */
+ ret = flb_net_tcp_fd_connect(fd_client, host, atol(TEST_PORT));
+ TEST_CHECK(ret == -1);
+ TEST_CHECK(errno == EINPROGRESS);
+
+#ifdef FLB_SYSTEM_MACOS
+ /* On macOS, its internal timer's is inacccurate without waiting code.
+ * We need to proceed its timer tick for processing events. */
+ flb_time_msleep(50);
+#endif
+
+ /* Event loop */
+ while (1) {
+ /* Break immediately for invalid status */
+ if (fd_client == -1 || fd_server == -1) {
+ break;
+ }
+ mk_event_wait(evl);
+ mk_event_foreach(e_item, evl) {
+ if (e_item->type == TEST_EV_CLIENT) {
+ /* Validate event mask */
+ TEST_CHECK(e_item->mask & MK_EVENT_WRITE);
+
+ /*
+ * Client socket get a notification, we expect to get a
+ * successful connection.
+ */
+ ret = socket_check_ok(fd_client);
+ TEST_CHECK(ret == 0);
+ client_OK = FLB_TRUE;
+ }
+ else if (e_item->type == TEST_EV_SERVER) {
+ /* Validate event mask */
+ TEST_CHECK(e_item->mask & MK_EVENT_READ);
+
+ /* Accept the incoming connection */
+ fd_remote = flb_net_accept(fd_server);
+ TEST_CHECK(fd_remote > 0);
+
+ server_OK = FLB_TRUE;
+ }
+ loops++;
+ }
+
+ if (client_OK == FLB_TRUE && server_OK == FLB_TRUE) {
+ break;
+ }
+
+ TEST_CHECK(loops < 2);
+ if (loops >= 2) {
+ break;
+ }
+ }
+
+ mk_event_loop_destroy(evl);
+ flb_socket_close(fd_client);
+ flb_socket_close(fd_server);
+ if (fd_remote > 0) {
+ flb_socket_close(fd_remote);
+ }
+}
+
+void test_ipv4_client_server()
+{
+ test_client_server(FLB_FALSE);
+}
+
+void test_ipv6_client_server()
+{
+ test_client_server(FLB_TRUE);
+}
+
+TEST_LIST = {
+ { "ipv4_client_server", test_ipv4_client_server},
+ { "ipv6_client_server", test_ipv6_client_server},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/pack.c b/fluent-bit/tests/internal/pack.c
new file mode 100644
index 00000000..1bfabfeb
--- /dev/null
+++ b/fluent-bit/tests/internal/pack.c
@@ -0,0 +1,914 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_error.h>
+#include <fluent-bit/flb_str.h>
+#include <monkey/mk_core.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <math.h> /* for NAN */
+
+
+#include "flb_tests_internal.h"
+
+/* JSON iteration tests */
+#define JSON_SINGLE_MAP1 FLB_TESTS_DATA_PATH "/data/pack/json_single_map_001.json"
+#define JSON_SINGLE_MAP2 FLB_TESTS_DATA_PATH "/data/pack/json_single_map_002.json"
+#define JSON_DUP_KEYS_I FLB_TESTS_DATA_PATH "/data/pack/dup_keys_in.json"
+#define JSON_DUP_KEYS_O FLB_TESTS_DATA_PATH "/data/pack/dup_keys_out.json"
+
+#define JSON_BUG342 FLB_TESTS_DATA_PATH "/data/pack/bug342.json"
+
+/* Pack Samples path */
+#define PACK_SAMPLES FLB_TESTS_DATA_PATH "/data/pack/"
+
+struct pack_test {
+ char *msgpack;
+ char *json;
+};
+
+/* If we get more than 256 tests, just update the size */
+struct pack_test pt[256];
+
+static inline void consume_bytes(char *buf, int bytes, int length)
+{
+ memmove(buf, buf + bytes, length - bytes);
+}
+
+/* Pack a simple JSON map */
+void test_json_pack()
+{
+ int ret;
+ int root_type;
+ size_t len;
+ char *data;
+ char *out_buf;
+ size_t out_size;
+
+ data = mk_file_to_buffer(JSON_SINGLE_MAP1);
+ TEST_CHECK(data != NULL);
+
+ len = strlen(data);
+
+ ret = flb_pack_json(data, len, &out_buf, &out_size, &root_type, NULL);
+ TEST_CHECK(ret == 0);
+
+ flb_free(data);
+ flb_free(out_buf);
+}
+
+/* Pack a simple JSON map using a state */
+void test_json_pack_iter()
+{
+ int i;
+ int ret;
+ size_t len;
+ char *data;
+ char *out_buf = NULL;
+ int out_size;
+ struct flb_pack_state state;
+
+ data = mk_file_to_buffer(JSON_SINGLE_MAP1);
+ TEST_CHECK(data != NULL);
+
+ len = strlen(data);
+
+ ret = flb_pack_state_init(&state);
+ TEST_CHECK(ret == 0);
+
+ /* Pass byte by byte */
+ for (i = 1; i < len; i++) {
+ ret = flb_pack_json_state(data, i, &out_buf, &out_size, &state);
+ if (i + 1 != len) {
+ TEST_CHECK(ret == FLB_ERR_JSON_PART);
+ }
+ }
+ TEST_CHECK(ret != FLB_ERR_JSON_INVAL && ret != FLB_ERR_JSON_PART);
+
+ flb_pack_state_reset(&state);
+ flb_free(data);
+ flb_free(out_buf);
+}
+
+/* Pack two concatenated JSON maps using a state */
+void test_json_pack_mult()
+
+{
+ int ret;
+ int maps = 0;
+ size_t off = 0;
+ size_t len1;
+ size_t len2;
+ size_t total;
+ char *buf;
+ char *data1;
+ char *data2;
+ char *out_buf;
+ int out_size;
+ msgpack_unpacked result;
+ msgpack_object root;
+ struct flb_pack_state state;
+
+ data1 = mk_file_to_buffer(JSON_SINGLE_MAP1);
+ TEST_CHECK(data1 != NULL);
+ len1 = strlen(data1);
+
+ data2 = mk_file_to_buffer(JSON_SINGLE_MAP2);
+ TEST_CHECK(data2 != NULL);
+ len2 = strlen(data2);
+
+ buf = flb_malloc(len1 + len2 + 1);
+ TEST_CHECK(buf != NULL);
+
+ /* Merge buffers */
+ memcpy(buf, data1, len1);
+ memcpy(buf + len1, data2, len2);
+ total = len1 + len2;
+ buf[total] = '\0';
+
+ /* Release buffers */
+ flb_free(data1);
+ flb_free(data2);
+
+ ret = flb_pack_state_init(&state);
+ TEST_CHECK(ret == 0);
+
+ /* Enable the 'multiple' flag so the parser will accept concatenated msgs */
+ state.multiple = FLB_TRUE;
+
+ /* It should pack two msgpack-maps in out_buf */
+ ret = flb_pack_json_state(buf, total, &out_buf, &out_size, &state);
+ TEST_CHECK(ret == 0);
+
+ /* Validate output buffer */
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, out_buf, out_size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ maps++;
+ root = result.data;
+ TEST_CHECK(root.type == MSGPACK_OBJECT_MAP);
+ }
+ msgpack_unpacked_destroy(&result);
+
+ TEST_CHECK(maps == 2);
+
+ flb_pack_state_reset(&state);
+ flb_free(out_buf);
+ flb_free(buf);
+}
+
+/* Pack two concatenated JSON maps byte by byte using a state */
+void test_json_pack_mult_iter()
+
+{
+ int i;
+ int ret;
+ int maps = 0;
+ int total_maps = 0;
+ size_t off = 0;
+ size_t len1;
+ size_t len2;
+ size_t total;
+ char *buf;
+ char *data1;
+ char *data2;
+ char *out_buf;
+ int out_size;
+ msgpack_unpacked result;
+ msgpack_object root;
+ struct flb_pack_state state;
+
+ data1 = mk_file_to_buffer(JSON_SINGLE_MAP1);
+ TEST_CHECK(data1 != NULL);
+ len1 = strlen(data1);
+
+ data2 = mk_file_to_buffer(JSON_SINGLE_MAP2);
+ TEST_CHECK(data2 != NULL);
+ len2 = strlen(data2);
+
+ buf = flb_malloc(len1 + len2 + 1);
+ TEST_CHECK(buf != NULL);
+
+ /* Merge buffers */
+ memcpy(buf, data1, len1);
+ memcpy(buf + len1, data2, len2);
+ total = len1 + len2;
+ buf[total] = '\0';
+
+ /* Release buffers */
+ flb_free(data1);
+ flb_free(data2);
+
+ ret = flb_pack_state_init(&state);
+ TEST_CHECK(ret == 0);
+
+ /* Enable the 'multiple' flag so the parser will accept concatenated msgs */
+ state.multiple = FLB_TRUE;
+
+ /* Pass byte by byte */
+ for (i = 1; i < total; i++) {
+ ret = flb_pack_json_state(buf, i, &out_buf, &out_size, &state);
+ if (ret == 0) {
+ /* Consume processed bytes */
+ consume_bytes(buf, state.last_byte, total);
+ i = 1;
+ total -= state.last_byte;
+ flb_pack_state_reset(&state);
+ flb_pack_state_init(&state);
+
+ /* Validate output buffer */
+ off = 0;
+ maps = 0;
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, out_buf, out_size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ root = result.data;
+ TEST_CHECK(root.type == MSGPACK_OBJECT_MAP);
+ maps++;
+ total_maps++;
+ }
+ TEST_CHECK(maps == 1);
+ msgpack_unpacked_destroy(&result);
+ flb_free(out_buf);
+ }
+ }
+
+ TEST_CHECK(total_maps == 2);
+ flb_pack_state_reset(&state);
+ flb_free(buf);
+}
+
+/* Validate default values of macros used in flb_msgpack_raw_to_json_sds */
+void test_msgpack_to_json_macros()
+{
+ /* Verify default values */
+ TEST_CHECK(FLB_MSGPACK_TO_JSON_INIT_BUFFER_SIZE == 2.0);
+ TEST_CHECK(FLB_MSGPACK_TO_JSON_REALLOC_BUFFER_SIZE == 0.10);
+}
+
+/* Validate that duplicated keys are removed */
+void test_json_dup_keys()
+{
+ int ret;
+ int type;
+ size_t len_in;
+ char *out_buf;
+ size_t out_size;
+ char *data_in;
+ char *data_out;
+ flb_sds_t out_json;
+ flb_sds_t d;
+
+ /* Read JSON input file */
+ data_in = mk_file_to_buffer(JSON_DUP_KEYS_I);
+ TEST_CHECK(data_in != NULL);
+ len_in = strlen(data_in);
+
+ /* Read JSON output file */
+ data_out = mk_file_to_buffer(JSON_DUP_KEYS_O);
+ TEST_CHECK(data_out != NULL);
+
+ /* Pack raw JSON as msgpack */
+ ret = flb_pack_json(data_in, len_in, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+
+ d = flb_sds_create("date");
+ TEST_CHECK(d != NULL);
+
+ /* Convert back to JSON */
+ out_json = flb_pack_msgpack_to_json_format(out_buf, out_size,
+ FLB_PACK_JSON_FORMAT_LINES,
+ FLB_PACK_JSON_DATE_EPOCH,
+ d);
+ TEST_CHECK(out_json != NULL);
+
+ TEST_CHECK(strncmp(out_json, data_out, flb_sds_len(out_json)) == 0);
+ flb_sds_destroy(d);
+ flb_sds_destroy(out_json);
+ flb_free(out_buf);
+ flb_free(data_in);
+ flb_free(data_out);
+}
+
+void test_json_pack_bug342()
+{
+ int i = 0;
+ int records = 0;
+ int fd;
+ int ret;
+ size_t off = 0;
+ ssize_t r = 0;
+ char *out;
+ char buf[1024*4];
+ int out_size;
+ size_t total = 0;
+ int bytes[] = {1, 3, 3, 5, 5, 35, 17, 23,
+ 46, 37, 49, 51, 68, 70, 86, 268,
+ 120, 178, 315, 754, 753, 125};
+ struct stat st;
+ struct flb_pack_state state;
+ msgpack_unpacked result;
+ ret = stat(JSON_BUG342, &st);
+ if (ret == -1) {
+ perror("stat");
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < (sizeof(bytes)/sizeof(int)); i++) {
+ total += bytes[i];
+ }
+
+ TEST_CHECK(total == st.st_size);
+ if (total != st.st_size) {
+ exit(EXIT_FAILURE);
+ }
+
+ fd = open(JSON_BUG342, O_RDONLY);
+ if (fd == -1) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ }
+
+ flb_pack_state_init(&state);
+ state.multiple = FLB_TRUE;
+
+ for (i = 0; i < (sizeof(bytes)/sizeof(int)); i++) {
+ r = read(fd, buf + off, bytes[i]);
+ TEST_CHECK(r == bytes[i]);
+ if (r <= 0) {
+ perror("read");
+ exit(EXIT_FAILURE);
+ }
+ off += r;
+
+ ret = flb_pack_json_state(buf, off, &out, &out_size, &state);
+ TEST_CHECK(ret != FLB_ERR_JSON_INVAL);
+ if (ret == FLB_ERR_JSON_INVAL) {
+ exit(EXIT_FAILURE);
+ }
+ else if (ret == FLB_ERR_JSON_PART) {
+ continue;
+ }
+ else if (ret == 0) {
+ /* remove used bytes */
+ consume_bytes(buf, state.last_byte, off);
+ off -= state.last_byte;
+
+ /* reset the packer state */
+ flb_pack_state_reset(&state);
+ flb_pack_state_init(&state);
+ state.multiple = FLB_TRUE;
+ }
+
+ size_t coff = 0;
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, out, out_size, &coff) == MSGPACK_UNPACK_SUCCESS) {
+ records++;
+ }
+ msgpack_unpacked_destroy(&result);
+
+ TEST_CHECK(off >= state.last_byte);
+ if (off < state.last_byte) {
+ exit(1);
+ }
+ flb_free(out);
+ }
+ flb_pack_state_reset(&state);
+ close(fd);
+ TEST_CHECK(records == 240);
+}
+
+
+/* Iterate data/pack/ directory and compose an array with files to test */
+static int utf8_tests_create()
+{
+ int i = 0;
+ int len;
+ int ret;
+ char ext_mp[PATH_MAX];
+ char ext_json[PATH_MAX];
+ DIR *dir;
+ struct pack_test *test;
+ struct dirent *entry;
+ struct stat st;
+
+ memset(pt, '\0', sizeof(pt));
+
+ dir = opendir(PACK_SAMPLES);
+ TEST_CHECK(dir != NULL);
+ if (dir == NULL) {
+ exit(EXIT_FAILURE);
+ }
+
+ while ((entry = readdir(dir)) != NULL) {
+ if (entry->d_type != DT_REG) {
+ continue;
+ }
+
+ len = strlen(entry->d_name);
+ if (strcmp(entry->d_name + (len - 3), ".mp") != 0) {
+ continue;
+ }
+
+ snprintf(ext_mp, sizeof(ext_mp) - 1,
+ "%s%s", PACK_SAMPLES, entry->d_name);
+ len = snprintf(ext_json, sizeof(ext_json) - 1, "%s%s",
+ PACK_SAMPLES, entry->d_name);
+ snprintf(ext_json + (len - 3), sizeof(ext_json) - len - 3,
+ "%s", ".json");
+
+
+ /* Validate new paths */
+ ret = stat(ext_mp, &st);
+ if (ret == -1) {
+ printf("Unit test msgpack not found: %s\n", ext_mp);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = stat(ext_json, &st);
+ if (ret == -1) {
+ printf("Unit test result JSON not found: %s\n", ext_json);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Insert into table */
+ test = &pt[i];
+ test->msgpack = flb_strdup(ext_mp);
+ test->json = flb_strdup(ext_json);
+ i++;
+ }
+
+ closedir(dir);
+ return i;
+}
+
+static void utf8_tests_destroy(int s)
+{
+ int i;
+ struct pack_test *test;
+
+ for (i = 0; i < s; i++) {
+ test = &pt[i];
+ flb_free(test->msgpack);
+ flb_free(test->json);
+ }
+}
+
+void test_utf8_to_json()
+{
+ int i;
+ int ret;
+ int n_tests;
+ char *file_msgp;
+ char *file_json;
+ flb_sds_t out_buf;
+ size_t out_size;
+ size_t msgp_size;
+ size_t json_size;
+ struct stat st;
+ struct pack_test *test;
+
+ n_tests = utf8_tests_create();
+
+ /* Iterate unit tests table */
+ for (i = 0; i < n_tests; i++) {
+ test = &pt[i];
+ if (!test->msgpack) {
+ break;
+ }
+
+ file_msgp = mk_file_to_buffer(test->msgpack);
+ TEST_CHECK(file_msgp != NULL);
+ stat(test->msgpack, &st);
+ msgp_size = st.st_size;
+
+ file_json = mk_file_to_buffer(test->json);
+ TEST_CHECK(file_json != NULL);
+ if (!file_json) {
+ printf("Missing JSON file: %s\n", test->json);
+ flb_free(file_msgp);
+ continue;
+ }
+
+ json_size = strlen(file_json);
+
+ out_buf = flb_msgpack_raw_to_json_sds(file_msgp, msgp_size);
+ TEST_CHECK(out_buf != NULL);
+ out_size = flb_sds_len(out_buf);
+
+ ret = strcmp(file_json, out_buf);
+ if (ret != 0) {
+ TEST_CHECK(ret == 0);
+ printf("[test] %s\n", test->json);
+ printf(" EXPECTED => '%s'\n", file_json);
+ printf(" ENCODED => '%s'\n", out_buf);
+ }
+
+ TEST_CHECK(out_size == json_size);
+
+ if (out_buf) {
+ flb_sds_destroy(out_buf);
+ }
+ flb_free(file_msgp);
+ flb_free(file_json);
+ }
+
+ utf8_tests_destroy(n_tests);
+}
+
+void test_json_pack_bug1278()
+{
+ int i;
+ int len;
+ int ret;
+ int items;
+ int type;
+ char *p_in;
+ char *p_out;
+ char *out_buf;
+ size_t out_size;
+ msgpack_sbuffer mp_sbuf;
+ msgpack_packer mp_pck;
+ msgpack_unpacked result;
+ msgpack_object root;
+ msgpack_object val;
+ size_t off = 0;
+ flb_sds_t json;
+ char tmp[32];
+
+ char *in[] = {
+ "one\atwo",
+ "one\btwo",
+ "one\ttwo",
+ "one\ntwo",
+ "one\vtwo",
+ "one\ftwo",
+ "one\rtwo",
+ "\\n",
+ };
+
+ char *out[] = {
+ "\"one\\u0007two\"",
+ "\"one\\btwo\"",
+ "\"one\\ttwo\"",
+ "\"one\\ntwo\"",
+ "\"one\\u000btwo\"",
+ "\"one\\ftwo\"",
+ "\"one\\rtwo\"",
+ "\"\\\\n\"",
+ };
+
+ printf("\n");
+ items = sizeof(in) / sizeof(char *);
+ for (i = 0; i < items; i++) {
+ p_in = in[i];
+ p_out = out[i];
+
+ len = strlen(p_in);
+
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_str(&mp_pck, len);
+ msgpack_pack_str_body(&mp_pck, p_in, len);
+
+ /* Pack raw string as JSON */
+ json = flb_msgpack_raw_to_json_sds(mp_sbuf.data, mp_sbuf.size);
+
+ /* Compare expected JSON output */
+ ret = strcmp(p_out, json);
+ TEST_CHECK(ret == 0);
+ if (ret != 0) {
+ printf("== JSON comparisson failed ==\n");
+ printf("expected: %s\n", p_out);
+ printf("output : %s\n", json);
+ }
+ else {
+ printf("test %i out => %s\n", i, json);
+ }
+ /* Put JSON string in a map and convert it to msgpack */
+ snprintf(tmp, sizeof(tmp) -1 , "{\"log\": %s}", json);
+ ret = flb_pack_json(tmp, strlen(tmp), &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret != 0) {
+ printf("failed packaging to JSON\n");
+ }
+
+ /* Unpack 'log' value and compare it to the original raw */
+ off = 0;
+ msgpack_unpacked_init(&result);
+ ret = msgpack_unpack_next(&result, out_buf, out_size, &off);
+ TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS);
+
+ /* Check parent type is a map */
+ root = result.data;
+ TEST_CHECK(root.type == MSGPACK_OBJECT_MAP);
+
+ /* Get map value */
+ val = root.via.map.ptr[0].val;
+ TEST_CHECK(val.type == MSGPACK_OBJECT_STR);
+
+ /* Compare bytes length */
+ len = strlen(p_in);
+ TEST_CHECK(len == val.via.str.size);
+ if (len != val.via.str.size) {
+ printf("failed comparing string length\n");
+ }
+
+ /* Compare raw bytes */
+ ret = memcmp(val.via.str.ptr, p_in, len);
+ TEST_CHECK(ret == 0);
+ if (ret != 0) {
+ printf("failed comparing to original value\n");
+ }
+
+ /* Relese resources */
+ flb_free(out_buf);
+ flb_sds_destroy(json);
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&mp_sbuf);
+
+ }
+}
+
+void test_json_pack_nan()
+{
+ int ret;
+ char json_str[128] = {0};
+ char *p = NULL;
+ struct flb_config config;
+ msgpack_sbuffer mp_sbuf;
+ msgpack_packer mp_pck;
+ msgpack_object obj;
+ msgpack_zone mempool;
+
+ config.convert_nan_to_null = FLB_TRUE;
+
+ // initialize msgpack
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+ msgpack_pack_double(&mp_pck, NAN);
+ msgpack_zone_init(&mempool, 2048);
+ msgpack_unpack(mp_sbuf.data, mp_sbuf.size, NULL, &mempool, &obj);
+ msgpack_zone_destroy(&mempool);
+ msgpack_sbuffer_destroy(&mp_sbuf);
+
+ // convert msgpack to json
+ ret = flb_msgpack_to_json(&json_str[0], sizeof(json_str), &obj);
+ TEST_CHECK(ret >= 0);
+
+ p = strstr(&json_str[0], "nan");
+ if (!TEST_CHECK(p != NULL)) {
+ TEST_MSG("json should be nan. json_str=%s", json_str);
+ }
+
+ // convert. nan -> null
+ memset(&json_str[0], 0, sizeof(json_str));
+ flb_pack_init(&config);
+ ret = flb_msgpack_to_json(&json_str[0], sizeof(json_str), &obj);
+ TEST_CHECK(ret >= 0);
+
+ p = strstr(&json_str[0], "null");
+ if (!TEST_CHECK(p != NULL)) {
+ TEST_MSG("json should be null. json_str=%s", json_str);
+ }
+
+ // clear setting
+ config.convert_nan_to_null = FLB_FALSE;
+ flb_pack_init(&config);
+}
+
+static int check_msgpack_val(msgpack_object obj, int expected_type, char *expected_val)
+{
+ int len;
+
+ if (!TEST_CHECK(obj.type == expected_type)) {
+ TEST_MSG("type mismatch\nexpected=%d got=%d", expected_type, obj.type);
+ return -1;
+ }
+ switch(obj.type) {
+ case MSGPACK_OBJECT_MAP:
+ if(!TEST_CHECK(obj.via.map.size == atoi(expected_val))) {
+ TEST_MSG("map size mismatch\nexpected=%s got=%d", expected_val, obj.via.map.size);
+ return -1;
+ }
+ break;
+
+ case MSGPACK_OBJECT_ARRAY:
+ if(!TEST_CHECK(obj.via.array.size == atoi(expected_val))) {
+ TEST_MSG("array size mismatch\nexpected=%s got=%d", expected_val, obj.via.array.size);
+ return -1;
+ }
+ break;
+
+ case MSGPACK_OBJECT_STR:
+ len = strlen(expected_val);
+ if (!TEST_CHECK(obj.via.str.size == strlen(expected_val))) {
+ TEST_MSG("str size mismatch\nexpected=%d got=%d", len, obj.via.str.size);
+ return -1;
+ }
+ else if(!TEST_CHECK(strncmp(expected_val, obj.via.str.ptr ,len) == 0)) {
+ TEST_MSG("str mismatch\nexpected=%.*s got=%.*s", len, expected_val, len, obj.via.str.ptr);
+ return -1;
+ }
+ break;
+
+ case MSGPACK_OBJECT_POSITIVE_INTEGER:
+ if(!TEST_CHECK(obj.via.u64 == (uint64_t)atoi(expected_val))) {
+ TEST_MSG("int mismatch\nexpected=%s got=%"PRIu64, expected_val, obj.via.u64);
+ return -1;
+ }
+ break;
+
+ case MSGPACK_OBJECT_BOOLEAN:
+ if (obj.via.boolean) {
+ if(!TEST_CHECK(strncasecmp(expected_val, "true",4) == 0)) {
+ TEST_MSG("bool mismatch\nexpected=%s got=true", expected_val);
+ return -1;
+ }
+ }
+ else {
+ if(!TEST_CHECK(strncasecmp(expected_val, "false",5) == 0)) {
+ TEST_MSG("bool mismatch\nexpected=%s got=false", expected_val);
+ return -1;
+ }
+ }
+ break;
+
+ default:
+ TEST_MSG("unknown type %d", obj.type);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * https://github.com/fluent/fluent-bit/issues/5336
+ * Pack "valid JSON + partial JSON"
+ */
+#define JSON_BUG5336 "{\"int\":10, \"string\":\"hello\", \"bool\":true, \"array\":[0,1,2]}"
+void test_json_pack_bug5336()
+{
+ int ret;
+ char *json_valid = JSON_BUG5336;
+ size_t len = strlen(json_valid);
+
+ char *json_incomplete = JSON_BUG5336 JSON_BUG5336;
+ char *out = NULL;
+ int out_size;
+ struct flb_pack_state state;
+ int i;
+
+ msgpack_unpacked result;
+ msgpack_object obj;
+ size_t off = 0;
+
+ int loop_cnt = 0;
+
+ for (i=len; i<len*2; i++) {
+ loop_cnt++;
+
+ flb_pack_state_init(&state);
+
+ /* Pass small string size to create incomplete JSON */
+ ret = flb_pack_json_state(json_incomplete, i, &out, &out_size, &state);
+ if (!TEST_CHECK(ret != FLB_ERR_JSON_INVAL)) {
+ TEST_MSG("%ld: FLB_ERR_JSON_INVAL\njson=%.*s", i-len, i, json_incomplete);
+ exit(EXIT_FAILURE);
+ }
+ else if(!TEST_CHECK(ret != FLB_ERR_JSON_PART)) {
+ TEST_MSG("%ld: FLB_ERR_JSON_PART\njson=%.*s", i-len, i, json_incomplete);
+ exit(EXIT_FAILURE);
+ }
+
+ /* unpack parsed data */
+ msgpack_unpacked_init(&result);
+ off = 0;
+ TEST_CHECK(msgpack_unpack_next(&result, out, out_size, &off) == MSGPACK_UNPACK_SUCCESS);
+
+ TEST_CHECK(check_msgpack_val(result.data, MSGPACK_OBJECT_MAP, "4" /*map size*/) == 0);
+
+ /* "int":10 */
+ obj = result.data.via.map.ptr[0].key;
+ TEST_CHECK(check_msgpack_val(obj, MSGPACK_OBJECT_STR, "int") == 0);
+ obj = result.data.via.map.ptr[0].val;
+ TEST_CHECK(check_msgpack_val(obj, MSGPACK_OBJECT_POSITIVE_INTEGER, "10") == 0);
+
+ /* "string":"hello"*/
+ obj = result.data.via.map.ptr[1].key;
+ TEST_CHECK(check_msgpack_val(obj, MSGPACK_OBJECT_STR, "string") == 0);
+ obj = result.data.via.map.ptr[1].val;
+ TEST_CHECK(check_msgpack_val(obj, MSGPACK_OBJECT_STR, "hello") == 0);
+
+ /* "bool":true */
+ obj = result.data.via.map.ptr[2].key;
+ TEST_CHECK(check_msgpack_val(obj, MSGPACK_OBJECT_STR, "bool") == 0);
+ obj = result.data.via.map.ptr[2].val;
+ TEST_CHECK(check_msgpack_val(obj, MSGPACK_OBJECT_BOOLEAN, "true") == 0);
+
+ /* "array":[0,1,2] */
+ obj = result.data.via.map.ptr[3].key;
+ TEST_CHECK(check_msgpack_val(obj, MSGPACK_OBJECT_STR, "array") == 0);
+ obj = result.data.via.map.ptr[3].val;
+ TEST_CHECK(check_msgpack_val(obj, MSGPACK_OBJECT_ARRAY, "3" /*array size*/) == 0);
+ TEST_CHECK(check_msgpack_val(obj.via.array.ptr[0], MSGPACK_OBJECT_POSITIVE_INTEGER, "0") == 0);
+ TEST_CHECK(check_msgpack_val(obj.via.array.ptr[1], MSGPACK_OBJECT_POSITIVE_INTEGER, "1") == 0);
+ TEST_CHECK(check_msgpack_val(obj.via.array.ptr[2], MSGPACK_OBJECT_POSITIVE_INTEGER, "2") == 0);
+
+ msgpack_unpacked_destroy(&result);
+ flb_free(out);
+ flb_pack_state_reset(&state);
+ }
+
+ if(!TEST_CHECK(loop_cnt == len)) {
+ TEST_MSG("loop_cnt expect=%ld got=%d", len, loop_cnt);
+ }
+}
+
+const char input_msgpack[] = {0x92,/* array 2 */
+ 0xd7, 0x00, /* event time*/
+ 0x07, 0x5b, 0xcd, 0x15, /* second = 123456789 = 1973/11/29 21:33:09 */
+ 0x07, 0x5b, 0xcd, 0x15, /* nanosecond = 123456789 */
+ 0x81, 0xa2, 0x61, 0x61, 0xa2, 0x62, 0x62 /* {"aa":"bb"} */
+};
+
+void test_json_date(char* expect, int date_format)
+{
+ flb_sds_t json_key;
+ flb_sds_t ret;
+
+ json_key = flb_sds_create("date");
+ if (!TEST_CHECK(json_key != NULL)) {
+ TEST_MSG("flb_sds_create failed");
+ exit(1);
+ }
+
+ ret = flb_pack_msgpack_to_json_format((const char*)&input_msgpack[0], sizeof(input_msgpack),
+ FLB_PACK_JSON_FORMAT_JSON, date_format,
+ json_key);
+ if (!TEST_CHECK(ret != NULL)) {
+ TEST_MSG("flb_pack_msgpack_to_json_format failed");
+ flb_sds_destroy(json_key);
+ exit(1);
+ }
+ flb_sds_destroy(json_key);
+
+ if (!TEST_CHECK(strstr(ret, expect) != NULL)) {
+ TEST_MSG("mismatch. Got=%s expect=%s", ret, expect);
+ }
+
+ flb_sds_destroy(ret);
+}
+
+void test_json_date_iso8601()
+{
+ test_json_date("1973-11-29T21:33:09.123456Z", FLB_PACK_JSON_DATE_ISO8601);
+}
+
+void test_json_date_double()
+{
+ test_json_date("123456789.123456", FLB_PACK_JSON_DATE_DOUBLE);
+}
+
+void test_json_date_java_sql()
+{
+ test_json_date("1973-11-29 21:33:09.123456", FLB_PACK_JSON_DATE_JAVA_SQL_TIMESTAMP);
+}
+
+void test_json_date_epoch()
+{
+ test_json_date("123456789", FLB_PACK_JSON_DATE_EPOCH);
+}
+
+void test_json_date_epoch_ms()
+{
+ test_json_date("123456789123", FLB_PACK_JSON_DATE_EPOCH_MS);
+}
+
+TEST_LIST = {
+ /* JSON maps iteration */
+ { "json_pack" , test_json_pack },
+ { "json_pack_iter" , test_json_pack_iter},
+ { "json_pack_mult" , test_json_pack_mult},
+ { "json_pack_mult_iter", test_json_pack_mult_iter},
+ { "json_macros" , test_msgpack_to_json_macros},
+ { "json_dup_keys" , test_json_dup_keys},
+ { "json_pack_bug342" , test_json_pack_bug342},
+ { "json_pack_bug1278" , test_json_pack_bug1278},
+ { "json_pack_nan" , test_json_pack_nan},
+ { "json_pack_bug5336" , test_json_pack_bug5336},
+ { "json_date_iso8601" , test_json_date_iso8601},
+ { "json_date_double" , test_json_date_double},
+ { "json_date_java_sql" , test_json_date_java_sql},
+ { "json_date_epoch" , test_json_date_epoch},
+ { "json_date_epoch_ms" , test_json_date_epoch_ms},
+
+ /* Mixed bytes, check JSON encoding */
+ { "utf8_to_json", test_utf8_to_json},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/parser.c b/fluent-bit/tests/internal/parser.c
new file mode 100644
index 00000000..6a560db7
--- /dev/null
+++ b/fluent-bit/tests/internal/parser.c
@@ -0,0 +1,528 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_error.h>
+#include <fluent-bit/flb_str.h>
+#include <fluent-bit/flb_sds.h>
+
+#include <time.h>
+#include <string.h>
+#include "flb_tests_internal.h"
+
+/* Parsers configuration */
+#define JSON_PARSERS FLB_TESTS_DATA_PATH "/data/parser/json.conf"
+#define REGEX_PARSERS FLB_TESTS_DATA_PATH "/data/parser/regex.conf"
+
+/* Templates */
+#define JSON_FMT_01 "{\"key001\": 12345, \"key002\": 0.99, \"time\": \"%s\"}"
+#define REGEX_FMT_01 "12345 0.99 %s"
+
+#define isleap(y) ((y) % 4 == 0 && ((y) % 400 == 0 || (y) % 100 != 0))
+#define year2sec(y) (isleap(y) ? 31622400 : 31536000)
+
+/* Timezone */
+struct tz_check {
+ char *val;
+ int diff;
+};
+
+
+struct tz_check tz_entries_ok[] = {
+ {"+0000", 0},
+ {"+00:00", 0},
+ {"+00:59", 3540},
+ {"-0600", -21000},
+ {"-06:00", -21000},
+};
+
+struct tz_check tz_entries_error[] = {
+ {"0000", 0},
+ {"+00:90", 0},
+ {"--600", 0},
+ {"-06:00", -21000},
+};
+
+/* Time Lookup */
+struct time_check {
+ char *parser_name;
+ char *time_string;
+ time_t epoch;
+ double frac_seconds;
+
+ /*
+ * Some tests requires to set the UTC offset to the given time,
+ * when this flag is enabled the parser is adjusted for each test.
+ */
+ int utc_offset;
+};
+
+struct time_check time_entries[] = {
+ /*
+ * Samples generated by scripts/dates.sh
+ * =====================================
+ * UTC => 07/17/2017 20:17:03 +0000, 1500322623
+ * IST => 07/18/2017 01:47:03 +0530, 1500322623
+ * JP => 07/18/2017 05:17:03 +0900, 1500322623
+ * ZW => 07/17/2017 22:17:03 +0200, 1500322623
+ */
+
+ /*
+ * No year tests (old Syslog)
+ * ==========================
+ */
+
+ /* Fixed UTC Offset = -0600 (-21600) */
+ {"no_year" , "Feb 16 04:06:58" , 1487239618, 0 , -21600},
+ {"no_year_N" , "Feb 16 04:06:58.1234" , 1487239618, 0.1234, -21600},
+ {"no_year_NC" , "Feb 16 04:06:58,1234" , 1487239618, 0.1234, -21600},
+
+ /* No year with imezone specified */
+ {"no_year_TZ" , "Feb 16 04:06:58 -0600" , 1487239618, 0 , 0},
+ {"no_year_N_TZ", "Feb 16 04:06:58.1234 -0600", 1487239618, 0.1234, 0},
+ {"no_year_NC_TZ","Feb 16 04:06:58,1234 -0600", 1487239618, 0.1234, 0},
+
+ /* Same date for different timezones, same timestamp */
+ {"generic_TZ" , "07/17/2017 20:17:03 +0000" , 1500322623, 0, 0},
+ {"generic_TZ" , "07/18/2017 01:47:03 +0530" , 1500322623, 0, 0},
+ {"generic_TZ" , "07/18/2017 05:17:03 +0900" , 1500322623, 0, 0},
+ {"generic_TZ" , "07/17/2017 22:17:03 +0200" , 1500322623, 0, 0},
+ {"generic_N_TZ" , "07/17/2017 22:17:03.1 +0200", 1500322623, 0.1, 0},
+ {"generic_NC_TZ", "07/17/2017 22:17:03,1 +0200", 1500322623, 0.1, 0},
+#ifndef __APPLE__
+ {"generic_TZ" , "07/18/2017 01:47:03 +05:30" , 1500322623, 0, 0},
+ {"generic_N_TZ" , "07/17/2017 22:17:03.1 +02:00", 1500322623, 0.1, 0},
+ {"generic_NC_TZ", "07/17/2017 22:17:03,1 +02:00", 1500322623, 0.1, 0},
+ {"generic_NL_TZ", "07/17/2017 22:17:03:1 +02:00", 1500322623, 0.1, 0},
+#endif
+ /* Same date for different timezones, same timestamp w/ fixed UTC offset */
+ {"generic" , "07/18/2017 01:47:03" , 1500322623, 0, 19800},
+ {"generic" , "07/18/2017 05:17:03" , 1500322623, 0, 32400},
+ {"generic" , "07/17/2017 22:17:03" , 1500322623, 0, 7200},
+ {"generic_N" , "07/17/2017 22:17:03.1" , 1500322623, 0.1, 7200},
+ {"generic_NC", "07/17/2017 22:17:03,1" , 1500322623, 0.1, 7200},
+
+ /* default UTC: the following timings 'are' in UTC already */
+ {"default_UTC" , "07/17/2017 20:17:03" , 1500322623, 0 , 0},
+ {"default_UTC_Z" , "07/17/2017 20:17:03Z" , 1500322623, 0 , 0},
+ {"default_UTC_N_Z", "07/17/2017 20:17:03.1234Z", 1500322623, 0.1234, 0},
+ {"default_UTC_NC_Z","07/17/2017 20:17:03,1234Z", 1500322623, 0.1234, 0},
+
+ {"apache_error", "Fri Jul 17 20:17:03.1234 2017", 1500322623, 0.1234, 0}
+};
+
+
+int flb_parser_json_do(struct flb_parser *parser,
+ char *buf, size_t length,
+ void **out_buf, size_t *out_size,
+ struct flb_time *out_time);
+
+int flb_parser_regex_do(struct flb_parser *parser,
+ char *buf, size_t length,
+ void **out_buf, size_t *out_size,
+ struct flb_time *out_time);
+
+/* Parse timezone string and get the offset */
+void test_parser_tzone_offset()
+{
+ int i;
+ int len;
+ int ret;
+ int diff;
+ struct tz_check *t;
+
+ /* Valid offsets */
+ for (i = 0; i < sizeof(tz_entries_ok) / sizeof(struct tz_check); i++) {
+ t = &tz_entries_ok[0];
+ len = strlen(t->val);
+
+ ret = flb_parser_tzone_offset(t->val, len, &diff);
+ TEST_CHECK(ret == 0 && diff == t->diff);
+ }
+
+ /* Invalid offsets */
+ for (i = 0; i < sizeof(tz_entries_error) / sizeof(struct tz_check); i++) {
+ t = &tz_entries_error[0];
+ len = strlen(t->val);
+
+ ret = flb_parser_tzone_offset(t->val, len, &diff);
+ TEST_CHECK(ret != 0);
+ }
+}
+
+static void load_json_parsers(struct flb_config *config)
+{
+ int ret;
+
+ ret = flb_parser_conf_file(JSON_PARSERS, config);
+ TEST_CHECK(ret == 0);
+}
+
+static void load_regex_parsers(struct flb_config *config)
+{
+ int ret;
+
+ ret = flb_parser_conf_file(REGEX_PARSERS, config);
+ TEST_CHECK(ret == 0);
+}
+
+void test_parser_time_lookup()
+{
+ int i;
+ int j;
+ int len;
+ int ret;
+ int toff;
+ int year_diff = 0;
+ double ns;
+ time_t now;
+ time_t epoch;
+ struct flb_parser *p;
+ struct flb_config *config;
+ struct time_check *t;
+ struct flb_tm tm;
+
+ config = flb_config_init();
+
+ load_json_parsers(config);
+
+ /* Iterate tests */
+ now = time(NULL);
+ for (i = 0; i < sizeof(time_entries) / sizeof(struct time_check); i++) {
+ t = &time_entries[i];
+ p = flb_parser_get(t->parser_name, config);
+ TEST_CHECK(p != NULL);
+
+ if (p == NULL) {
+ continue;
+ }
+
+ /* Alter time offset if set */
+ toff = 0;
+ if (t->utc_offset != 0) {
+ toff = p->time_offset;
+ p->time_offset = t->utc_offset;
+ }
+
+ /* Adjust timestamp for parsers using no-year */
+ if (p->time_with_year == FLB_FALSE) {
+ time_t time_test = t->epoch;
+ struct tm tm_now;
+ struct tm tm_test;
+
+ gmtime_r(&now, &tm_now);
+ gmtime_r(&time_test, &tm_test);
+
+ year_diff = 0;
+ for (j = tm_test.tm_year; j < tm_now.tm_year; j++) {
+ year_diff += year2sec(tm_test.tm_mon < 2 ? j : j + 1);
+ }
+ }
+ else {
+ year_diff = 0;
+ }
+
+ /* Lookup time */
+ len = strlen(t->time_string);
+ ret = flb_parser_time_lookup(t->time_string, len, now, p, &tm, &ns);
+ if(!(TEST_CHECK(ret == 0))) {
+ TEST_MSG("time lookup error: parser:'%s' timestr:'%s'", t->parser_name, t->time_string);
+ continue;
+ }
+
+ epoch = flb_parser_tm2time(&tm);
+ epoch -= year_diff;
+ TEST_CHECK(t->epoch == epoch);
+ TEST_CHECK(t->frac_seconds == ns);
+
+ if (t->utc_offset != 0) {
+ p->time_offset = toff;
+ }
+ }
+
+ flb_parser_exit(config);
+ flb_config_exit(config);
+}
+
+/* Do time lookup using the JSON parser backend*/
+void test_json_parser_time_lookup()
+{
+ int i;
+ int j;
+ int ret;
+ int len;
+ int toff;
+ int year_diff = 0;
+ time_t epoch;
+ long nsec;
+ char buf[512];
+ void *out_buf;
+ size_t out_size;
+ struct flb_time out_time;
+ struct flb_parser *p;
+ struct flb_config *config;
+ struct time_check *t;
+
+ config = flb_config_init();
+
+ /* Load parsers */
+ load_json_parsers(config);
+
+ for (i = 0; i < sizeof(time_entries) / sizeof(struct time_check); i++) {
+ t = &time_entries[i];
+ p = flb_parser_get(t->parser_name, config);
+ TEST_CHECK(p != NULL);
+
+ if (p == NULL) {
+ continue;
+ }
+
+ /* Alter time offset if set */
+ toff = 0;
+ if (t->utc_offset != 0) {
+ toff = p->time_offset;
+ p->time_offset = t->utc_offset;
+ }
+
+ /* Adjust timestamp for parsers using no-year */
+ if (p->time_with_year == FLB_FALSE) {
+ time_t time_now = time(NULL);
+ time_t time_test = t->epoch;
+ struct tm tm_now;
+ struct tm tm_test;
+
+ gmtime_r(&time_now, &tm_now);
+ gmtime_r(&time_test, &tm_test);
+
+ year_diff = 0;
+ for (j = tm_test.tm_year; j < tm_now.tm_year; j++) {
+ year_diff += year2sec(tm_test.tm_mon < 2 ? j : j + 1);
+ }
+ }
+ else {
+ year_diff = 0;
+ }
+
+ /* Compose the string */
+ len = snprintf(buf, sizeof(buf) - 1, JSON_FMT_01, t->time_string);
+
+ /* Invoke the JSON parser backend */
+ ret = flb_parser_json_do(p, buf, len, &out_buf, &out_size, &out_time);
+ TEST_CHECK(ret != -1);
+ TEST_CHECK(out_buf != NULL);
+
+ /* Check time */
+ epoch = t->epoch + year_diff;
+
+ TEST_CHECK(out_time.tm.tv_sec == epoch);
+ nsec = t->frac_seconds * 1000000000;
+ TEST_CHECK(out_time.tm.tv_nsec == nsec);
+
+ if (t->utc_offset != 0) {
+ p->time_offset = toff;
+ }
+
+ flb_free(out_buf);
+ }
+
+ flb_parser_exit(config);
+ flb_config_exit(config);
+}
+
+/* Do time lookup using the Regex parser backend*/
+void test_regex_parser_time_lookup()
+{
+ int i;
+ int j;
+ int ret;
+ int len;
+ int toff;
+ int year_diff = 0;
+ time_t epoch;
+ long nsec;
+ char buf[512];
+ void *out_buf;
+ size_t out_size;
+ struct flb_time out_time;
+ struct flb_parser *p;
+ struct flb_config *config;
+ struct time_check *t;
+
+ config = flb_config_init();
+
+ /* Load parsers */
+ load_regex_parsers(config);
+
+ for (i = 0; i < sizeof(time_entries) / sizeof(struct time_check); i++) {
+ t = &time_entries[i];
+ p = flb_parser_get(t->parser_name, config);
+ TEST_CHECK(p != NULL);
+
+ if (p == NULL) {
+ continue;
+ }
+
+ /* Alter time offset if set */
+ toff = 0;
+ if (t->utc_offset != 0) {
+ toff = p->time_offset;
+ p->time_offset = t->utc_offset;
+ }
+
+ /* Adjust timestamp for parsers using no-year */
+ if (p->time_with_year == FLB_FALSE) {
+ time_t time_now = time(NULL);
+ time_t time_test = t->epoch;
+ struct tm tm_now;
+ struct tm tm_test;
+
+ gmtime_r(&time_now, &tm_now);
+ gmtime_r(&time_test, &tm_test);
+
+ year_diff = 0;
+ for (j = tm_test.tm_year; j < tm_now.tm_year; j++) {
+ year_diff += year2sec(tm_test.tm_mon < 2 ? j : j + 1);
+ }
+ }
+ else {
+ year_diff = 0;
+ }
+
+ /* Compose the string */
+ len = snprintf(buf, sizeof(buf) - 1, REGEX_FMT_01, t->time_string);
+
+ /* Invoke the JSON parser backend */
+ ret = flb_parser_regex_do(p, buf, len, &out_buf, &out_size, &out_time);
+ TEST_CHECK(ret != -1);
+ TEST_CHECK(out_buf != NULL);
+
+ /* Adjust time without year */
+ epoch = t->epoch + year_diff;
+
+ /* Check time */
+ TEST_CHECK(out_time.tm.tv_sec == epoch);
+ nsec = t->frac_seconds * 1000000000;
+ TEST_CHECK(out_time.tm.tv_nsec == nsec);
+
+ if (t->utc_offset != 0) {
+ p->time_offset = toff;
+ }
+
+ flb_free(out_buf);
+ }
+
+ flb_parser_exit(config);
+ flb_config_exit(config);
+}
+
+static char *get_msgpack_map_key(void *buf, size_t buf_size, char *key) {
+ int i;
+ size_t off = 0;
+ int key_size;
+ char *ptr = NULL;
+ msgpack_unpacked result;
+ msgpack_object map;
+ msgpack_object k;
+ msgpack_object v;
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, buf, buf_size, &off);
+
+ map = result.data;
+
+ if (map.type != MSGPACK_OBJECT_MAP) {
+ msgpack_unpacked_destroy(&result);
+ return NULL;
+ }
+
+ key_size = strlen(key);
+
+
+ /* printf("map_size: %d\n", map.via.map.size); */
+
+ for (i = 0; i < map.via.map.size; i++) {
+ k = map.via.map.ptr[i].key;
+ v = map.via.map.ptr[i].val;
+ if (k.type != MSGPACK_OBJECT_STR) {
+ continue;
+ }
+ /* printf("key(%.*s)(%d) == (%s)(%d)\n", k.via.str.size, k.via.str.ptr, k.via.str.size, key, key_size); */
+ if (k.via.str.size == key_size && strncmp(key, (char *) k.via.str.ptr, k.via.str.size) == 0) {
+ ptr = flb_strndup(v.via.str.ptr, v.via.str.size);
+ break;
+ }
+ }
+
+ msgpack_unpacked_destroy(&result);
+
+ return ptr;
+}
+
+static int a_mysql_unquote_test(struct flb_parser *p, char *source, char *expected) {
+
+ int ret;
+ void *out_buf;
+ size_t out_size;
+ struct flb_time out_time;
+ char *val001;
+
+
+ ret = flb_parser_regex_do(p, source, strlen(source), &out_buf, &out_size, &out_time);
+
+ TEST_CHECK(ret != -1);
+ TEST_CHECK(out_buf != NULL);
+ if(ret < 0 || out_buf == NULL) return -1;
+
+ val001 = get_msgpack_map_key(out_buf, out_size, "key001");
+ if(!TEST_CHECK(val001 != NULL)) {
+ flb_free(out_buf);
+ return -1;
+ }
+
+ TEST_CHECK_(strcmp(val001,expected) == 0, "source(%s) expected(%s) got(%s)", source, expected, val001);
+ flb_free(val001);
+ flb_free(out_buf);
+
+ return 1;
+}
+
+
+void test_mysql_unquoted()
+{
+ struct flb_parser *p;
+ struct flb_config *config;
+
+ config = flb_config_init();
+
+ /* Load parsers */
+ load_regex_parsers(config);
+
+ p = flb_parser_get("mysql_quoted_stuff", config);
+ TEST_CHECK(p != NULL);
+
+ a_mysql_unquote_test(p,"2010-01-01 02:10:22,plain", "plain");
+ a_mysql_unquote_test(p,"2010-01-01 02:10:22,''", "");
+ a_mysql_unquote_test(p,"2010-01-01 02:10:22,'333'", "333");
+ a_mysql_unquote_test(p,"2010-01-01 02:10:22,'\\n'", "\n");
+ a_mysql_unquote_test(p,"2010-01-01 02:10:22,'\\r'", "\r");
+ a_mysql_unquote_test(p,"2010-01-01 02:10:22,'\\''", "'");
+ a_mysql_unquote_test(p,"2010-01-01 02:10:22,'\\\"'", "\"");
+ a_mysql_unquote_test(p,"2010-01-01 02:10:22,'\\\\'", "\\");
+ a_mysql_unquote_test(p,"2010-01-01 02:10:22,'abc\\nE\\\\'", "abc\nE\\");
+
+ flb_parser_exit(config);
+ flb_config_exit(config);
+
+
+}
+
+
+TEST_LIST = {
+ { "tzone_offset", test_parser_tzone_offset},
+ { "time_lookup", test_parser_time_lookup},
+ { "json_time_lookup", test_json_parser_time_lookup},
+ { "regex_time_lookup", test_regex_parser_time_lookup},
+ { "mysql_unquoted" , test_mysql_unquoted },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/parser_json.c b/fluent-bit/tests/internal/parser_json.c
new file mode 100644
index 00000000..5a9b6ead
--- /dev/null
+++ b/fluent-bit/tests/internal/parser_json.c
@@ -0,0 +1,547 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_config.h>
+#include <fluent-bit/flb_config_format.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_parser_decoder.h>
+#include <msgpack.h>
+#include <float.h>
+#include <math.h>
+#include "flb_tests_internal.h"
+
+static int msgpack_strncmp(char* str, size_t str_len, msgpack_object obj)
+{
+ int ret = -1;
+
+ if (str == NULL) {
+ flb_error("str is NULL");
+ return -1;
+ }
+
+ switch (obj.type) {
+ case MSGPACK_OBJECT_STR:
+ if (obj.via.str.size != str_len) {
+ return -1;
+ }
+ ret = strncmp(str, obj.via.str.ptr, str_len);
+ break;
+ case MSGPACK_OBJECT_POSITIVE_INTEGER:
+ {
+ unsigned long val = strtoul(str, NULL, 10);
+ if (val == (unsigned long)obj.via.u64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_NEGATIVE_INTEGER:
+ {
+ long long val = strtoll(str, NULL, 10);
+ if (val == (unsigned long)obj.via.i64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_FLOAT32:
+ case MSGPACK_OBJECT_FLOAT64:
+ {
+ double val = strtod(str, NULL);
+ if (fabs(val - obj.via.f64) < DBL_EPSILON) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_BOOLEAN:
+ if (obj.via.boolean) {
+ if (str_len != 4 /*true*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "true", 4);
+ }
+ else {
+ if (str_len != 5 /*false*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "false", 5);
+ }
+ break;
+ default:
+ flb_error("not supported");
+ }
+
+ return ret;
+}
+
+struct str_list {
+ size_t size;
+ char **lists;
+};
+
+static int compare_msgpack(void *msgpack_data, size_t msgpack_size, struct str_list *l)
+{
+ msgpack_unpacked result;
+ msgpack_object obj;
+ size_t off = 0;
+ int map_size;
+ int i_map;
+ int i_list;
+ int num = 0;
+
+ if (!TEST_CHECK(msgpack_data != NULL)) {
+ TEST_MSG("msgpack_data is NULL");
+ return -1;
+ }
+ else if (!TEST_CHECK(msgpack_size > 0)) {
+ TEST_MSG("msgpack_size is 0");
+ return -1;
+ }
+
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, msgpack_data, msgpack_size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ obj = result.data;
+ /*
+ msgpack_object_print(stdout, obj);
+ */
+ if (!TEST_CHECK(obj.type == MSGPACK_OBJECT_MAP)) {
+ TEST_MSG("map error. type = %d", obj.type);
+ continue;
+ }
+ map_size = obj.via.map.size;
+ for (i_map=0; i_map<map_size; i_map++) {
+ if (!TEST_CHECK(obj.via.map.ptr[i_map].key.type == MSGPACK_OBJECT_STR)) {
+ TEST_MSG("key is not string. type =%d", obj.via.map.ptr[i_map].key.type);
+ continue;
+ }
+ for (i_list=0; i_list< l->size/2; i_list++) {
+ if (msgpack_strncmp(l->lists[i_list*2], strlen(l->lists[i_list*2]),
+ obj.via.map.ptr[i_map].key) == 0 &&
+ msgpack_strncmp(l->lists[i_list*2+1], strlen(l->lists[i_list*2+1]),
+ obj.via.map.ptr[i_map].val) == 0) {
+ num++;
+ }
+ }
+ }
+ }
+ msgpack_unpacked_destroy(&result);
+ if (!TEST_CHECK(num == l->size/2)) {
+ msgpack_object_print(stdout, obj);
+ putchar('\n');
+ TEST_MSG("compare failed. matched_num=%d expect=%lu", num, l->size/2);
+ return -1;
+ }
+ return 0;
+}
+
+void test_basic()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "{\"str\":\"text\", \"int\":100, \"double\":1.23, \"bool\":true}";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("json", "json", NULL, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_time_key()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "{\"str\":\"text\", \"int\":100, \"double\":1.23, \"bool\":true, \"time\":\"2022-10-31T12:00:01.123\"}";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ out_time.tm.tv_sec = 0;
+ out_time.tm.tv_nsec = 0;
+
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("json", "json", NULL, FLB_FALSE, "%Y-%m-%dT%H:%M:%S.%L", "time", NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ if (!TEST_CHECK(out_time.tm.tv_sec == 1667217601 && out_time.tm.tv_nsec == 123000000)) {
+ TEST_MSG("timestamp error. sec Got=%ld Expect=1667217601", out_time.tm.tv_sec);
+ TEST_MSG("timestamp error. nsec Got=%ld Expect=123000000", out_time.tm.tv_nsec);
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_time_keep()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "{\"str\":\"text\", \"int\":100, \"double\":1.23, \"bool\":true, \"time\":\"2022-10-31T12:00:01.123\"}";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true", "time", "2022-10-31T12:00:01.123"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ out_time.tm.tv_sec = 0;
+ out_time.tm.tv_nsec = 0;
+
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("json", "json", NULL, FLB_FALSE, "%Y-%m-%dT%H:%M:%S.%L", "time", NULL,
+ FLB_TRUE /*time_keep */, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ if (!TEST_CHECK(out_time.tm.tv_sec == 1667217601 && out_time.tm.tv_nsec == 123000000)) {
+ TEST_MSG("timestamp error. sec Got=%ld Expect=1667217601", out_time.tm.tv_sec);
+ TEST_MSG("timestamp error. nsec Got=%ld Expect=123000000", out_time.tm.tv_nsec);
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+/*
+ * JSON parser doesn't support 'types' option.
+ * This test is to check that 'types' doesn't affect output.
+ */
+void test_types_is_not_supported()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "{\"str\":\"text\", \"int\":100, \"double\":1.23, \"bool\":true}";
+ struct flb_parser_types *types = NULL;
+
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ /* Note: types will be released by flb_parser_destroy */
+ types = flb_malloc(sizeof(struct flb_parser_types));
+ if (!TEST_CHECK(types != NULL)) {
+ TEST_MSG("flb_malloc failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+ types->key = flb_malloc(strlen("int")+1);
+ if (!TEST_CHECK(types->key != NULL)) {
+ TEST_MSG("flb_malloc failed");
+ flb_free(types);
+ flb_config_exit(config);
+ exit(1);
+ }
+ strcpy(types->key, "int");
+ types->key_len = 3;
+ types->type = FLB_PARSER_TYPE_HEX;
+
+ parser = flb_parser_create("json", "json", NULL, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ types, 1, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_free(types->key);
+ flb_free(types);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_decode_field_json()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ struct cfl_variant *var = NULL;
+ int ret = 0;
+ char *input = "{\"json_str\":\"{\\\"str\\\":\\\"text\\\", \\\"int\\\":100, \\\"double\\\":1.23, \\\"bool\\\":true}\"}";
+ struct flb_cf *cf = NULL;
+ struct flb_cf_section *section = NULL;
+ struct mk_list *decoder = NULL;
+
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+ cf = flb_cf_create();
+ if (!TEST_CHECK(cf != NULL)) {
+ TEST_MSG("flb_cf_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ section = flb_cf_section_create(cf, "TEST", 4);
+ if (!TEST_CHECK(section != NULL)) {
+ TEST_MSG("flb_cf_section_create failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ var = flb_cf_section_property_add(cf, section->properties, "decode_field", 12, "json json_str", 13);
+ if(!TEST_CHECK(var != NULL)) {
+ TEST_MSG("flb_cf_section_property_add failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ decoder = flb_parser_decoder_list_create(section);
+ if (!TEST_CHECK(decoder != NULL)) {
+ TEST_MSG("flb_parser_decoder_list_create failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ parser = flb_parser_create("json", "json", NULL, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, decoder, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+}
+
+void test_time_key_kept_if_parse_fails()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "{\"str\":\"text\", \"time\":\"nonsense\"}";
+ char *time_format = "%Y-%m-%dT%H:%M:%S.%L";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "time", "nonsense"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ out_time.tm.tv_sec = 0;
+ out_time.tm.tv_nsec = 0;
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("json", "json", NULL, FLB_FALSE, time_format, "time", NULL,
+ FLB_FALSE, FLB_TRUE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+
+TEST_LIST = {
+ { "basic", test_basic},
+ { "time_key", test_time_key},
+ { "time_keep", test_time_keep},
+ { "types_is_not_supported", test_types_is_not_supported},
+ { "decode_field_json", test_decode_field_json},
+ { "time_key_kept_if_parse_fails", test_time_key_kept_if_parse_fails},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/parser_logfmt.c b/fluent-bit/tests/internal/parser_logfmt.c
new file mode 100644
index 00000000..ea363c53
--- /dev/null
+++ b/fluent-bit/tests/internal/parser_logfmt.c
@@ -0,0 +1,398 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_config.h>
+#include <fluent-bit/flb_config_format.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_parser_decoder.h>
+#include <msgpack.h>
+#include <float.h>
+#include <math.h>
+#include "flb_tests_internal.h"
+
+static int msgpack_strncmp(char* str, size_t str_len, msgpack_object obj)
+{
+ int ret = -1;
+
+ if (str == NULL) {
+ flb_error("str is NULL");
+ return -1;
+ }
+
+ switch (obj.type) {
+ case MSGPACK_OBJECT_STR:
+ if (obj.via.str.size != str_len) {
+ return -1;
+ }
+ ret = strncmp(str, obj.via.str.ptr, str_len);
+ break;
+ case MSGPACK_OBJECT_POSITIVE_INTEGER:
+ {
+ unsigned long val = strtoul(str, NULL, 10);
+ if (val == (unsigned long)obj.via.u64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_NEGATIVE_INTEGER:
+ {
+ long long val = strtoll(str, NULL, 10);
+ if (val == (unsigned long)obj.via.i64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_FLOAT32:
+ case MSGPACK_OBJECT_FLOAT64:
+ {
+ double val = strtod(str, NULL);
+ if (fabs(val - obj.via.f64) < DBL_EPSILON) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_BOOLEAN:
+ if (obj.via.boolean) {
+ if (str_len != 4 /*true*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "true", 4);
+ }
+ else {
+ if (str_len != 5 /*false*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "false", 5);
+ }
+ break;
+ default:
+ flb_error("not supported");
+ }
+
+ return ret;
+}
+
+struct str_list {
+ size_t size;
+ char **lists;
+};
+
+static int compare_msgpack(void *msgpack_data, size_t msgpack_size, struct str_list *l)
+{
+ msgpack_unpacked result;
+ msgpack_object obj;
+ size_t off = 0;
+ int map_size;
+ int i_map;
+ int i_list;
+ int num = 0;
+
+ if (!TEST_CHECK(msgpack_data != NULL)) {
+ TEST_MSG("msgpack_data is NULL");
+ return -1;
+ }
+ else if (!TEST_CHECK(msgpack_size > 0)) {
+ TEST_MSG("msgpack_size is 0");
+ return -1;
+ }
+
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, msgpack_data, msgpack_size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ obj = result.data;
+ /*
+ msgpack_object_print(stdout, obj);
+ */
+ if (!TEST_CHECK(obj.type == MSGPACK_OBJECT_MAP)) {
+ TEST_MSG("map error. type = %d", obj.type);
+ continue;
+ }
+ map_size = obj.via.map.size;
+ for (i_map=0; i_map<map_size; i_map++) {
+ if (!TEST_CHECK(obj.via.map.ptr[i_map].key.type == MSGPACK_OBJECT_STR)) {
+ TEST_MSG("key is not string. type =%d", obj.via.map.ptr[i_map].key.type);
+ continue;
+ }
+ for (i_list=0; i_list< l->size/2; i_list++) {
+ if (msgpack_strncmp(l->lists[i_list*2], strlen(l->lists[i_list*2]),
+ obj.via.map.ptr[i_map].key) == 0 &&
+ msgpack_strncmp(l->lists[i_list*2+1], strlen(l->lists[i_list*2+1]),
+ obj.via.map.ptr[i_map].val) == 0) {
+ num++;
+ }
+ }
+ }
+ }
+ msgpack_unpacked_destroy(&result);
+ if (!TEST_CHECK(num == l->size/2)) {
+ msgpack_object_print(stdout, obj);
+ putchar('\n');
+ TEST_MSG("compare failed. matched_num=%d expect=%lu", num, l->size/2);
+ return -1;
+ }
+ return 0;
+}
+
+void test_basic()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "str=\"text\" int=100 double=1.23 bool=true";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("logfmt", "logfmt", NULL, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_time_key()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "str=\"text\" int=100 double=1.23 bool=true time=2022-10-31T12:00:01.123";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ out_time.tm.tv_sec = 0;
+ out_time.tm.tv_nsec = 0;
+
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("logfmt", "logfmt", NULL, FLB_FALSE, "%Y-%m-%dT%H:%M:%S.%L", "time", NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ if (!TEST_CHECK(out_time.tm.tv_sec == 1667217601 && out_time.tm.tv_nsec == 123000000)) {
+ TEST_MSG("timestamp error. sec Got=%ld Expect=1667217601", out_time.tm.tv_sec);
+ TEST_MSG("timestamp error. nsec Got=%ld Expect=123000000", out_time.tm.tv_nsec);
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_time_keep()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "str=\"text\" int=100 double=1.23 bool=true time=2022-10-31T12:00:01.123";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true", "time", "2022-10-31T12:00:01.123"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ out_time.tm.tv_sec = 0;
+ out_time.tm.tv_nsec = 0;
+
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("logfmt", "logfmt", NULL, FLB_FALSE, "%Y-%m-%dT%H:%M:%S.%L", "time", NULL,
+ FLB_TRUE /*time_keep */, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ if (!TEST_CHECK(out_time.tm.tv_sec == 1667217601 && out_time.tm.tv_nsec == 123000000)) {
+ TEST_MSG("timestamp error. sec Got=%ld Expect=1667217601", out_time.tm.tv_sec);
+ TEST_MSG("timestamp error. nsec Got=%ld Expect=123000000", out_time.tm.tv_nsec);
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_types()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "str=\"text\" int=100 double=1.23 bool=true";
+ struct flb_parser_types *types = NULL;
+
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "256" /*= 0x100 */, "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ /* Note: types will be released by flb_parser_destroy */
+ types = flb_malloc(sizeof(struct flb_parser_types));
+ if (!TEST_CHECK(types != NULL)) {
+ TEST_MSG("flb_malloc failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+ types->key = flb_malloc(strlen("int")+1);
+ if (!TEST_CHECK(types->key != NULL)) {
+ TEST_MSG("flb_malloc failed");
+ flb_free(types);
+ flb_config_exit(config);
+ exit(1);
+ }
+ strcpy(types->key, "int");
+ types->key_len = 3;
+ types->type = FLB_PARSER_TYPE_HEX;
+
+ parser = flb_parser_create("logfmt", "logfmt", NULL, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ types, 1, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_free(types->key);
+ flb_free(types);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+TEST_LIST = {
+ { "basic", test_basic},
+ { "time_key", test_time_key},
+ { "time_keep", test_time_keep},
+ { "types", test_types},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/parser_ltsv.c b/fluent-bit/tests/internal/parser_ltsv.c
new file mode 100644
index 00000000..3f917051
--- /dev/null
+++ b/fluent-bit/tests/internal/parser_ltsv.c
@@ -0,0 +1,486 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_config.h>
+#include <fluent-bit/flb_config_format.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_parser_decoder.h>
+#include <msgpack.h>
+#include <float.h>
+#include <math.h>
+#include "flb_tests_internal.h"
+
+static int msgpack_strncmp(char* str, size_t str_len, msgpack_object obj)
+{
+ int ret = -1;
+
+ if (str == NULL) {
+ flb_error("str is NULL");
+ return -1;
+ }
+
+ switch (obj.type) {
+ case MSGPACK_OBJECT_STR:
+ if (obj.via.str.size != str_len) {
+ return -1;
+ }
+ ret = strncmp(str, obj.via.str.ptr, str_len);
+ break;
+ case MSGPACK_OBJECT_POSITIVE_INTEGER:
+ {
+ unsigned long val = strtoul(str, NULL, 10);
+ if (val == (unsigned long)obj.via.u64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_NEGATIVE_INTEGER:
+ {
+ long long val = strtoll(str, NULL, 10);
+ if (val == (unsigned long)obj.via.i64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_FLOAT32:
+ case MSGPACK_OBJECT_FLOAT64:
+ {
+ double val = strtod(str, NULL);
+ if (fabs(val - obj.via.f64) < DBL_EPSILON) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_BOOLEAN:
+ if (obj.via.boolean) {
+ if (str_len != 4 /*true*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "true", 4);
+ }
+ else {
+ if (str_len != 5 /*false*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "false", 5);
+ }
+ break;
+ default:
+ flb_error("not supported");
+ }
+
+ return ret;
+}
+
+struct str_list {
+ size_t size;
+ char **lists;
+};
+
+static int compare_msgpack(void *msgpack_data, size_t msgpack_size, struct str_list *l)
+{
+ msgpack_unpacked result;
+ msgpack_object obj;
+ size_t off = 0;
+ int map_size;
+ int i_map;
+ int i_list;
+ int num = 0;
+
+ if (!TEST_CHECK(msgpack_data != NULL)) {
+ TEST_MSG("msgpack_data is NULL");
+ return -1;
+ }
+ else if (!TEST_CHECK(msgpack_size > 0)) {
+ TEST_MSG("msgpack_size is 0");
+ return -1;
+ }
+
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, msgpack_data, msgpack_size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ obj = result.data;
+ /*
+ msgpack_object_print(stdout, obj);
+ */
+ if (!TEST_CHECK(obj.type == MSGPACK_OBJECT_MAP)) {
+ TEST_MSG("map error. type = %d", obj.type);
+ continue;
+ }
+ map_size = obj.via.map.size;
+ for (i_map=0; i_map<map_size; i_map++) {
+ if (!TEST_CHECK(obj.via.map.ptr[i_map].key.type == MSGPACK_OBJECT_STR)) {
+ TEST_MSG("key is not string. type =%d", obj.via.map.ptr[i_map].key.type);
+ continue;
+ }
+ for (i_list=0; i_list< l->size/2; i_list++) {
+ if (msgpack_strncmp(l->lists[i_list*2], strlen(l->lists[i_list*2]),
+ obj.via.map.ptr[i_map].key) == 0 &&
+ msgpack_strncmp(l->lists[i_list*2+1], strlen(l->lists[i_list*2+1]),
+ obj.via.map.ptr[i_map].val) == 0) {
+ num++;
+ }
+ }
+ }
+ }
+ msgpack_unpacked_destroy(&result);
+ if (!TEST_CHECK(num == l->size/2)) {
+ msgpack_object_print(stdout, obj);
+ putchar('\n');
+ TEST_MSG("compare failed. matched_num=%d expect=%lu", num, l->size/2);
+ return -1;
+ }
+ return 0;
+}
+
+void test_basic()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "str:text\tint:100\tdouble:1.23\tbool:true";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("ltsv", "ltsv", NULL, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_time_key()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "str:text\tint:100\tdouble:1.23\tbool:true\ttime:2022-10-31T12:00:01.123";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ out_time.tm.tv_sec = 0;
+ out_time.tm.tv_nsec = 0;
+
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("ltsv", "ltsv", NULL, FLB_FALSE, "%Y-%m-%dT%H:%M:%S.%L", "time", NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ if (!TEST_CHECK(out_time.tm.tv_sec == 1667217601 && out_time.tm.tv_nsec == 123000000)) {
+ TEST_MSG("timestamp error. sec Got=%ld Expect=1667217601", out_time.tm.tv_sec);
+ TEST_MSG("timestamp error. nsec Got=%ld Expect=123000000", out_time.tm.tv_nsec);
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_time_keep()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "str:text\tint:100\tdouble:1.23\tbool:true\ttime:2022-10-31T12:00:01.123";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true", "time", "2022-10-31T12:00:01.123"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ out_time.tm.tv_sec = 0;
+ out_time.tm.tv_nsec = 0;
+
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("ltsv", "ltsv", NULL, FLB_FALSE, "%Y-%m-%dT%H:%M:%S.%L", "time", NULL,
+ FLB_TRUE /*time_keep */, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ if (!TEST_CHECK(out_time.tm.tv_sec == 1667217601 && out_time.tm.tv_nsec == 123000000)) {
+ TEST_MSG("timestamp error. sec Got=%ld Expect=1667217601", out_time.tm.tv_sec);
+ TEST_MSG("timestamp error. nsec Got=%ld Expect=123000000", out_time.tm.tv_nsec);
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_types()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "str:text\tint:100\tdouble:1.23\tbool:true";
+ struct flb_parser_types *types = NULL;
+
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "256" /*= 0x100 */, "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ /* Note: types will be released by flb_parser_destroy */
+ types = flb_malloc(sizeof(struct flb_parser_types));
+ if (!TEST_CHECK(types != NULL)) {
+ TEST_MSG("flb_malloc failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+ types->key = flb_malloc(strlen("int")+1);
+ if (!TEST_CHECK(types->key != NULL)) {
+ TEST_MSG("flb_malloc failed");
+ flb_free(types);
+ flb_config_exit(config);
+ exit(1);
+ }
+ strcpy(types->key, "int");
+ types->key_len = 3;
+ types->type = FLB_PARSER_TYPE_HEX;
+
+ parser = flb_parser_create("ltsv", "ltsv", NULL, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ types, 1, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_free(types->key);
+ flb_free(types);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_decode_field_json()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ struct cfl_variant *var = NULL;
+ int ret = 0;
+ char *input = "json_str:{\"str\":\"text\", \"int\":100, \"double\":1.23, \"bool\":true}";
+ struct flb_cf *cf = NULL;
+ struct flb_cf_section *section = NULL;
+ struct mk_list *decoder = NULL;
+
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+ cf = flb_cf_create();
+ if (!TEST_CHECK(cf != NULL)) {
+ TEST_MSG("flb_cf_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ section = flb_cf_section_create(cf, "TEST", 4);
+ if (!TEST_CHECK(section != NULL)) {
+ TEST_MSG("flb_cf_section_create failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ var = flb_cf_section_property_add(cf, section->properties, "decode_field", 12, "json json_str", 13);
+ if(!TEST_CHECK(var != NULL)) {
+ TEST_MSG("flb_cf_section_property_add failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ decoder = flb_parser_decoder_list_create(section);
+ if (!TEST_CHECK(decoder != NULL)) {
+ TEST_MSG("flb_parser_decoder_list_create failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ parser = flb_parser_create("ltsv", "ltsv", NULL, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, decoder, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+}
+
+
+TEST_LIST = {
+ { "basic", test_basic},
+ { "time_key", test_time_key},
+ { "time_keep", test_time_keep},
+ { "types", test_types},
+ { "decode_field_json", test_decode_field_json},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/parser_regex.c b/fluent-bit/tests/internal/parser_regex.c
new file mode 100644
index 00000000..2283231c
--- /dev/null
+++ b/fluent-bit/tests/internal/parser_regex.c
@@ -0,0 +1,491 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_config.h>
+#include <fluent-bit/flb_config_format.h>
+#include <fluent-bit/flb_parser.h>
+#include <fluent-bit/flb_parser_decoder.h>
+#include <msgpack.h>
+#include <float.h>
+#include <math.h>
+#include "flb_tests_internal.h"
+
+static int msgpack_strncmp(char* str, size_t str_len, msgpack_object obj)
+{
+ int ret = -1;
+
+ if (str == NULL) {
+ flb_error("str is NULL");
+ return -1;
+ }
+
+ switch (obj.type) {
+ case MSGPACK_OBJECT_STR:
+ if (obj.via.str.size != str_len) {
+ return -1;
+ }
+ ret = strncmp(str, obj.via.str.ptr, str_len);
+ break;
+ case MSGPACK_OBJECT_POSITIVE_INTEGER:
+ {
+ unsigned long val = strtoul(str, NULL, 10);
+ if (val == (unsigned long)obj.via.u64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_NEGATIVE_INTEGER:
+ {
+ long long val = strtoll(str, NULL, 10);
+ if (val == (unsigned long)obj.via.i64) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_FLOAT32:
+ case MSGPACK_OBJECT_FLOAT64:
+ {
+ double val = strtod(str, NULL);
+ if (fabs(val - obj.via.f64) < DBL_EPSILON) {
+ ret = 0;
+ }
+ }
+ break;
+ case MSGPACK_OBJECT_BOOLEAN:
+ if (obj.via.boolean) {
+ if (str_len != 4 /*true*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "true", 4);
+ }
+ else {
+ if (str_len != 5 /*false*/) {
+ return -1;
+ }
+ ret = strncasecmp(str, "false", 5);
+ }
+ break;
+ default:
+ flb_error("not supported");
+ }
+
+ return ret;
+}
+
+struct str_list {
+ size_t size;
+ char **lists;
+};
+
+static int compare_msgpack(void *msgpack_data, size_t msgpack_size, struct str_list *l)
+{
+ msgpack_unpacked result;
+ msgpack_object obj;
+ size_t off = 0;
+ int map_size;
+ int i_map;
+ int i_list;
+ int num = 0;
+
+ if (!TEST_CHECK(msgpack_data != NULL)) {
+ TEST_MSG("msgpack_data is NULL");
+ return -1;
+ }
+ else if (!TEST_CHECK(msgpack_size > 0)) {
+ TEST_MSG("msgpack_size is 0");
+ return -1;
+ }
+
+ msgpack_unpacked_init(&result);
+ while (msgpack_unpack_next(&result, msgpack_data, msgpack_size, &off) == MSGPACK_UNPACK_SUCCESS) {
+ obj = result.data;
+ /*
+ msgpack_object_print(stdout, obj);
+ */
+ if (!TEST_CHECK(obj.type == MSGPACK_OBJECT_MAP)) {
+ TEST_MSG("map error. type = %d", obj.type);
+ continue;
+ }
+ map_size = obj.via.map.size;
+ for (i_map=0; i_map<map_size; i_map++) {
+ if (!TEST_CHECK(obj.via.map.ptr[i_map].key.type == MSGPACK_OBJECT_STR)) {
+ TEST_MSG("key is not string. type =%d", obj.via.map.ptr[i_map].key.type);
+ continue;
+ }
+ for (i_list=0; i_list< l->size/2; i_list++) {
+ if (msgpack_strncmp(l->lists[i_list*2], strlen(l->lists[i_list*2]),
+ obj.via.map.ptr[i_map].key) == 0 &&
+ msgpack_strncmp(l->lists[i_list*2+1], strlen(l->lists[i_list*2+1]),
+ obj.via.map.ptr[i_map].val) == 0) {
+ num++;
+ }
+ }
+ }
+ }
+ msgpack_unpacked_destroy(&result);
+ if (!TEST_CHECK(num == l->size/2)) {
+ msgpack_object_print(stdout, obj);
+ putchar('\n');
+ TEST_MSG("compare failed. matched_num=%d expect=%lu", num, l->size/2);
+ return -1;
+ }
+ return 0;
+}
+
+void test_basic()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "text 100 1.23 true";
+ char *regex = "(?<str>[a-z]+) (?<int>\\d+) (?<double>\\d+\\.\\d+) (?<bool>true)";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("regex", "regex", regex, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_time_key()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "text 100 1.23 true 2022-10-31T12:00:01.123";
+ char *regex = "(?<str>[a-z]+) (?<int>\\d+) (?<double>\\d+\\.\\d+) (?<bool>true) (?<time>.+)";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ out_time.tm.tv_sec = 0;
+ out_time.tm.tv_nsec = 0;
+
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("regex", "regex", regex, FLB_FALSE, "%Y-%m-%dT%H:%M:%S.%L", "time", NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ if (!TEST_CHECK(out_time.tm.tv_sec == 1667217601 && out_time.tm.tv_nsec == 123000000)) {
+ TEST_MSG("timestamp error. sec Got=%ld Expect=1667217601", out_time.tm.tv_sec);
+ TEST_MSG("timestamp error. nsec Got=%ld Expect=123000000", out_time.tm.tv_nsec);
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_time_keep()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "text 100 1.23 true 2022-10-31T12:00:01.123";
+ char *regex = "(?<str>[a-z]+) (?<int>\\d+) (?<double>\\d+\\.\\d+) (?<bool>true) (?<time>.+)";
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true", "time", "2022-10-31T12:00:01.123"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ out_time.tm.tv_sec = 0;
+ out_time.tm.tv_nsec = 0;
+
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ parser = flb_parser_create("regex", "regex", regex, FLB_FALSE, "%Y-%m-%dT%H:%M:%S.%L", "time", NULL,
+ FLB_TRUE /*time_keep */, FLB_FALSE, FLB_FALSE,
+ NULL, 0, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ if (!TEST_CHECK(out_time.tm.tv_sec == 1667217601 && out_time.tm.tv_nsec == 123000000)) {
+ TEST_MSG("timestamp error. sec Got=%ld Expect=1667217601", out_time.tm.tv_sec);
+ TEST_MSG("timestamp error. nsec Got=%ld Expect=123000000", out_time.tm.tv_nsec);
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_types()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ int ret = 0;
+ char *input = "text 100 1.23 true";
+ char *regex = "(?<str>[a-z]+) (?<int>\\d+) (?<double>\\d+\\.\\d+) (?<bool>true)";
+ struct flb_parser_types *types = NULL;
+
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "256" /*= 0x100 */, "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+
+ /* Note: types will be released by flb_parser_destroy */
+ types = flb_malloc(sizeof(struct flb_parser_types));
+ if (!TEST_CHECK(types != NULL)) {
+ TEST_MSG("flb_malloc failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+ types->key = flb_malloc(strlen("int")+1);
+ if (!TEST_CHECK(types->key != NULL)) {
+ TEST_MSG("flb_malloc failed");
+ flb_free(types);
+ flb_config_exit(config);
+ exit(1);
+ }
+ strcpy(types->key, "int");
+ types->key_len = 3;
+ types->type = FLB_PARSER_TYPE_HEX;
+
+ parser = flb_parser_create("regex", "regex", regex, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ types, 1, NULL, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_free(types->key);
+ flb_free(types);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_config_exit(config);
+}
+
+void test_decode_field_json()
+{
+ struct flb_parser *parser = NULL;
+ struct flb_config *config = NULL;
+ struct cfl_variant *var = NULL;
+ int ret = 0;
+ char *input = "{\"str\":\"text\", \"int\":100, \"double\":1.23, \"bool\":true}";
+ char *regex = "(?<json_str>.+)";
+ struct flb_cf *cf = NULL;
+ struct flb_cf_section *section = NULL;
+ struct mk_list *decoder = NULL;
+
+ void *out_buf = NULL;
+ size_t out_size = 0;
+ struct flb_time out_time;
+ char *expected_strs[] = {"str", "text", "int", "100", "double","1.23", "bool", "true"};
+ struct str_list expected = {
+ .size = sizeof(expected_strs)/sizeof(char*),
+ .lists = &expected_strs[0],
+ };
+
+ config = flb_config_init();
+ if(!TEST_CHECK(config != NULL)) {
+ TEST_MSG("flb_config_init failed");
+ exit(1);
+ }
+ cf = flb_cf_create();
+ if (!TEST_CHECK(cf != NULL)) {
+ TEST_MSG("flb_cf_create failed");
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ section = flb_cf_section_create(cf, "TEST", 4);
+ if (!TEST_CHECK(section != NULL)) {
+ TEST_MSG("flb_cf_section_create failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ var = flb_cf_section_property_add(cf, section->properties, "decode_field", 12, "json json_str", 13);
+ if(!TEST_CHECK(var != NULL)) {
+ TEST_MSG("flb_cf_section_property_add failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ decoder = flb_parser_decoder_list_create(section);
+ if (!TEST_CHECK(decoder != NULL)) {
+ TEST_MSG("flb_parser_decoder_list_create failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ parser = flb_parser_create("regex", "regex", regex, FLB_FALSE, NULL, NULL, NULL,
+ FLB_FALSE, FLB_FALSE, FLB_FALSE,
+ NULL, 0, decoder, config);
+ if (!TEST_CHECK(parser != NULL)) {
+ TEST_MSG("flb_parser_create failed");
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = flb_parser_do(parser, input, strlen(input), &out_buf, &out_size, &out_time);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_parser_do failed");
+ flb_parser_destroy(parser);
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+ exit(1);
+ }
+
+ ret = compare_msgpack(out_buf, out_size, &expected);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("compare failed");
+ }
+
+ flb_free(out_buf);
+ flb_parser_destroy(parser);
+ flb_cf_destroy(cf);
+ flb_config_exit(config);
+}
+
+
+TEST_LIST = {
+ { "basic", test_basic},
+ { "time_key", test_time_key},
+ { "time_keep", test_time_keep},
+ { "types", test_types},
+ { "decode_field_json", test_decode_field_json},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/pipe.c b/fluent-bit/tests/internal/pipe.c
new file mode 100644
index 00000000..f8a08e0d
--- /dev/null
+++ b/fluent-bit/tests/internal/pipe.c
@@ -0,0 +1,89 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_pipe.h>
+#ifdef FLB_SYSTEM_WINDOWS
+#include <fluent-bit/flb_compat.h>
+#endif
+
+#include <inttypes.h>
+#include "flb_tests_internal.h"
+
+struct data {
+ int x;
+ char str[32];
+};
+
+static void test_pipe_usage()
+{
+ int i;
+ int ret;
+ char *b;
+ ssize_t bytes;
+ flb_pipefd_t p[2];
+ struct data data;
+ struct data rec;
+#ifdef FLB_SYSTEM_WINDOWS
+ WSADATA wsa_data;
+#endif
+
+#ifdef FLB_SYSTEM_WINDOWS
+ WSAStartup(0x0201, &wsa_data);
+#endif
+ /* Create pipe */
+ ret = flb_pipe_create(p);
+ TEST_CHECK(ret == 0);
+
+ /* Prepare test */
+ memset(&data, '\0', sizeof(struct data));
+ data.x = 2017;
+ memcpy(data.str, "this is a test\n", 15);
+ data.str[15] = '\0';
+
+ /* Simple write/read */
+ bytes = flb_pipe_w(p[1], &data, sizeof(struct data));
+ TEST_CHECK(bytes > 0);
+
+ bytes = flb_pipe_r(p[0], &rec, sizeof(struct data));
+ TEST_CHECK(bytes > 0);
+ TEST_CHECK(rec.x == 2017);
+ TEST_CHECK(strlen(rec.str) == 15);
+
+ /* Iterate write, all_read */
+ b = (char *) &data;
+ for (i = 0; i < sizeof(struct data); i++, b++) {
+ bytes = flb_pipe_w(p[1], b, 1);
+ TEST_CHECK(bytes == 1);
+ }
+
+ b = (char *) &rec;
+ memset(&rec, '\0', sizeof(struct data));
+ for (i = 0; i < sizeof(struct data); i++, b++) {
+ bytes = flb_pipe_read_all(p[0], b, 1);
+ TEST_CHECK(bytes == 1);
+ }
+ TEST_CHECK(rec.x == 2017);
+ TEST_CHECK(strlen(rec.str) == 15);
+
+ /* All write, all read */
+ bytes = flb_pipe_write_all(p[1], &data, sizeof(struct data));
+ TEST_CHECK(bytes == sizeof(struct data));
+
+ memset(&rec, '\0', sizeof(struct data));
+ bytes = flb_pipe_read_all(p[0], &rec, sizeof(struct data));
+ TEST_CHECK(bytes == sizeof(struct data));
+ TEST_CHECK(rec.x == 2017);
+ TEST_CHECK(strlen(rec.str) == 15);
+
+ /* Close pipe channels */
+ flb_pipe_close(p[0]);
+ flb_pipe_close(p[1]);
+#ifdef FLB_SYSTEM_WINDOWS
+ WSACleanup();
+#endif
+}
+
+TEST_LIST = {
+ { "pipe_usage", test_pipe_usage},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/processor.c b/fluent-bit/tests/internal/processor.c
new file mode 100644
index 00000000..679fd131
--- /dev/null
+++ b/fluent-bit/tests/internal/processor.c
@@ -0,0 +1,125 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2023 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_lib.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_processor.h>
+//#include <msgpack.h>
+
+#include "flb_tests_internal.h"
+
+#define APACHE_10K FLB_TESTS_DATA_PATH "/data/mp/apache_10k.mp"
+
+static int create_msgpack_records(char **out_buf, size_t *out_size)
+{
+ int ret;
+ int root_type;
+ char *json = "{\"key1\": 12345, \"key2\": \"fluent bit\"}";
+ char *mp_tmp;
+ size_t mp_size;
+
+ msgpack_sbuffer mp_sbuf;
+ msgpack_packer mp_pck;
+
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_array(&mp_pck, 2);
+ flb_pack_time_now(&mp_pck);
+
+ ret = flb_pack_json(json, strlen(json), &mp_tmp, &mp_size, &root_type, NULL);
+ TEST_CHECK(ret == 0);
+
+ msgpack_sbuffer_write(&mp_sbuf, mp_tmp, mp_size);
+ flb_free(mp_tmp);
+
+ *out_buf = mp_sbuf.data;
+ *out_size = mp_sbuf.size;
+
+ return 0;
+}
+
+static void processor()
+{
+ int ret;
+ struct flb_processor *proc;
+ struct flb_processor_unit *pu;
+ struct flb_config *config;
+ char *mp_buf;
+ size_t mp_size;
+ void *out_buf = NULL;
+ size_t out_size;
+ flb_sds_t hostname_prop_key;
+ struct cfl_variant var = {
+ .type = CFL_VARIANT_STRING,
+ .data.as_string = NULL,
+ };
+
+ printf("\n\n");
+
+ flb_init_env();
+
+ hostname_prop_key = flb_sds_create("hostname monox");
+ TEST_CHECK(hostname_prop_key != NULL);
+ var.data.as_string = hostname_prop_key;
+
+ config = flb_config_init();
+ TEST_CHECK(config != NULL);
+
+ proc = flb_processor_create(config, "unit_test", NULL, 0);
+ TEST_CHECK(proc != NULL);
+
+ pu = flb_processor_unit_create(proc, FLB_PROCESSOR_LOGS, "stdout");
+ TEST_CHECK(pu != NULL);
+
+ pu = flb_processor_unit_create(proc, FLB_PROCESSOR_LOGS, "modify");
+ TEST_CHECK(pu != NULL);
+
+ ret = flb_processor_unit_set_property(pu, "add", &var);
+ TEST_CHECK(ret == 0);
+
+ pu = flb_processor_unit_create(proc, FLB_PROCESSOR_LOGS, "stdout");
+ TEST_CHECK(pu != NULL);
+
+ ret = flb_processor_init(proc);
+ TEST_CHECK(ret == 0);
+
+ /* generate records (simulate an input plugin */
+ ret = create_msgpack_records(&mp_buf, &mp_size);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_processor_run(proc, 0, FLB_PROCESSOR_LOGS, "TEST", 4, mp_buf, mp_size, &out_buf, &out_size);
+
+ if (out_buf != mp_buf) {
+ flb_free(out_buf);
+ }
+ flb_free(mp_buf);
+
+ flb_processor_destroy(proc);
+ flb_config_exit(config);
+
+ flb_sds_destroy(hostname_prop_key);
+}
+
+TEST_LIST = {
+ { "processor", processor },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/random.c b/fluent-bit/tests/internal/random.c
new file mode 100644
index 00000000..236eaa57
--- /dev/null
+++ b/fluent-bit/tests/internal/random.c
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_random.h>
+
+#include "flb_tests_internal.h"
+
+void test_random_bytes()
+{
+ int ret;
+ unsigned char buf1[64] = {0};
+ unsigned char buf2[64] = {0};
+
+ /* The following tests check whether:
+ *
+ * (1) the random generator fills the buffer with numbers at all.
+ * (2) a successive call generates different numbers.
+ *
+ * These tests are probabilistic by nature; If we assume an ideal random
+ * generator, they are expected to fail once in 2^192 (= 10^57) runs.
+ */
+ ret = flb_random_bytes(buf1, 64);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(memcmp(buf1, buf2, 64) != 0);
+
+ ret = flb_random_bytes(buf2, 64);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(memcmp(buf1, buf2, 64) != 0);
+}
+
+TEST_LIST = {
+ {"random_bytes", test_random_bytes},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/record_accessor.c b/fluent-bit/tests/internal/record_accessor.c
new file mode 100644
index 00000000..5f62b846
--- /dev/null
+++ b/fluent-bit/tests/internal/record_accessor.c
@@ -0,0 +1,1731 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2020 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_error.h>
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_sds_list.h>
+#include <fluent-bit/flb_record_accessor.h>
+#include <fluent-bit/record_accessor/flb_ra_parser.h>
+#include <msgpack.h>
+
+#include "flb_tests_internal.h"
+
+#include <stdlib.h>
+
+static int create_map(char *input_json, msgpack_object *out_map,
+ char **out_buf, msgpack_unpacked *out_result)
+{
+ int len;
+ int ret;
+ size_t out_size;
+ int type;
+ size_t off = 0;
+
+ if (input_json == NULL || out_map == NULL) {
+ return -1;
+ }
+ len = strlen(input_json);
+ ret = flb_pack_json(input_json, len, out_buf, &out_size, &type, NULL);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("can't convert. input=%s", input_json);
+ exit(EXIT_FAILURE);
+ }
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(out_result);
+ msgpack_unpack_next(out_result, *out_buf, out_size, &off);
+ *out_map = out_result->data;
+
+ return 0;
+}
+
+static int set_str_to_msgpack_object(char *str, msgpack_object *obj)
+{
+ if (str == NULL || obj == NULL) {
+ return -1;
+ }
+ /* create value object to overwrite */
+ obj->type = MSGPACK_OBJECT_STR;
+ obj->via.str.size = strlen(str);
+ obj->via.str.ptr = str;
+ return 0;
+}
+
+
+void cb_keys()
+{
+ struct flb_record_accessor *ra;
+
+ printf("\n=== test ===");
+ ra = flb_ra_create("$aaa['a'] extra $bbb['b'] final access", FLB_TRUE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+ TEST_CHECK(mk_list_size(&ra->list) == 4);
+ flb_ra_dump(ra);
+ flb_ra_destroy(ra);
+
+ printf("\n=== test ===");
+ ra = flb_ra_create("$b['x']['y']", FLB_TRUE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+ TEST_CHECK(mk_list_size(&ra->list) == 1);
+ flb_ra_dump(ra);
+ flb_ra_destroy(ra);
+
+ printf("\n=== test ===");
+ ra = flb_ra_create("$z", FLB_TRUE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+ TEST_CHECK(mk_list_size(&ra->list) == 1);
+ flb_ra_dump(ra);
+ flb_ra_destroy(ra);
+
+ printf("\n=== test ===");
+ ra = flb_ra_create("abc", FLB_TRUE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+ TEST_CHECK(mk_list_size(&ra->list) == 1);
+ flb_ra_dump(ra);
+ flb_ra_destroy(ra);
+
+ ra = flb_ra_create("$abc['a'", FLB_TRUE);
+ TEST_CHECK(ra == NULL);
+
+ ra = flb_ra_create("", FLB_TRUE);
+ flb_ra_destroy(ra);
+}
+
+void cb_translate()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char *fmt;
+ char *fmt_out;
+ flb_sds_t str;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_record_accessor *ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"k1\": \"string\", \"k2\": true, \"k3\": false," \
+ " \"k4\": 0.123456789, \"k5\": 123456789," \
+ " \"k6\": {\"s1\": {\"s2\": \"nested\"}}}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Set environment variables */
+ putenv("FLB_ENV=translated");
+
+ /* Formatter */
+ fmt = \
+ "START k1 => \"$k1\", k2 => $k2 (bool), k3 => $k3 (bool), " \
+ "k4 => $k4 (float), k5 => $k5 (int)," \
+ "k6 => $k6['s1']['s2'] (nested), k8 => $k8 (nothing), ${FLB_ENV} END";
+
+ fmt_out = \
+ "START k1 => \"string\", k2 => true (bool), " \
+ "k3 => false (bool), k4 => 0.123457 (float), " \
+ "k5 => 123456789 (int),k6 => nested (nested), " \
+ "k8 => (nothing), translated END";
+
+ ra = flb_ra_create(fmt, FLB_TRUE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation */
+ str = flb_ra_translate(ra, NULL, -1, map, NULL);
+ TEST_CHECK(str != NULL);
+ if (!str) {
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(flb_sds_len(str) == strlen(fmt_out));
+ TEST_CHECK(memcmp(str, fmt_out, strlen(fmt_out)) == 0);
+ printf("== input ==\n%s\n== output ==\n%s\n", str, fmt_out);
+
+ flb_sds_destroy(str);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void cb_translate_tag()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char *fmt;
+ flb_sds_t str;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_record_accessor *ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"k1\": \"string\", \"k2\": true, \"k3\": false," \
+ " \"k4\": 0.123456789, \"k5\": 123456789," \
+ " \"k6\": {\"s1\": {\"s2\": \"nested\"}}}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ fmt = "$TAG";
+ ra = flb_ra_create(fmt, FLB_TRUE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation */
+ str = flb_ra_translate(ra, "testapp", 7, map, NULL);
+ TEST_CHECK(str != NULL);
+ if (!str) {
+ exit(EXIT_FAILURE);
+ }
+ TEST_CHECK(flb_sds_len(str) == 7);
+
+ flb_sds_destroy(str);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void cb_dots_subkeys()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char *fmt;
+ char *fmt_out;
+ flb_sds_t str;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_record_accessor *ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", \"kubernetes\": {\"annotations\": "
+ "{\"fluentbit.io/tag\": \"thetag\"}}}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$kubernetes['annotations']['fluentbit.io/tag']");
+ fmt_out = "thetag";
+
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation */
+ str = flb_ra_translate(ra, NULL, -1, map, NULL);
+ TEST_CHECK(str != NULL);
+ if (!str) {
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(flb_sds_len(str) == strlen(fmt_out));
+ TEST_CHECK(memcmp(str, fmt_out, strlen(fmt_out)) == 0);
+ printf("== input ==\n%s\n== output ==\n%s\n", str, fmt_out);
+
+ flb_sds_destroy(str);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void cb_array_id()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char *fmt;
+ char *fmt_out;
+ flb_sds_t str;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_record_accessor *ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\""
+ "}}]}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$kubernetes[2]['annotations']['fluentbit.io/tag']");
+ fmt_out = "thetag";
+
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation */
+ str = flb_ra_translate(ra, NULL, -1, map, NULL);
+ TEST_CHECK(str != NULL);
+ if (!str) {
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(flb_sds_len(str) == strlen(fmt_out));
+ TEST_CHECK(memcmp(str, fmt_out, strlen(fmt_out)) == 0);
+ printf("== input ==\n%s\n== output ==\n%s\n", str, fmt_out);
+
+ flb_sds_destroy(str);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void cb_get_kv_pair()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char *fmt;
+ char *fmt_out;
+ msgpack_object *start_key;
+ msgpack_object *out_key;
+ msgpack_object *out_val;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_record_accessor *ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\""
+ "}}]}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$kubernetes[2]['annotations']['fluentbit.io/tag']");
+ fmt_out = "thetag";
+
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation */
+ ret = flb_ra_get_kv_pair(ra, map, &start_key, &out_key, &out_val);
+ TEST_CHECK(ret == 0);
+ if (ret != 0) {
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(out_val->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_val->via.str.size == strlen(fmt_out));
+ TEST_CHECK(memcmp(out_val->via.str.ptr, fmt_out, strlen(fmt_out)) == 0);
+
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void cb_update_key_val()
+{
+ int ret;
+ size_t off = 0;
+ char *json;
+ flb_sds_t fmt;
+ flb_sds_t updated_fmt;
+ char *fmt_out_key = "updated_key";
+ char *fmt_out_val = "updated_val";
+
+ char *out_buf = NULL;
+ size_t out_size = 0;
+
+ msgpack_unpacked result;
+ msgpack_unpacked out_result;
+
+ msgpack_object map;
+ msgpack_object *start_key = NULL;
+ msgpack_object *out_key = NULL;
+ msgpack_object *out_val = NULL;
+ void *updated_map;
+ msgpack_object in_key;
+ msgpack_object in_val;
+
+ struct flb_record_accessor *ra;
+ struct flb_record_accessor *updated_ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\""
+ "}}]}";
+ ret = create_map(json, &map, &out_buf, &result);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed create map");
+ if (out_buf != NULL) {
+ flb_free(out_buf);
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$kubernetes[2]['annotations']['fluentbit.io/tag']");
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ if(!TEST_CHECK(ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ updated_fmt = flb_sds_create("$kubernetes[2]['annotations']['updated_key']");
+ updated_ra = flb_ra_create(updated_fmt, FLB_FALSE);
+ if(!TEST_CHECK(updated_ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* create key object to overwrite */
+ ret = set_str_to_msgpack_object(fmt_out_key, &in_key);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to set object");
+ exit(EXIT_FAILURE);
+ }
+ /* create value object to overwrite */
+ ret = set_str_to_msgpack_object(fmt_out_val, &in_val);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to set object");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Update only value */
+ ret = flb_ra_update_kv_pair(ra, map, (void**)&updated_map, &out_size, &in_key, &in_val);
+ TEST_CHECK(ret == 0);
+ off = 0;
+ msgpack_unpacked_init(&out_result);
+ if (msgpack_unpack_next(&out_result, updated_map, out_size, &off)
+ != MSGPACK_UNPACK_SUCCESS) {
+ TEST_MSG("failed to unpack");
+ exit(EXIT_FAILURE);
+ }
+ ret = flb_ra_get_kv_pair(updated_ra, out_result.data, &start_key, &out_key, &out_val);
+ if (!TEST_CHECK(ret == 0)) {
+ printf("print out_result\n");
+ msgpack_object_print(stdout, out_result.data);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check updated key */
+ TEST_CHECK(out_key->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_key->via.str.size == strlen(fmt_out_key));
+ TEST_CHECK(memcmp(out_key->via.str.ptr, fmt_out_key, strlen(fmt_out_key)) == 0);
+
+ /* Check updated val */
+ TEST_CHECK(out_val->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_val->via.str.size == strlen(fmt_out_val));
+ TEST_CHECK(memcmp(out_val->via.str.ptr, fmt_out_val, strlen(fmt_out_val)) == 0);
+
+ msgpack_unpacked_destroy(&out_result);
+ msgpack_unpacked_destroy(&result);
+ flb_free(updated_map);
+ flb_sds_destroy(updated_fmt);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(updated_ra);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+}
+
+void cb_update_val()
+{
+ int ret;
+ size_t off = 0;
+ char *json;
+ flb_sds_t fmt;
+ char *fmt_out = "updated";
+ char *out_buf = NULL;
+ size_t out_size = 0;
+
+ msgpack_unpacked result;
+ msgpack_unpacked out_result;
+
+ msgpack_object map;
+ msgpack_object *start_key = NULL;
+ msgpack_object *out_key = NULL;
+ msgpack_object *out_val = NULL;
+ void *updated_map;
+ msgpack_object in_val;
+
+ struct flb_record_accessor *ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\""
+ "}}]}";
+ ret = create_map(json, &map, &out_buf, &result);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed create map");
+ if (out_buf != NULL) {
+ flb_free(out_buf);
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$kubernetes[2]['annotations']['fluentbit.io/tag']");
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ if(!TEST_CHECK(ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* create value object to overwrite */
+ ret = set_str_to_msgpack_object(fmt_out, &in_val);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to set object");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Update only value */
+ ret = flb_ra_update_kv_pair(ra, map, (void**)&updated_map, &out_size, NULL, &in_val);
+ TEST_CHECK(ret == 0);
+ off = 0;
+ msgpack_unpacked_init(&out_result);
+ if (msgpack_unpack_next(&out_result, updated_map, out_size, &off)
+ != MSGPACK_UNPACK_SUCCESS) {
+ TEST_MSG("failed to unpack");
+ exit(EXIT_FAILURE);
+ }
+ ret = flb_ra_get_kv_pair(ra, out_result.data, &start_key, &out_key, &out_val);
+ if (!TEST_CHECK(ret == 0)) {
+ printf("print out_result\n");
+ msgpack_object_print(stdout, out_result.data);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check updated val */
+ TEST_CHECK(out_val->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_val->via.str.size == strlen(fmt_out));
+ TEST_CHECK(memcmp(out_val->via.str.ptr, fmt_out, strlen(fmt_out)) == 0);
+
+ msgpack_unpacked_destroy(&out_result);
+ msgpack_unpacked_destroy(&result);
+ flb_free(updated_map);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+}
+
+void cb_update_key()
+{
+ int ret;
+ size_t off = 0;
+ char *json;
+ flb_sds_t fmt;
+ flb_sds_t updated_fmt;
+ char *fmt_out = "updated_key";
+
+
+ char *out_buf = NULL;
+ size_t out_size = 0;
+
+ msgpack_unpacked result;
+ msgpack_unpacked out_result;
+
+ msgpack_object map;
+ msgpack_object *start_key = NULL;
+ msgpack_object *out_key = NULL;
+ msgpack_object *out_val = NULL;
+ void *updated_map;
+ msgpack_object in_key;
+
+ struct flb_record_accessor *ra;
+ struct flb_record_accessor *updated_ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\""
+ "}}]}";
+ ret = create_map(json, &map, &out_buf, &result);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed create map");
+ if (out_buf != NULL) {
+ flb_free(out_buf);
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$kubernetes[2]['annotations']['fluentbit.io/tag']");
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ if(!TEST_CHECK(ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ updated_fmt = flb_sds_create("$kubernetes[2]['annotations']['updated_key']");
+ updated_ra = flb_ra_create(updated_fmt, FLB_FALSE);
+ if(!TEST_CHECK(updated_ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* create key object to overwrite */
+ ret = set_str_to_msgpack_object(fmt_out, &in_key);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to set object");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Update only value */
+ ret = flb_ra_update_kv_pair(ra, map, (void**)&updated_map, &out_size, &in_key, NULL);
+ TEST_CHECK(ret == 0);
+ off = 0;
+ msgpack_unpacked_init(&out_result);
+ if (msgpack_unpack_next(&out_result, updated_map, out_size, &off)
+ != MSGPACK_UNPACK_SUCCESS) {
+ TEST_MSG("failed to unpack");
+ exit(EXIT_FAILURE);
+ }
+ ret = flb_ra_get_kv_pair(updated_ra, out_result.data, &start_key, &out_key, &out_val);
+ if (!TEST_CHECK(ret == 0)) {
+ printf("print out_result\n");
+ msgpack_object_print(stdout, out_result.data);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check updated key */
+ TEST_CHECK(out_key->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_key->via.str.size == strlen(fmt_out));
+ TEST_CHECK(memcmp(out_key->via.str.ptr, fmt_out, strlen(fmt_out)) == 0);
+
+ msgpack_unpacked_destroy(&out_result);
+ msgpack_unpacked_destroy(&result);
+ flb_free(updated_map);
+ flb_sds_destroy(updated_fmt);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(updated_ra);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+}
+
+void cb_dash_key()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char *fmt;
+ char *fmt_out;
+ flb_sds_t str;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_record_accessor *ra;
+
+ /* Sample JSON message */
+ json = "{\"key-dash\": \"something\"}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$key-dash");
+ fmt_out = "something";
+
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation */
+ str = flb_ra_translate(ra, NULL, -1, map, NULL);
+ TEST_CHECK(str != NULL);
+ if (!str) {
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(flb_sds_len(str) == strlen(fmt_out));
+ TEST_CHECK(memcmp(str, fmt_out, strlen(fmt_out)) == 0);
+ printf("== input ==\n%s\n== output ==\n%s\n", str, fmt_out);
+
+ flb_sds_destroy(str);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void cb_dot_and_slash_key()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char *fmt;
+ char *fmt_out;
+ flb_sds_t str;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_record_accessor *ra;
+
+ /* Sample JSON message */
+ json = "{\"root.with/symbols\": \"something\"}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$root.with/symbols");
+ if (!TEST_CHECK(fmt != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ fmt_out = "something";
+
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation */
+ str = flb_ra_translate(ra, NULL, -1, map, NULL);
+ TEST_CHECK(str != NULL);
+ if (!str) {
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(flb_sds_len(str) == strlen(fmt_out));
+ TEST_CHECK(memcmp(str, fmt_out, strlen(fmt_out)) == 0);
+ printf("== input ==\n%s\n== output ==\n%s\n", str, fmt_out);
+
+ flb_sds_destroy(str);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+ msgpack_unpacked_destroy(&result);
+}
+
+static int order_lookup_check(char *buf, size_t size,
+ char *fmt, char *expected_out)
+{
+ size_t off = 0;
+ char *fmt_out;
+ flb_sds_t str;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_record_accessor *ra;
+
+ /* Check bool is 'true' */
+ fmt = flb_sds_create(fmt);
+ if (!TEST_CHECK(fmt != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+ fmt_out = expected_out;
+
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, buf, size, &off);
+ map = result.data;
+
+ /* Do translation */
+ str = flb_ra_translate(ra, NULL, -1, map, NULL);
+ TEST_CHECK(str != NULL);
+ if (!str) {
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(flb_sds_len(str) == strlen(expected_out));
+ if (flb_sds_len(str) != strlen(expected_out)) {
+ printf("received: '%s', expected: '%s'\n", str, fmt_out);
+ }
+
+ TEST_CHECK(memcmp(str, expected_out, strlen(expected_out)) == 0);
+
+ flb_sds_destroy(str);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(ra);
+ msgpack_unpacked_destroy(&result);
+
+ return 0;
+}
+
+void cb_key_order_lookup()
+{
+ int len;
+ int ret;
+ int type;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+
+ /* Sample JSON message */
+ json = "{\"key\": \"abc\", \"bool\": false, \"bool\": true, "
+ "\"str\": \"bad\", \"str\": \"good\", "
+ "\"num\": 0, \"num\": 1}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ printf("\n-- record --\n");
+ flb_pack_print(out_buf, out_size);
+
+ /* check expected outputs per record accessor pattern */
+ order_lookup_check(out_buf, out_size, "$bool", "true");
+ order_lookup_check(out_buf, out_size, "$str" , "good");
+ order_lookup_check(out_buf, out_size, "$num" , "1");
+
+ flb_free(out_buf);
+}
+
+void cb_issue_4917()
+{
+ int len;
+ int ret;
+ int type;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char *fmt_out;
+ size_t off = 0;
+ flb_sds_t fmt;
+ flb_sds_t str;
+ struct flb_record_accessor *ra;
+ msgpack_unpacked result;
+ msgpack_object map;
+
+ fmt_out = "from.new.fluent.bit.out";
+
+ /* Sample JSON message */
+ json = "{\"tool\": \"fluent\", \"sub\": {\"s1\": {\"s2\": \"bit\"}}}";
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ printf("\n-- record --\n");
+ flb_pack_print(out_buf, out_size);
+
+ /* Formatter */
+ fmt = flb_sds_create("from.new.$tool.$sub['s1']['s2'].out");
+ if (!TEST_CHECK(fmt != NULL)) {
+ flb_free(out_buf);
+ exit(EXIT_FAILURE);
+ }
+
+ /* create ra */
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ flb_sds_destroy(fmt);
+ flb_free(out_buf);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation */
+ str = flb_ra_translate(ra, NULL, -1, map, NULL);
+ TEST_CHECK(str != NULL);
+ if (!str) {
+ flb_ra_destroy(ra);
+ msgpack_unpacked_destroy(&result);
+ flb_sds_destroy(fmt);
+ flb_free(out_buf);
+ exit(EXIT_FAILURE);
+ }
+ TEST_CHECK(flb_sds_len(str) == strlen(fmt_out));
+ TEST_CHECK(memcmp(str, fmt_out, strlen(fmt_out)) == 0);
+ printf("== input ==\n%s\n== output ==\n%s\n", str, fmt_out);
+
+ flb_sds_destroy(fmt);
+ flb_sds_destroy(str);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void cb_update_root_key()
+{
+ int ret;
+ size_t off = 0;
+ char *json;
+ flb_sds_t fmt;
+ flb_sds_t updated_fmt;
+ char *fmt_out = "updated_key";
+
+
+ char *out_buf = NULL;
+ size_t out_size = 0;
+
+ msgpack_unpacked result;
+ msgpack_unpacked out_result;
+
+ msgpack_object map;
+ msgpack_object *start_key = NULL;
+ msgpack_object *out_key = NULL;
+ msgpack_object *out_val = NULL;
+ void *updated_map;
+ msgpack_object in_key;
+
+ struct flb_record_accessor *ra;
+ struct flb_record_accessor *updated_ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\""
+ "}}]}";
+ ret = create_map(json, &map, &out_buf, &result);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed create map");
+ if (out_buf != NULL) {
+ flb_free(out_buf);
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$key1");
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ if(!TEST_CHECK(ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ updated_fmt = flb_sds_create("$updated_key");
+ updated_ra = flb_ra_create(updated_fmt, FLB_FALSE);
+ if(!TEST_CHECK(updated_ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* create key object to overwrite */
+ ret = set_str_to_msgpack_object(fmt_out, &in_key);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to set object");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Update only value */
+ ret = flb_ra_update_kv_pair(ra, map, (void**)&updated_map, &out_size, &in_key, NULL);
+ TEST_CHECK(ret == 0);
+ off = 0;
+ msgpack_unpacked_init(&out_result);
+ if (msgpack_unpack_next(&out_result, updated_map, out_size, &off)
+ != MSGPACK_UNPACK_SUCCESS) {
+ TEST_MSG("failed to unpack");
+ exit(EXIT_FAILURE);
+ }
+ ret = flb_ra_get_kv_pair(updated_ra, out_result.data, &start_key, &out_key, &out_val);
+ if (!TEST_CHECK(ret == 0)) {
+ printf("print out_result\n");
+ msgpack_object_print(stdout, out_result.data);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check updated key */
+ TEST_CHECK(out_key->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_key->via.str.size == strlen(fmt_out));
+ TEST_CHECK(memcmp(out_key->via.str.ptr, fmt_out, strlen(fmt_out)) == 0);
+
+ msgpack_unpacked_destroy(&out_result);
+ msgpack_unpacked_destroy(&result);
+ flb_free(updated_map);
+ flb_sds_destroy(updated_fmt);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(updated_ra);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+}
+
+void cb_update_root_key_val()
+{
+ int ret;
+ size_t off = 0;
+ char *json;
+ flb_sds_t fmt;
+ flb_sds_t updated_fmt;
+ char *fmt_out_key = "updated_key";
+ char *fmt_out_val = "updated_val";
+
+ char *out_buf = NULL;
+ size_t out_size = 0;
+
+ msgpack_unpacked result;
+ msgpack_unpacked out_result;
+
+ msgpack_object map;
+ msgpack_object *start_key = NULL;
+ msgpack_object *out_key = NULL;
+ msgpack_object *out_val = NULL;
+ void *updated_map;
+ msgpack_object in_key;
+ msgpack_object in_val;
+
+ struct flb_record_accessor *ra;
+ struct flb_record_accessor *updated_ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\""
+ "}}]}";
+ ret = create_map(json, &map, &out_buf, &result);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed create map");
+ if (out_buf != NULL) {
+ flb_free(out_buf);
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$key1");
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ if(!TEST_CHECK(ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ updated_fmt = flb_sds_create("$updated_key");
+ updated_ra = flb_ra_create(updated_fmt, FLB_FALSE);
+ if(!TEST_CHECK(updated_ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* create key object to overwrite */
+ ret = set_str_to_msgpack_object(fmt_out_key, &in_key);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to set object");
+ exit(EXIT_FAILURE);
+ }
+ /* create value object to overwrite */
+ ret = set_str_to_msgpack_object(fmt_out_val, &in_val);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to set object");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Update only value */
+ ret = flb_ra_update_kv_pair(ra, map, (void**)&updated_map, &out_size, &in_key, &in_val);
+ TEST_CHECK(ret == 0);
+ off = 0;
+ msgpack_unpacked_init(&out_result);
+ if (msgpack_unpack_next(&out_result, updated_map, out_size, &off)
+ != MSGPACK_UNPACK_SUCCESS) {
+ TEST_MSG("failed to unpack");
+ exit(EXIT_FAILURE);
+ }
+ ret = flb_ra_get_kv_pair(updated_ra, out_result.data, &start_key, &out_key, &out_val);
+ if (!TEST_CHECK(ret == 0)) {
+ printf("print out_result\n");
+ msgpack_object_print(stdout, out_result.data);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check updated key */
+ TEST_CHECK(out_key->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_key->via.str.size == strlen(fmt_out_key));
+ TEST_CHECK(memcmp(out_key->via.str.ptr, fmt_out_key, strlen(fmt_out_key)) == 0);
+
+ /* Check updated val */
+ TEST_CHECK(out_val->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_val->via.str.size == strlen(fmt_out_val));
+ TEST_CHECK(memcmp(out_val->via.str.ptr, fmt_out_val, strlen(fmt_out_val)) == 0);
+
+ msgpack_unpacked_destroy(&out_result);
+ msgpack_unpacked_destroy(&result);
+ flb_free(updated_map);
+ flb_sds_destroy(updated_fmt);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(updated_ra);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+}
+
+void cb_add_key_val()
+{
+ int ret;
+ size_t off = 0;
+ char *json;
+ flb_sds_t fmt;
+ flb_sds_t updated_fmt;
+ char *fmt_out_key = "add_key";
+ char *fmt_out_val = "add_val";
+
+ char *out_buf = NULL;
+ size_t out_size = 0;
+
+ msgpack_unpacked result;
+ msgpack_unpacked out_result;
+
+ msgpack_object map;
+ msgpack_object *start_key = NULL;
+ msgpack_object *out_key = NULL;
+ msgpack_object *out_val = NULL;
+ void *updated_map;
+ msgpack_object in_val;
+
+ struct flb_record_accessor *ra;
+ struct flb_record_accessor *updated_ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\""
+ "}}]}";
+ ret = create_map(json, &map, &out_buf, &result);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed create map");
+ if (out_buf != NULL) {
+ flb_free(out_buf);
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$kubernetes[2]['annotations']['add_key']");
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ if(!TEST_CHECK(ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ updated_fmt = flb_sds_create("$kubernetes[2]['annotations']['add_key']");
+ updated_ra = flb_ra_create(updated_fmt, FLB_FALSE);
+ if(!TEST_CHECK(updated_ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* create value object to overwrite */
+ ret = set_str_to_msgpack_object(fmt_out_val, &in_val);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to set object");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Add key/value */
+ ret = flb_ra_append_kv_pair(ra, map, (void**)&updated_map, &out_size, &in_val);
+ TEST_CHECK(ret == 0);
+
+ off = 0;
+ msgpack_unpacked_init(&out_result);
+ ret = msgpack_unpack_next(&out_result, updated_map, out_size, &off);
+ if (!TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS)) {
+ TEST_MSG("failed to unpack");
+ exit(EXIT_FAILURE);
+ }
+ ret = flb_ra_get_kv_pair(updated_ra, out_result.data, &start_key, &out_key, &out_val);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("print out_result\n");
+ msgpack_object_print(stdout, out_result.data);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check updated key */
+ TEST_CHECK(out_key->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_key->via.str.size == strlen(fmt_out_key));
+ TEST_CHECK(memcmp(out_key->via.str.ptr, fmt_out_key, strlen(fmt_out_key)) == 0);
+
+ /* Check updated val */
+ TEST_CHECK(out_val->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_val->via.str.size == strlen(fmt_out_val));
+ TEST_CHECK(memcmp(out_val->via.str.ptr, fmt_out_val, strlen(fmt_out_val)) == 0);
+
+ msgpack_unpacked_destroy(&out_result);
+ msgpack_unpacked_destroy(&result);
+ flb_free(updated_map);
+ flb_sds_destroy(updated_fmt);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(updated_ra);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+}
+
+void cb_add_root_key_val()
+{
+ int ret;
+ size_t off = 0;
+ char *json;
+ flb_sds_t fmt;
+ flb_sds_t updated_fmt;
+ char *fmt_out_key = "add_key";
+ char *fmt_out_val = "add_val";
+
+ char *out_buf = NULL;
+ size_t out_size = 0;
+
+ msgpack_unpacked result;
+ msgpack_unpacked out_result;
+
+ msgpack_object map;
+ msgpack_object *start_key = NULL;
+ msgpack_object *out_key = NULL;
+ msgpack_object *out_val = NULL;
+ void *updated_map;
+ msgpack_object in_val;
+
+ struct flb_record_accessor *ra;
+ struct flb_record_accessor *updated_ra;
+
+ /* Sample JSON message */
+ json =
+ "{\"key1\": \"something\", "
+ "\"kubernetes\": "
+ " [true, "
+ " false, "
+ " {\"a\": false, "
+ " \"annotations\": { "
+ " \"fluentbit.io/tag\": \"thetag\""
+ "}}]}";
+ ret = create_map(json, &map, &out_buf, &result);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed create map");
+ if (out_buf != NULL) {
+ flb_free(out_buf);
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$add_key");
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ if(!TEST_CHECK(ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ updated_fmt = flb_sds_create("$add_key");
+ updated_ra = flb_ra_create(updated_fmt, FLB_FALSE);
+ if(!TEST_CHECK(updated_ra != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* create value object to overwrite */
+ ret = set_str_to_msgpack_object(fmt_out_val, &in_val);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to set object");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Add key/value */
+ ret = flb_ra_append_kv_pair(ra, map, (void**)&updated_map, &out_size, &in_val);
+ TEST_CHECK(ret == 0);
+
+ off = 0;
+ msgpack_unpacked_init(&out_result);
+ ret = msgpack_unpack_next(&out_result, updated_map, out_size, &off);
+ if (!TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS)) {
+ TEST_MSG("failed to unpack");
+ exit(EXIT_FAILURE);
+ }
+ ret = flb_ra_get_kv_pair(updated_ra, out_result.data, &start_key, &out_key, &out_val);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("print out_result\n");
+ msgpack_object_print(stdout, out_result.data);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Check updated key */
+ TEST_CHECK(out_key->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_key->via.str.size == strlen(fmt_out_key));
+ TEST_CHECK(memcmp(out_key->via.str.ptr, fmt_out_key, strlen(fmt_out_key)) == 0);
+
+ /* Check updated val */
+ TEST_CHECK(out_val->type == MSGPACK_OBJECT_STR);
+ TEST_CHECK(out_val->via.str.size == strlen(fmt_out_val));
+ TEST_CHECK(memcmp(out_val->via.str.ptr, fmt_out_val, strlen(fmt_out_val)) == 0);
+
+ msgpack_unpacked_destroy(&out_result);
+ msgpack_unpacked_destroy(&result);
+ flb_free(updated_map);
+ flb_sds_destroy(updated_fmt);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(updated_ra);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+}
+
+void cb_ra_translate_check()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char *fmt;
+ flb_sds_t str;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_record_accessor *ra;
+ int check_translation = FLB_TRUE;
+
+ /* Sample JSON message */
+ json = "{\"root.with/symbols\": \"something\"}";
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$root");
+ if (!TEST_CHECK(fmt != NULL)) {
+ exit(EXIT_FAILURE);
+ }
+
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation - with check enabled */
+ str = flb_ra_translate_check(ra, NULL, -1, map, NULL, check_translation);
+ /* since translation fails and check is enabled, it returns NULL */
+ TEST_CHECK(str == NULL);
+ if (str) {
+ exit(EXIT_FAILURE);
+ }
+
+ flb_sds_destroy(str);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+ msgpack_unpacked_destroy(&result);
+}
+
+/*
+ * https://github.com/fluent/fluent-bit/issues/5936
+ * If the last nested element is an array, record accessor can't get its value.
+ */
+void cb_issue_5936_last_array()
+{
+ int len;
+ int ret;
+ int type;
+ size_t off = 0;
+ char *out_buf;
+ size_t out_size;
+ char *json;
+ char *fmt;
+ char *fmt_out;
+ flb_sds_t str;
+ msgpack_unpacked result;
+ msgpack_object map;
+ struct flb_record_accessor *ra;
+
+ /* Sample JSON message */
+ json ="{ \"key\": {\"nested\":[\"val0\", \"val1\"]}}";
+
+
+ /* Convert to msgpack */
+ len = strlen(json);
+ ret = flb_pack_json(json, len, &out_buf, &out_size, &type, NULL);
+ TEST_CHECK(ret == 0);
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Formatter */
+ fmt = flb_sds_create("$key['nested'][1]");
+ fmt_out = "val1";
+
+ ra = flb_ra_create(fmt, FLB_FALSE);
+ TEST_CHECK(ra != NULL);
+ if (!ra) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation */
+ str = flb_ra_translate(ra, NULL, -1, map, NULL);
+ TEST_CHECK(str != NULL);
+ if (!str) {
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(flb_sds_len(str) == strlen(fmt_out));
+ TEST_CHECK(memcmp(str, fmt_out, strlen(fmt_out)) == 0);
+ printf("== input ==\n%s\n== output ==\n%s\n", str, fmt_out);
+
+ flb_sds_destroy(str);
+ flb_sds_destroy(fmt);
+ flb_ra_destroy(ra);
+ flb_free(out_buf);
+ msgpack_unpacked_destroy(&result);
+}
+
+struct char_list_ra_str{
+ char **strs;
+ char *expect;
+};
+
+void cb_ra_create_str_from_list()
+{
+ char *case1[] = {"a", NULL};
+ char *case2[] = {"aa", "bb", "cc", NULL};
+
+ struct char_list_ra_str testcases[] = {
+ { .strs = &case1[0], .expect = "$a"},
+ { .strs = &case2[0], .expect = "$aa['bb']['cc']"},
+ };
+ size_t case_size = sizeof(testcases)/sizeof(struct char_list_ra_str);
+ int case_i;
+ struct flb_sds_list *list = NULL;
+ flb_sds_t ret_str;
+ char *str;
+ int i;
+ int ret;
+
+ for (case_i = 0; case_i < case_size; case_i++) {
+ list = flb_sds_list_create();
+ if (!TEST_CHECK(list != NULL)) {
+ TEST_MSG("%d: flb_sds_list_create failed", case_i);
+ exit(EXIT_FAILURE);
+ }
+ i = 0;
+ while(testcases[case_i].strs[i] != NULL) {
+ str = testcases[case_i].strs[i];
+ ret = flb_sds_list_add(list, str, strlen(str));
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("%d: flb_sds_list_add failed", case_i);
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+ i++;
+ }
+
+ ret_str = flb_ra_create_str_from_list(list);
+ if (!TEST_CHECK(ret_str != NULL)) {
+ TEST_MSG("%d: flb_ra_create_str_from failed", case_i);
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+ if (!TEST_CHECK(strcmp(testcases[case_i].expect, ret_str) == 0)) {
+ TEST_MSG("%d: strcmp error.got=%s expect=%s", case_i, ret_str, testcases[case_i].expect);
+ }
+
+ flb_sds_destroy(ret_str);
+ flb_sds_list_destroy(list);
+ }
+
+
+ /* Error if we pass empty list */
+ list = flb_sds_list_create();
+ if (!TEST_CHECK(list != NULL)) {
+ TEST_MSG("flb_sds_list_create failed");
+ exit(EXIT_FAILURE);
+ }
+ ret_str = flb_ra_create_str_from_list(list);
+ if (!TEST_CHECK(ret_str == NULL)) {
+ TEST_MSG("flb_ra_create_str_from should be failed");
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+ flb_sds_list_destroy(list);
+}
+
+/*
+ * https://github.com/fluent/fluent-bit/issues/7330
+ */
+void cb_issue_7330_single_char()
+{
+ int ret;
+ int type;
+ char *json;
+ char *out_buf = NULL;
+ size_t out_size;
+ size_t off = 0;
+ flb_sds_t input = NULL;
+ flb_sds_t out_tag = NULL;
+ struct flb_regex_search regex_result;
+ struct flb_record_accessor *ra_tag = NULL;
+ msgpack_unpacked result;
+ msgpack_object map;
+
+ json = "{\"tool\":\"fluent\"}";
+ ret = flb_pack_json(json, strlen(json), &out_buf, &out_size, &type, NULL);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_pack_json failed");
+ exit(EXIT_FAILURE);
+ }
+
+ input = flb_sds_create("b");
+ if (!TEST_CHECK(input != NULL)) {
+ goto issue_7330;
+ }
+
+ /* create flb_record_accessor from single character */
+ ra_tag = flb_ra_create(input, FLB_FALSE);
+ if (!TEST_CHECK(ra_tag != NULL)) {
+ TEST_MSG("flb_ra_create failed");
+ goto issue_7330;
+ }
+
+ /* Unpack msgpack object */
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, out_buf, out_size, &off);
+ map = result.data;
+
+ /* Do translation */
+ out_tag = flb_ra_translate(ra_tag, "old", 3, map, &regex_result);
+ msgpack_unpacked_destroy(&result);
+ if (!TEST_CHECK(out_tag != NULL)) {
+ TEST_MSG("flb_ra_translate failed");
+ goto issue_7330;
+ }
+ else if (!TEST_CHECK(flb_sds_len(out_tag) > 0)) {
+ TEST_MSG("out_tag len error. len=%zd", flb_sds_len(out_tag));
+ goto issue_7330;
+ }
+
+ issue_7330:
+ if (input) {
+ flb_sds_destroy(input);
+ }
+ if (out_tag) {
+ flb_sds_destroy(out_tag);
+ }
+ if (out_buf) {
+ flb_free(out_buf);
+ }
+ if (ra_tag) {
+ flb_ra_destroy(ra_tag);
+ }
+}
+
+TEST_LIST = {
+ { "keys" , cb_keys},
+ { "dash_key" , cb_dash_key},
+ /*
+ * If #4370 is fixed, this testcase should be enabled.
+ { "dot_slash_key" , cb_dot_and_slash_key},
+ */
+ { "translate" , cb_translate},
+ { "translate_tag" , cb_translate_tag},
+ { "dots_subkeys" , cb_dots_subkeys},
+ { "array_id" , cb_array_id},
+ { "get_kv_pair" , cb_get_kv_pair},
+ { "key_order_lookup", cb_key_order_lookup},
+ { "update_key_val", cb_update_key_val},
+ { "update_key", cb_update_key},
+ { "update_val", cb_update_val},
+ { "update_root_key", cb_update_root_key},
+ { "update_root_key_val", cb_update_root_key_val},
+ { "add_key_val", cb_add_key_val},
+ { "add_root_key_val", cb_add_root_key_val},
+ { "issue_4917" , cb_issue_4917},
+ { "flb_ra_translate_check" , cb_ra_translate_check},
+ { "issue_5936_last_array" , cb_issue_5936_last_array},
+ { "ra_create_str_from_list", cb_ra_create_str_from_list},
+ { "issue_7330_single_character" , cb_issue_7330_single_char},
+ { NULL }
+};
diff --git a/fluent-bit/tests/internal/regex.c b/fluent-bit/tests/internal/regex.c
new file mode 100644
index 00000000..77045a38
--- /dev/null
+++ b/fluent-bit/tests/internal/regex.c
@@ -0,0 +1,330 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2022 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <fluent-bit/flb_regex.h>
+#include "flb_tests_internal.h"
+
+struct kv_list {
+ int index;
+ size_t size;
+ char **lists;
+};
+static void cb_kv(const char *name, const char *value,
+ size_t vlen, void *data)
+{
+ size_t len;
+ struct kv_list *l = (struct kv_list*)data;
+
+ if (!TEST_CHECK(name != NULL && value != NULL && data != NULL)) {
+ TEST_MSG("input is NULL");
+ return;
+ }
+ len = strlen(name);
+
+ if (!TEST_CHECK(len == strlen(l->lists[l->index * 2]))) {
+ TEST_MSG("name: lenght error. got:%zu expect:%zu", len, strlen(l->lists[l->index * 2]));
+ TEST_MSG("name: got:%s expect:%s", name, l->lists[l->index * 2]);
+ goto cb_kv_end;
+ }
+ if (!TEST_CHECK(strncmp(name, l->lists[l->index * 2], len) == 0)) {
+ TEST_MSG("name: mismatch. got:%s expect:%s", name, l->lists[l->index * 2]);
+ goto cb_kv_end;
+ }
+
+ if (!TEST_CHECK(vlen == strlen(l->lists[l->index * 2+1]))) {
+ TEST_MSG("value: lenght error. got:%zu expect:%zu", vlen, strlen(l->lists[l->index * 2+1]));
+ TEST_MSG("value: got:%s expect:%s", value, l->lists[l->index * 2+1]);
+ goto cb_kv_end;
+ }
+ if (!TEST_CHECK(strncmp(value, l->lists[l->index * 2+1], vlen) == 0)) {
+ TEST_MSG("value: mismatch. got:%s expect:%s", value, l->lists[l->index * 2+1]);
+ goto cb_kv_end;
+ }
+ cb_kv_end:
+ l->index++;
+}
+
+static void test_basic()
+{
+ struct flb_regex *regex = NULL;
+ struct flb_regex_search result;
+ int ret;
+ ssize_t size;
+ const char *input = "string 1234 2022/10/24";
+
+ char *expected_strs[] = {"str","string", "num","1234", "time","2022/10/24"};
+ struct kv_list expected = {
+ .index = 0,
+ .size = sizeof(expected_strs)/sizeof(char *),
+ .lists = &expected_strs[0],
+ };
+
+ regex = flb_regex_create("/(?<str>[a-z]+) (?<num>\\d+) (?<time>\\d{4}/\\d{2}/\\d{2})/");
+ if (!TEST_CHECK(regex != NULL)) {
+ TEST_MSG("flb_regex_create failed");
+ exit(1);
+ }
+
+ size = flb_regex_do(regex, input, strlen(input), &result);
+ if (!TEST_CHECK(size >= 0)) {
+ TEST_MSG("flb_regex_do failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_parse(regex, &result, cb_kv, &expected);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_regex_parse failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_destroy(regex);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_regex_destroy failed");
+ exit(1);
+ }
+}
+
+/* test uri to check if misunderstanding "/pattern/option" */
+static void test_uri()
+{
+ struct flb_regex *regex = NULL;
+ struct flb_regex_search result;
+ int ret;
+ ssize_t size;
+ const char *input = "/uri/is/hoge";
+
+ char *expected_strs[] = {"middle","is"};
+ struct kv_list expected = {
+ .index = 0,
+ .size = sizeof(expected_strs)/sizeof(char *),
+ .lists = &expected_strs[0],
+ };
+
+ regex = flb_regex_create("/uri/(?<middle>[a-z]+)/hoge");
+ if (!TEST_CHECK(regex != NULL)) {
+ TEST_MSG("flb_regex_create failed");
+ exit(1);
+ }
+
+ size = flb_regex_do(regex, input, strlen(input), &result);
+ if (!TEST_CHECK(size >= 0)) {
+ TEST_MSG("flb_regex_do failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_parse(regex, &result, cb_kv, &expected);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_regex_parse failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_destroy(regex);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_regex_destroy failed");
+ exit(1);
+ }
+}
+
+/* test "/pattern/i" */
+static void test_option_ignore_case()
+{
+ struct flb_regex *regex = NULL;
+ struct flb_regex_search result;
+ int ret;
+ ssize_t size;
+ const char *input = "STRING";
+
+ char *expected_strs[] = {"str","STRING"};
+ struct kv_list expected = {
+ .index = 0,
+ .size = sizeof(expected_strs)/sizeof(char *),
+ .lists = &expected_strs[0],
+ };
+
+ regex = flb_regex_create("/(?<str>[a-z]+)/i");
+ if (!TEST_CHECK(regex != NULL)) {
+ TEST_MSG("flb_regex_create failed");
+ exit(1);
+ }
+
+ size = flb_regex_do(regex, input, strlen(input), &result);
+ if (!TEST_CHECK(size >= 0)) {
+ TEST_MSG("flb_regex_do failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_parse(regex, &result, cb_kv, &expected);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_regex_parse failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_destroy(regex);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_regex_destroy failed");
+ exit(1);
+ }
+}
+
+/* test "/pattern/m" */
+static void test_option_multiline()
+{
+ struct flb_regex *regex = NULL;
+ struct flb_regex_search result;
+ int ret;
+ ssize_t size;
+ const char *input = "string\n1234\nstring";
+
+ char *expected_strs[] = {"full_str","string\n1234\nstring"};
+ struct kv_list expected = {
+ .index = 0,
+ .size = sizeof(expected_strs)/sizeof(char *),
+ .lists = &expected_strs[0],
+ };
+
+ regex = flb_regex_create("/(?<full_str>.+)/m");
+ if (!TEST_CHECK(regex != NULL)) {
+ TEST_MSG("flb_regex_create failed");
+ exit(1);
+ }
+
+ size = flb_regex_do(regex, input, strlen(input), &result);
+ if (!TEST_CHECK(size >= 0)) {
+ TEST_MSG("flb_regex_do failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_parse(regex, &result, cb_kv, &expected);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_regex_parse failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_destroy(regex);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_regex_destroy failed");
+ exit(1);
+ }
+}
+
+/* test "/pattern/x" */
+static void test_option_extend()
+{
+ struct flb_regex *regex = NULL;
+ struct flb_regex_search result;
+ int ret;
+ ssize_t size;
+ const char *input = "3.14";
+
+ char *expected_strs[] = {"pi","3.14"};
+ struct kv_list expected = {
+ .index = 0,
+ .size = sizeof(expected_strs)/sizeof(char *),
+ .lists = &expected_strs[0],
+ };
+
+ regex = flb_regex_create("/(?<pi>\\d \\. 14)/x");
+ if (!TEST_CHECK(regex != NULL)) {
+ TEST_MSG("flb_regex_create failed");
+ exit(1);
+ }
+
+ size = flb_regex_do(regex, input, strlen(input), &result);
+ if (!TEST_CHECK(size >= 0)) {
+ TEST_MSG("flb_regex_do failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_parse(regex, &result, cb_kv, &expected);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_regex_parse failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_destroy(regex);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_regex_destroy failed");
+ exit(1);
+ }
+}
+
+/* test "/pattern/ix" */
+static void test_option_i_x()
+{
+ struct flb_regex *regex = NULL;
+ struct flb_regex_search result;
+ int ret;
+ ssize_t size;
+ const char *input = "3.14pi";
+
+ char *expected_strs[] = {"full_str","3.14pi"};
+ struct kv_list expected = {
+ .index = 0,
+ .size = sizeof(expected_strs)/sizeof(char *),
+ .lists = &expected_strs[0],
+ };
+
+ regex = flb_regex_create("/(?<full_str>\\d \\. 14PI)/ix");
+ if (!TEST_CHECK(regex != NULL)) {
+ TEST_MSG("flb_regex_create failed");
+ exit(1);
+ }
+
+ size = flb_regex_do(regex, input, strlen(input), &result);
+ if (!TEST_CHECK(size >= 0)) {
+ TEST_MSG("flb_regex_do failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_parse(regex, &result, cb_kv, &expected);
+ if (!TEST_CHECK(ret != -1)) {
+ TEST_MSG("flb_regex_parse failed");
+ flb_regex_destroy(regex);
+ exit(1);
+ }
+
+ ret = flb_regex_destroy(regex);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_regex_destroy failed");
+ exit(1);
+ }
+}
+
+TEST_LIST = {
+ { "basic" , test_basic},
+ { "uri" , test_uri},
+ { "option_ignore_case", test_option_ignore_case},
+ { "option_multiline" , test_option_multiline},
+ { "option_extend" , test_option_extend},
+ { "option_i_x" , test_option_i_x},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/reload.c b/fluent-bit/tests/internal/reload.c
new file mode 100644
index 00000000..e6a695d5
--- /dev/null
+++ b/fluent-bit/tests/internal/reload.c
@@ -0,0 +1,249 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_kv.h>
+#include <fluent-bit/flb_config_format.h>
+#include <fluent-bit/flb_lib.h>
+#include <fluent-bit/flb_reload.h>
+
+#include <cfl/cfl.h>
+#include <cfl/cfl_variant.h>
+#include <cfl/cfl_kvlist.h>
+
+#include "flb_tests_internal.h"
+
+#define FLB_YAML FLB_TESTS_DATA_PATH "/data/reload/yaml/processor.yaml"
+#define FLB_CLASSIC FLB_TESTS_DATA_PATH "/data/reload/fluent-bit.conf"
+
+void test_reconstruct_cf()
+{
+ struct flb_cf *cf;
+ struct flb_cf_section *s_tmp;
+ struct flb_cf_section *service;
+ struct flb_cf_group *g_tmp;
+ struct cfl_variant *ret;
+ struct flb_kv *meta;
+ struct flb_cf *new_cf;
+ int status;
+
+
+ /* create context */
+ cf = flb_cf_create();
+ TEST_CHECK(cf != NULL);
+
+ /* create service section */
+ service = flb_cf_section_create(cf, "SERVICE", 7);
+ TEST_CHECK(service != NULL);
+
+ /* add a property */
+ ret = flb_cf_section_property_add(cf, service->properties, "key", 3, "val", 3);
+ TEST_CHECK(ret != NULL);
+
+ /* add a property with empty spaces on left/right */
+ ret = flb_cf_section_property_add(cf, service->properties, " key ", 5, " val ", 7);
+ TEST_CHECK(ret != NULL);
+
+ /* add an invalid property */
+ ret = flb_cf_section_property_add(cf, service->properties, " ", 3, "", 0);
+ TEST_CHECK(ret == NULL);
+
+ /* try to add another 'SERVICE' section, it should return the same one */
+ s_tmp = flb_cf_section_create(cf, "SERVICE", 7);
+ TEST_CHECK(s_tmp == service);
+
+ /* add a valid section */
+ s_tmp = flb_cf_section_create(cf, "INPUT", 5);
+ TEST_CHECK(s_tmp != NULL);
+
+ TEST_CHECK(mk_list_size(&cf->inputs) == 1);
+
+ /* add property to the section recently created */
+ ret = flb_cf_section_property_add(cf, s_tmp->properties, "key", 3, "val", 3);
+ TEST_CHECK(ret != NULL);
+
+ /* groups: add groups to the last section created */
+ g_tmp = flb_cf_group_create(cf, s_tmp, "FLUENT GROUP", 12);
+ TEST_CHECK(g_tmp != NULL);
+
+ /* add properties to the group */
+ ret = flb_cf_section_property_add(cf, g_tmp->properties, "key", 3, "val", 3);
+ TEST_CHECK(ret != NULL);
+
+ /* groups: invalid group */
+ g_tmp = flb_cf_group_create(cf, s_tmp, "", 0);
+ TEST_CHECK(g_tmp == NULL);
+
+ /* Meta commands */
+ meta = flb_cf_meta_property_add(cf, "@SET a=1 ", 20);
+
+ TEST_CHECK(meta != NULL);
+ TEST_CHECK(flb_sds_len(meta->key) == 3 && strcmp(meta->key, "SET") == 0);
+ TEST_CHECK(flb_sds_len(meta->val) == 3 && strcmp(meta->val, "a=1") == 0);
+
+ /* create new context */
+ new_cf = flb_cf_create();
+ TEST_CHECK(cf != NULL);
+
+ status = flb_reload_reconstruct_cf(cf, new_cf);
+ TEST_CHECK(status == 0);
+ TEST_CHECK(new_cf != NULL);
+
+ TEST_CHECK(mk_list_size(&new_cf->inputs) == 1);
+ TEST_CHECK(mk_list_size(&new_cf->sections) == 2);
+ TEST_CHECK(mk_list_size(&new_cf->metas) == 1);
+
+ printf("\n");
+ flb_cf_dump(new_cf);
+
+ /* destroy context */
+ flb_cf_destroy(cf);
+ flb_cf_destroy(new_cf);
+}
+
+/* data/reload/fluent-bit.conf */
+void test_reload()
+{
+ struct flb_cf *cf = NULL;
+ struct flb_cf *cf_opts;
+ struct flb_cf_section *section;
+ struct cfl_variant *ret;
+ flb_ctx_t *ctx;
+ int status;
+
+ /* create context */
+ cf_opts = flb_cf_create();
+ TEST_CHECK(cf_opts != NULL);
+
+ /* add a valid section (input) */
+ section = flb_cf_section_create(cf_opts, "INPUT", 5);
+ TEST_CHECK(section != NULL);
+
+ /* add property to the section recently created */
+ ret = flb_cf_section_property_add(cf_opts, section->properties, "name", 0, "dummy", 0);
+ TEST_CHECK(ret != NULL);
+
+ TEST_CHECK(mk_list_size(&cf_opts->inputs) == 1);
+
+ ctx = flb_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("flb_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ cf = ctx->config->cf_main;
+
+ status = flb_reload_reconstruct_cf(cf_opts, cf);
+ TEST_CHECK(status == 0);
+
+ /* Mimic operation like as service_configure() */
+ cf = flb_cf_create_from_file(cf, FLB_CLASSIC);
+ TEST_CHECK(cf != NULL);
+
+ ctx->config->conf_path_file = flb_sds_create(FLB_CLASSIC);
+ ctx->config->enable_hot_reload = FLB_TRUE;
+
+ status = flb_config_load_config_format(ctx->config, cf);
+ TEST_CHECK(status == 0);
+
+ /* Start the engine */
+ status = flb_start(ctx);
+ TEST_CHECK(status == 0);
+ TEST_CHECK(mk_list_size(&ctx->config->inputs) == 2);
+
+ sleep(2);
+
+ status = flb_reload(ctx, cf_opts);
+ TEST_CHECK(status == 0);
+
+ sleep(2);
+
+ /* flb context should be replaced with flb_reload() */
+ ctx = flb_context_get();
+
+ TEST_CHECK(mk_list_size(&ctx->config->cf_opts->inputs) == 1);
+ TEST_CHECK(mk_list_size(&ctx->config->inputs) == 2);
+
+ flb_cf_destroy(cf_opts);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+/* data/reload/yaml/processor.yaml */
+void test_reload_yaml()
+{
+ struct flb_cf *cf = NULL;
+ struct flb_cf *cf_opts;
+ struct flb_cf_section *section;
+ struct cfl_variant *ret;
+ flb_ctx_t *ctx;
+ int status;
+
+ /* create context */
+ cf_opts = flb_cf_create();
+ TEST_CHECK(cf_opts != NULL);
+
+ /* add a valid section (input) */
+ section = flb_cf_section_create(cf_opts, "INPUT", 5);
+ TEST_CHECK(section != NULL);
+
+ /* add property to the section recently created */
+ ret = flb_cf_section_property_add(cf_opts, section->properties, "name", 0, "dummy", 0);
+ TEST_CHECK(ret != NULL);
+
+ TEST_CHECK(mk_list_size(&cf_opts->inputs) == 1);
+
+ ctx = flb_create();
+ if (!TEST_CHECK(ctx != NULL)) {
+ TEST_MSG("flb_create failed");
+ exit(EXIT_FAILURE);
+ }
+
+ cf = ctx->config->cf_main;
+
+ status = flb_reload_reconstruct_cf(cf_opts, cf);
+ TEST_CHECK(status == 0);
+
+ /* Mimic operation like as service_configure() */
+ cf = flb_cf_create_from_file(cf, FLB_YAML);
+ TEST_CHECK(cf != NULL);
+
+ ctx->config->conf_path_file = flb_sds_create(FLB_YAML);
+ ctx->config->enable_hot_reload = FLB_TRUE;
+
+ status = flb_config_load_config_format(ctx->config, cf);
+ TEST_CHECK(status == 0);
+
+ /* Start the engine */
+ status = flb_start(ctx);
+ TEST_CHECK(status == 0);
+ TEST_CHECK(mk_list_size(&ctx->config->inputs) == 2);
+
+ sleep(2);
+
+ status = flb_reload(ctx, cf_opts);
+ TEST_CHECK(status == 0);
+
+ sleep(2);
+
+ /* flb context should be replaced with flb_reload() */
+ ctx = flb_context_get();
+
+ TEST_CHECK(mk_list_size(&ctx->config->cf_opts->inputs) == 1);
+ TEST_CHECK(mk_list_size(&ctx->config->inputs) == 2);
+ TEST_CHECK(mk_list_size(&ctx->config->filters) == 0);
+ TEST_CHECK(mk_list_size(&ctx->config->outputs) == 1);
+
+ flb_cf_destroy(cf_opts);
+
+ flb_stop(ctx);
+ flb_destroy(ctx);
+}
+
+TEST_LIST = {
+ { "reconstruct_cf" , test_reconstruct_cf},
+ { "reload" , test_reload},
+ { "reload_yaml" , test_reload_yaml},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/ring_buffer.c b/fluent-bit/tests/internal/ring_buffer.c
new file mode 100644
index 00000000..0c0e2074
--- /dev/null
+++ b/fluent-bit/tests/internal/ring_buffer.c
@@ -0,0 +1,168 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_engine.h>
+#include <fluent-bit/flb_ring_buffer.h>
+#include <fluent-bit/flb_event_loop.h>
+#include <fluent-bit/flb_bucket_queue.h>
+
+#include "flb_tests_internal.h"
+
+struct check {
+ char *buf_a;
+ char *buf_b;
+};
+
+struct check checks[] = {
+ {"a1", "a2"},
+ {"b1", "b2"},
+ {"c1", "c2"},
+ {"d1", "d2"},
+ {"e1", "e2"},
+};
+
+static void test_basic()
+{
+ int i;
+ int ret;
+ int elements;
+ struct check *c;
+ struct check *tmp;
+ struct flb_ring_buffer *rb;
+
+ elements = sizeof(checks) / sizeof(struct check);
+
+ rb = flb_ring_buffer_create(sizeof(struct check *) * elements);
+ TEST_CHECK(rb != NULL);
+ if (!rb) {
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < elements; i++) {
+ c = &checks[i];
+ ret = flb_ring_buffer_write(rb, (void *) &c, sizeof(c));
+ TEST_CHECK(ret == 0);
+ }
+
+ /* try to write another record, it must fail */
+ tmp = c;
+ ret = flb_ring_buffer_write(rb, (void *) &tmp, sizeof(tmp));
+ TEST_CHECK(ret == -1);
+
+ c = NULL;
+
+ /* consume one entry */
+ ret = flb_ring_buffer_read(rb, (void *) &c, sizeof(c));
+ TEST_CHECK(ret == 0);
+
+ /* the consumed entry must be equal to the first one */
+ c = &checks[0];
+ TEST_CHECK(strcmp(c->buf_a, "a1") == 0 && strcmp(c->buf_b, "a2") ==0);
+
+ /* try 'again' to write 'c2', it should succeed */
+ ret = flb_ring_buffer_write(rb, (void *) &tmp, sizeof(tmp));
+ TEST_CHECK(ret == 0);
+
+ flb_ring_buffer_destroy(rb);
+}
+
+static void test_smart_flush()
+{
+ int i;
+ int ret;
+ int n_events;
+ int elements;
+ size_t slots;
+ uint64_t window;
+ struct check *c;
+ struct check *tmp;
+ int flush_event_detected;
+ char signal_buffer[512];
+ struct mk_event *event;
+ struct mk_event_loop *evl;
+ struct flb_ring_buffer *rb;
+ struct flb_bucket_queue *bktq;
+
+#ifdef _WIN32
+ WSADATA wsa_data;
+ WSAStartup(0x0201, &wsa_data);
+#endif
+
+ evl = mk_event_loop_create(100);
+ TEST_CHECK(evl != NULL);
+ if (!evl) {
+ exit(EXIT_FAILURE);
+ }
+
+ bktq = flb_bucket_queue_create(10);
+ TEST_CHECK(bktq != NULL);
+ if (!bktq) {
+ exit(EXIT_FAILURE);
+ }
+
+ elements = sizeof(checks) / sizeof(struct check);
+ slots = elements * 2;
+ window = (((double) (elements + 1)) / slots) * 100;
+
+ /* The slot count was chosen to trigger the flush request
+ * after writing the predefined elements + 1
+ */
+
+ rb = flb_ring_buffer_create(sizeof(struct check *) * slots);
+ TEST_CHECK(rb != NULL);
+ if (!rb) {
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_ring_buffer_add_event_loop(rb, evl, window);
+ TEST_CHECK(ret == 0);
+ if (ret) {
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < elements; i++) {
+ c = &checks[i];
+ ret = flb_ring_buffer_write(rb, (void *) &c, sizeof(c));
+ TEST_CHECK(ret == 0);
+
+ n_events = mk_event_wait_2(evl, 0);
+ TEST_CHECK(n_events == 0);
+ }
+
+ /* write another record, a signal must be produced */
+ ret = flb_ring_buffer_write(rb, (void *) &tmp, sizeof(tmp));
+ TEST_CHECK(ret == 0);
+
+ n_events = mk_event_wait_2(evl, 0);
+ TEST_CHECK(n_events == 1);
+
+ flush_event_detected = FLB_FALSE;
+ flb_event_priority_live_foreach(event, bktq, evl, 10) {
+ if(event->type == FLB_ENGINE_EV_THREAD_INPUT) {
+ flb_pipe_r(event->fd, signal_buffer, sizeof(signal_buffer));
+
+ flush_event_detected = FLB_TRUE;
+ }
+ }
+
+ TEST_CHECK(flush_event_detected == FLB_TRUE);
+
+ /* write another record, a signal must not be produced because the previous one
+ * was not acknowledged by setting `flush_pending` to `FLB_FALSE`
+ */
+ ret = flb_ring_buffer_write(rb, (void *) &tmp, sizeof(tmp));
+ TEST_CHECK(ret == 0);
+
+ n_events = mk_event_wait_2(evl, 0);
+ TEST_CHECK(n_events == 0);
+
+ flb_ring_buffer_destroy(rb);
+ flb_bucket_queue_destroy(bktq);
+ mk_event_loop_destroy(evl);
+}
+TEST_LIST = {
+ { "basic", test_basic},
+ { "smart_flush", test_smart_flush},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/router.c b/fluent-bit/tests/internal/router.c
new file mode 100644
index 00000000..04960a25
--- /dev/null
+++ b/fluent-bit/tests/internal/router.c
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_router.h>
+
+#include "flb_tests_internal.h"
+
+struct check {
+ char *tag;
+ char *match;
+ int matched;
+};
+
+struct check route_checks[] = {
+
+ {"file.apache.log", "file.*.log" , FLB_TRUE},
+ {"cpu.rpi" , "cpu.rpi" , FLB_TRUE},
+ {"cpu.rpi" , "cpu.*" , FLB_TRUE},
+ {"cpu.rpi" , "*" , FLB_TRUE},
+ {"cpu.rpi" , "*.*" , FLB_TRUE},
+ {"cpu.rpi" , "*.rpi" , FLB_TRUE},
+ {"cpu.rpi" , "mem.*" , FLB_FALSE},
+ {"cpu.rpi" , "*u.r*" , FLB_TRUE},
+ {"hoge" , "hogeeeeeee" , FLB_FALSE},
+ {"test" , "test" , FLB_TRUE}
+};
+
+void test_router_wildcard()
+{
+ int i;
+ int ret;
+ int len;
+ int checks = 0;
+ struct check *c;
+
+ checks = sizeof(route_checks) / sizeof(struct check);
+ for (i = 0; i < checks; i++) {
+ c = &route_checks[i];
+ len = strlen(c->tag);
+ ret = flb_router_match(c->tag, len, c->match, NULL);
+ TEST_CHECK(ret == c->matched);
+ if (ret != c->matched) {
+ fprintf(stderr, "test %i failed: tag=%s match=%s expected_to_match=%s\n",
+ i, c->tag, c->match, c->matched ? "YES": "NO");
+ }
+ }
+
+ ret = flb_router_match("aaaX", 3, "aaa", NULL);
+ TEST_CHECK(ret == FLB_TRUE);
+}
+
+TEST_LIST = {
+ { "wildcard", test_router_wildcard},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/sds.c b/fluent-bit/tests/internal/sds.c
new file mode 100644
index 00000000..30dc166d
--- /dev/null
+++ b/fluent-bit/tests/internal/sds.c
@@ -0,0 +1,88 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_sds.h>
+
+#include "flb_tests_internal.h"
+
+static void test_sds_usage()
+{
+ flb_sds_t s;
+
+ s = flb_sds_create("test");
+ TEST_CHECK(s != NULL);
+ TEST_CHECK(flb_sds_len(s) == 4);
+ TEST_CHECK(flb_sds_alloc(s) == 4);
+ TEST_CHECK(strcmp("test", s) == 0);
+
+ s = flb_sds_cat(s, ",cat message", 12);
+ TEST_CHECK(strcmp("test,cat message", s) == 0);
+
+ flb_sds_destroy(s);
+}
+
+static void test_sds_printf()
+{
+ int len;
+ flb_sds_t s;
+ flb_sds_t tmp;
+ char *str = "0123456789ABCDEFGHIJQLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvewxyz";
+
+ s = flb_sds_create_size(10);
+ tmp = flb_sds_printf(&s, "%s=%s", str, str);
+
+ len = (strlen(str) * 2) + 1;
+ TEST_CHECK(tmp == s);
+ TEST_CHECK(flb_sds_len(s) == len);
+ flb_sds_destroy(s);
+}
+
+/* https://github.com/fluent/fluent-bit/issues/7143 */
+static void test_sds_printf_7143_off_by_1()
+{
+ flb_sds_t tmp;
+ flb_sds_t test;
+ flb_sds_t test2;
+ int len;
+
+ /* 66 char final string, not impacted by bug */
+ test = flb_sds_create_size(64);
+ TEST_CHECK(test != NULL);
+ tmp = flb_sds_printf(&test, "A0123456789 %s", "this-is-54-chars-1234567890-abcdefghijklmnopqrstuvwxyz");
+ TEST_CHECK(tmp != NULL);
+ len = flb_sds_len(test);
+ TEST_CHECK(len == 66);
+ TEST_CHECK(test[len -1] == 'z');
+ flb_sds_destroy(test);
+
+ /* 65 char final string, impacted by bug */
+ test2 = flb_sds_create_size(64);
+ TEST_CHECK(test2 != NULL);
+ tmp = flb_sds_printf(&test2, "0123456789 %s", "this-is-54-chars-1234567890-abcdefghijklmnopqrstuvwxyz");
+ TEST_CHECK(tmp != NULL);
+ len = flb_sds_len(test2);
+ TEST_CHECK(len == 65);
+ TEST_CHECK(test2[len -1] == 'z');
+ flb_sds_destroy(test2);
+
+}
+
+static void test_sds_cat_utf8()
+{
+ flb_sds_t s;
+ char *utf8_str = "\xe8\x9f\xb9\xf0\x9f\xa6\x80";
+
+ s = flb_sds_create("");
+ flb_sds_cat_utf8(&s, utf8_str, strlen(utf8_str));
+
+ TEST_CHECK(strcmp(s, "\\u87f9\\u1f980") == 0);
+ flb_sds_destroy(s);
+}
+
+TEST_LIST = {
+ { "sds_usage" , test_sds_usage},
+ { "sds_printf", test_sds_printf},
+ { "sds_cat_utf8", test_sds_cat_utf8},
+ { "test_sds_printf_7143_off_by_1", test_sds_printf_7143_off_by_1},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/sds_list.c b/fluent-bit/tests/internal/sds_list.c
new file mode 100644
index 00000000..63ac280c
--- /dev/null
+++ b/fluent-bit/tests/internal/sds_list.c
@@ -0,0 +1,238 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_sds.h>
+#include <fluent-bit/flb_sds_list.h>
+
+#include "flb_tests_internal.h"
+
+static void test_sds_list_create_destroy()
+{
+ struct flb_sds_list *list = NULL;
+ int ret = 0;
+
+ list = flb_sds_list_create();
+ if(!TEST_CHECK(list != NULL)) {
+ TEST_MSG("failed to allocate flb_sds_list");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_sds_list_destroy(list);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to destroy flb_sds_list");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void test_sds_list_add()
+{
+ struct flb_sds_list *list = NULL;
+ int ret = 0;
+
+ list = flb_sds_list_create();
+ if(!TEST_CHECK(list != NULL)) {
+ TEST_MSG("failed to allocate flb_sds_list");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_sds_list_add(list, "hoge", 4);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to flb_sds_list_add");
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_sds_list_destroy(list);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to destroy flb_sds_list");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void test_sds_list_array()
+{
+ struct flb_sds_list *list = NULL;
+ char *c_array[] = {
+ "hoge", "moge", "aaa", NULL
+ };
+ char **ret_array = NULL;
+ int i;
+ int ret = 0;
+
+ list = flb_sds_list_create();
+ if(!TEST_CHECK(list != NULL)) {
+ TEST_MSG("failed to allocate flb_sds_list");
+ exit(EXIT_FAILURE);
+ }
+
+ for (i=0; c_array[i] != NULL; i++) {
+ ret = flb_sds_list_add(list, c_array[i], strlen(c_array[i]));
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_sds_list_add failed");
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ ret_array = flb_sds_list_create_str_array(list);
+ if (!TEST_CHECK(ret_array != NULL)) {
+ TEST_MSG("flb_sds_list_create_str_array failed");
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+
+ for (i=0; c_array[i] != NULL; i++) {
+ if (!TEST_CHECK(ret_array[i] != NULL)) {
+ TEST_MSG("ret_array[%d] should not be NULL", i);
+ flb_sds_list_destroy_str_array(ret_array);
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+ if(!TEST_CHECK(strcmp(c_array[i], ret_array[i]) == 0)) {
+ TEST_MSG("%d:mismatch. c=%s sds=%s",i, c_array[i], ret_array[i]);
+ flb_sds_list_destroy_str_array(ret_array);
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ ret = flb_sds_list_destroy_str_array(ret_array);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_sds_list_destroy_str_array failed");
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = flb_sds_list_destroy(list);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to destroy flb_sds_list");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void test_sds_list_size()
+{
+ struct flb_sds_list *list = NULL;
+ char *c_array[] = {
+ "hoge", "moge", "aaa", NULL
+ };
+ int i;
+ int ret = 0;
+ size_t size = 0;
+
+ list = flb_sds_list_create();
+ if(!TEST_CHECK(list != NULL)) {
+ TEST_MSG("failed to allocate flb_sds_list");
+ exit(EXIT_FAILURE);
+ }
+
+ for (i=0; c_array[i] != NULL; i++) {
+ ret = flb_sds_list_add(list, c_array[i], strlen(c_array[i]));
+ size++;
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_sds_list_add failed");
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(size == flb_sds_list_size(list))) {
+ TEST_MSG("%d: size mismatch. got=%lu expect=%lu",i, flb_sds_list_size(list), size);
+ }
+ }
+
+ ret = flb_sds_list_destroy(list);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to destroy flb_sds_list");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void test_sds_list_del_last_entry()
+{
+ struct flb_sds_list *list = NULL;
+ char *c_array[] = {
+ "hoge", "moge", "aaa", NULL
+ };
+ char **str_array = NULL;
+ int i;
+ int ret = 0;
+ size_t size = 0;
+
+ list = flb_sds_list_create();
+ if(!TEST_CHECK(list != NULL)) {
+ TEST_MSG("failed to allocate flb_sds_list");
+ exit(EXIT_FAILURE);
+ }
+
+ for (i=0; c_array[i] != NULL; i++) {
+ ret = flb_sds_list_add(list, c_array[i], strlen(c_array[i]));
+ size++;
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_sds_list_add failed");
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!TEST_CHECK(size == flb_sds_list_size(list))) {
+ TEST_MSG("%d: size mismatch. got=%zu expect=%zu",i, flb_sds_list_size(list), size);
+ }
+ }
+
+ size = flb_sds_list_size(list);
+
+ while(size > 0) {
+ ret = flb_sds_list_del_last_entry(list);
+ if (!TEST_CHECK(ret==0)) {
+ TEST_MSG("flb_sds_list_del_last_entry failed");
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+ size--;
+ if (!TEST_CHECK(size == flb_sds_list_size(list))) {
+ TEST_MSG("size mismatch. got=%zu expect=%zu",flb_sds_list_size(list), size);
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+ if (size == 0) {
+ break;
+ }
+
+ str_array = flb_sds_list_create_str_array(list);
+ if (!TEST_CHECK(str_array != NULL)) {
+ TEST_MSG("flb_sds_list_create_str_array failed. size=%zu", size);
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+
+ for (i=0; str_array[i] != NULL; i++) {
+ if (!TEST_CHECK(str_array[i] != NULL)) {
+ TEST_MSG("str_array[%d] should not be NULL", i);
+ flb_sds_list_destroy_str_array(str_array);
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+ if(!TEST_CHECK(strcmp(c_array[i], str_array[i]) == 0)) {
+ TEST_MSG("%d:mismatch. c=%s sds=%s",i, c_array[i], str_array[i]);
+ flb_sds_list_destroy_str_array(str_array);
+ flb_sds_list_destroy(list);
+ exit(EXIT_FAILURE);
+ }
+ }
+ flb_sds_list_destroy_str_array(str_array);
+ }
+
+ ret = flb_sds_list_destroy(list);
+ if(!TEST_CHECK(ret == 0)) {
+ TEST_MSG("failed to destroy flb_sds_list");
+ exit(EXIT_FAILURE);
+ }
+}
+
+TEST_LIST = {
+ { "sds_list_create_destroy" , test_sds_list_create_destroy},
+ { "sds_list_add" , test_sds_list_add},
+ { "sds_list_array" , test_sds_list_array},
+ { "sds_list_size" , test_sds_list_size},
+ { "sds_list_del_last_entry" , test_sds_list_del_last_entry},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/signv4.c b/fluent-bit/tests/internal/signv4.c
new file mode 100644
index 00000000..7c691bc3
--- /dev/null
+++ b/fluent-bit/tests/internal/signv4.c
@@ -0,0 +1,666 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2020 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * AWS Signv4 documentation
+ * ========================
+ *
+ * https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
+ *
+ * AWS Signv4 Test Suite
+ * =====================
+ *
+ * AWS provides a test suite that can be used to validate certain requests type and
+ * expected signatures. The following unit test file, uses the suite provided and
+ * provides certain wrappers to validate expected results.
+ */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_kv.h>
+#include <fluent-bit/flb_http_client.h>
+#include <fluent-bit/flb_upstream.h>
+#include <fluent-bit/flb_signv4.h>
+#include <fluent-bit/flb_aws_credentials.h>
+#include <monkey/mk_core.h>
+
+#include "flb_tests_internal.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <stdlib.h>
+
+/* Test suite entry point */
+#define AWS_SUITE FLB_TESTS_DATA_PATH "data/signv4/aws-sig-v4-test-suite/"
+
+/* Credentials Environment Variables */
+#define AWS_ACCESS_KEY_ID "AWS_ACCESS_KEY_ID"
+#define AWS_SECRET_ACCESS_KEY "AWS_SECRET_ACCESS_KEY"
+#define AWS_SESSION_TOKEN "AWS_SESSION_TOKEN"
+
+struct request {
+ int method_i;
+ flb_sds_t method;
+ flb_sds_t uri;
+ flb_sds_t uri_full;
+ flb_sds_t query_string;
+ flb_sds_t payload;
+ struct mk_list headers;
+};
+
+struct aws_test {
+ flb_sds_t name; /* test name */
+ flb_sds_t authz;
+ flb_sds_t creq;
+ flb_sds_t req;
+ flb_sds_t sreq;
+ flb_sds_t sts;
+ struct request *r;
+ struct flb_http_client *c;
+ struct mk_list _head;
+};
+
+static struct request *http_request_create(char *request)
+{
+ int len;
+ char *sep;
+ char *start;
+ char *end;
+ char *p;
+ char *br = NULL;
+ flb_sds_t tmp;
+ flb_sds_t key;
+ flb_sds_t val;
+ flb_sds_t payload = NULL;
+ struct flb_kv *kv = NULL;
+ struct request *req;
+
+ req = flb_calloc(1, sizeof(struct request));
+ if (!req) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(&req->headers);
+
+ /* method */
+ p = strchr(request, ' ');
+ req->method = flb_sds_create_size(10);
+ if (!req->method) {
+ flb_free(req);
+ return NULL;
+ }
+
+ tmp = flb_sds_copy(req->method, request, p - request);
+ if (!tmp) {
+ flb_sds_destroy(req->method);
+ flb_free(req);
+ return NULL;
+ }
+ req->method = tmp;
+
+ if (strcmp(req->method, "GET") == 0) {
+ req->method_i = FLB_HTTP_GET;
+ }
+ else if (strcmp(req->method, "POST") == 0) {
+ req->method_i = FLB_HTTP_POST;
+ }
+
+ /* URI */
+ start = p + 1;
+ p = strchr(start, '\n');
+ if (!p) {
+ flb_sds_destroy(req->method);
+ flb_free(req);
+ return NULL;
+ }
+ p--;
+ if ((p - 8) <= start) {
+ return NULL;
+ }
+ end = (p - 8);
+
+ len = end - start;
+ req->uri = flb_sds_create_size(len);
+ tmp = flb_sds_copy(req->uri, start, len);
+ if (!tmp) {
+ flb_sds_destroy(req->method);
+ flb_sds_destroy(req->uri);
+ flb_free(req);
+ return NULL;
+ }
+
+ req->uri = tmp;
+ req->uri_full = flb_sds_create(req->uri);
+
+ /* Query string: it might be inside the URI */
+ start = req->uri;
+ p = strchr(start, '?');
+ if (p) {
+ flb_sds_len_set(req->uri, (p - req->uri));
+ len = flb_sds_len(req->uri) - (p - req->uri);
+ *p = '\0'; /* terminate the string */
+ start = p + 1;
+ req->query_string = flb_sds_create_len(start, len);
+ }
+
+ /* Headers, everything after the first LF (\n) */
+ p = strchr(request, '\n');
+ p++;
+
+ len = strlen(request) - (p - request);
+ start = p;
+ do {
+ /* HTTP line folding (multi line header) */
+ if ((*start == ' ' || *start == '\t') && kv) {
+ key = flb_sds_create(kv->key);
+ sep = start + 1;
+ }
+ else {
+ /* key */
+ sep = strchr(start, ':');
+ if (!sep) {
+ break;
+ }
+ key = flb_sds_create_len(start, sep - start);
+ }
+
+ /* value */
+ start = sep + 1;
+ br = strchr(start, '\n');
+ if (!br) {
+ break;
+ }
+
+ val = flb_sds_create_len(start, br - start);
+ kv = flb_kv_item_create_len(&req->headers,
+ key, flb_sds_len(key), val, flb_sds_len(val));
+ flb_sds_destroy(key);
+ flb_sds_destroy(val);
+
+ /* next header */
+ start = br + 1;
+ p = strchr(start, '\n');
+ } while (p && *p == '\n');
+
+ /* Is this a POST request with a payload ? */
+ if (p && *p == '\n') {
+ p++;
+ if (p) {
+ len = strlen(request) - (p - request);
+ payload = flb_sds_create_len(p, len);
+ }
+ }
+ else {
+ /* Append any remaining headers, aws tests do not end files with a \n */
+ br++;
+ if ((br - request) - len) {
+ start = br;
+ sep = strchr(start, ':');
+
+ key = flb_sds_create_len(start, sep - start);
+ val = flb_sds_create(sep + 1);
+ flb_kv_item_create_len(&req->headers,
+ key, flb_sds_len(key), val, flb_sds_len(val));
+ flb_sds_destroy(key);
+ flb_sds_destroy(val);
+ }
+ }
+
+ if (payload) {
+ req->payload = payload;
+ }
+
+ return req;
+}
+
+static void http_request_destroy(struct request *req)
+{
+ if (!req) {
+ return;
+ }
+
+ if (req->method) {
+ flb_sds_destroy(req->method);
+ }
+ if (req->uri) {
+ flb_sds_destroy(req->uri);
+ }
+ if (req->uri_full) {
+ flb_sds_destroy(req->uri_full);
+ }
+ if (req->query_string) {
+ flb_sds_destroy(req->query_string);
+ }
+ if (req->payload) {
+ flb_sds_destroy(req->payload);
+ }
+
+ flb_kv_release(&req->headers);
+ flb_free(req);
+}
+
+/* Convert a TXT HTTP request to a Fluent Bit http_client context */
+static struct flb_http_client *convert_request_file(char *request,
+ struct request **r,
+ struct flb_config *config)
+{
+ struct flb_upstream *u;
+ struct flb_connection *u_conn;
+ struct flb_http_client *c;
+ struct mk_list *head;
+ struct flb_kv *kv;
+ struct request *req;
+
+ /* Fake Upstream context, required by http client */
+ u = flb_upstream_create(config, "127.0.0.1", 80, 0, NULL);
+ if (!u) {
+ fprintf(stderr, "error creating upstream context");
+ flb_free(config);
+ return NULL;
+ }
+
+ /* Fake upstream connection */
+ u_conn = flb_calloc(1, sizeof(struct flb_connection));
+ if (!u_conn) {
+ flb_errno();
+ flb_upstream_destroy(u);
+ flb_free(config);
+ }
+ u_conn->upstream = u;
+
+ /* Convert TXT HTTP request to our local 'request' structure */
+ req = http_request_create(request);
+ if (!req) {
+ fprintf(stderr, "error parsing txt http request");
+ exit(1);
+ }
+
+ /* HTTP Client context */
+ c = flb_http_client(u_conn, req->method_i, req->uri_full,
+ req->payload, req->payload ? flb_sds_len(req->payload): -1,
+ NULL, -1, NULL, 0);
+
+ /*
+ * flb_http_client automatically adds host and content-length
+ * for the tests we remove these since all headers come from
+ * the the test file
+ */
+ mk_list_foreach(head, &c->headers) {
+ kv = mk_list_entry(head, struct flb_kv, _head);
+ if (strncasecmp(kv->key, "Host", 4) == 0) {
+ flb_kv_item_destroy(kv);
+ break;
+ }
+ }
+ mk_list_foreach(head, &c->headers) {
+ kv = mk_list_entry(head, struct flb_kv, _head);
+ if (strncasecmp(kv->key, "Content-Length", 14) == 0) {
+ flb_kv_item_destroy(kv);
+ break;
+ }
+ }
+
+ /* Append registered headers */
+ mk_list_foreach(head, &req->headers) {
+ kv = mk_list_entry(head, struct flb_kv, _head);
+ flb_http_add_header(c,
+ kv->key, flb_sds_len(kv->key),
+ kv->val, flb_sds_len(kv->val));
+ }
+
+ *r = req;
+ return c;
+}
+
+static flb_sds_t file_to_buffer(char *path, char *context, char *ext)
+{
+ char abs_path[2048];
+ char *buf;
+ flb_sds_t data;
+
+ snprintf(abs_path, sizeof(abs_path) - 1, "%s/%s.%s", path, context, ext);
+ buf = mk_file_to_buffer(abs_path);
+ if (!buf) {
+ return NULL;
+ }
+
+ data = flb_sds_create(buf);
+ if (!data) {
+ fprintf(stderr, "error allocating sds buffer to test %s file\n", abs_path);
+ flb_free(buf);
+ return NULL;
+ }
+ flb_free(buf);
+
+ return data;
+}
+
+static void aws_test_destroy(struct aws_test *awt)
+{
+ if (awt->name) {
+ flb_sds_destroy(awt->name);
+ }
+ if (awt->authz) {
+ flb_sds_destroy(awt->authz);
+ }
+ if (awt->creq) {
+ flb_sds_destroy(awt->creq);
+ }
+ if (awt->req) {
+ flb_sds_destroy(awt->req);
+ }
+ if (awt->sreq) {
+ flb_sds_destroy(awt->sreq);
+ }
+ if (awt->sts) {
+ flb_sds_destroy(awt->sts);
+ }
+
+ if (awt->c) {
+ flb_upstream_destroy(awt->c->u_conn->upstream);
+ flb_free(awt->c->u_conn);
+ flb_http_client_destroy(awt->c);
+ }
+
+ http_request_destroy(awt->r);
+ flb_free(awt);
+}
+
+static struct aws_test *aws_test_create(char *path, char *context,
+ struct flb_config *config)
+{
+ struct aws_test *awt;
+
+ awt = flb_calloc(1, sizeof(struct aws_test));
+ if (!awt) {
+ flb_errno();
+ return NULL;
+ }
+
+ awt->name = flb_sds_create(context);
+ if (!awt->name) {
+ fprintf(stderr, "cannot allocate awt name\n");
+ goto error;
+ }
+
+ /* If no 'authz' file is found, return right away */
+ awt->authz = file_to_buffer(path, context, "authz");
+ if (!awt->authz) {
+ aws_test_destroy(awt);
+ return NULL;
+ }
+
+ awt->creq = file_to_buffer(path, context, "creq");
+ if (!awt->creq) {
+ fprintf(stderr, "error reading creq file");
+ goto error;
+ }
+
+ awt->req = file_to_buffer(path, context, "req");
+ if (!awt->req) {
+ fprintf(stderr, "error reading req file");
+ goto error;
+ }
+
+ awt->sreq = file_to_buffer(path, context, "sreq");
+ if (!awt->sreq) {
+ fprintf(stderr, "error reading req file");
+ goto error;
+ }
+
+ awt->sts = file_to_buffer(path, context, "sts");
+ if (!awt->sts) {
+ fprintf(stderr, "error reading req file");
+ goto error;
+ }
+
+ /* Convert TXT HTTP request to http_client context */
+ awt->c = convert_request_file(awt->req, &awt->r, config);
+ if (!awt->c) {
+ fprintf(stderr, "error converting TXT request to a context: %s", awt->name);
+ goto error;
+ }
+
+ return awt;
+
+ error:
+ //aws_test_destroy(awt);
+ return NULL;
+}
+
+static int load_aws_test_directory(struct mk_list *list, char *ut_path,
+ struct flb_config *config)
+{
+ int ret;
+ struct dirent *e;
+ DIR *dir;
+ char path[2048];
+ char ut[4096];
+ struct stat st;
+ struct aws_test *awt;
+
+ dir = opendir(ut_path);
+ if (!dir) {
+ flb_errno();
+ flb_error("signv4: cannot open test suite located at '%s'", AWS_SUITE);
+ return -1;
+ }
+
+ /* Read directory entries */
+ while ((e = readdir(dir)) != NULL) {
+ if (*e->d_name == '.') {
+ continue;
+ }
+
+ snprintf(path, sizeof(path) - 1, "%s%s", ut_path, e->d_name);
+ ret = stat(path, &st);
+ if (ret == -1) {
+ continue;
+ }
+
+ /* only process directories */
+ if (!S_ISDIR(st.st_mode)) {
+ continue;
+ }
+
+ /* check for unit test file */
+ snprintf(ut, sizeof(ut) - 1, "%s%s/%s.req", path, e->d_name, e->d_name);
+ ret = stat(path, &st);
+ if (ret == -1) {
+ continue;
+ }
+
+ awt = aws_test_create(path, e->d_name, config);
+ if (!awt) {
+ continue;
+ }
+ mk_list_add(&awt->_head, list);
+ }
+
+ closedir(dir);
+ return 0;
+}
+
+static void aws_tests_destroy(struct mk_list *list)
+{
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct aws_test *awt;
+
+ mk_list_foreach_safe(head, tmp, list) {
+ awt = mk_list_entry(head, struct aws_test, _head);
+ mk_list_del(&awt->_head);
+ aws_test_destroy(awt);
+ }
+
+ flb_free(list);
+}
+
+static struct mk_list *aws_tests_create(struct flb_config *config)
+{
+ struct mk_list *list;
+ char path[2048];
+
+ printf("\n");
+
+ list = flb_malloc(sizeof(struct mk_list));
+ if (!list) {
+ flb_errno();
+ return NULL;
+ }
+ mk_list_init(list);
+
+ /* Load base path for AWS test suite, some sub-directories will be skipped */
+ load_aws_test_directory(list, AWS_SUITE, config);
+
+ /* Load pending sub-directories */
+ snprintf(path, sizeof(path) - 1, "%s/normalize-path/", AWS_SUITE);
+ load_aws_test_directory(list, path, config);
+
+ snprintf(path, sizeof(path) - 1, "%s/post-sts-token/", AWS_SUITE);
+ load_aws_test_directory(list, path, config);
+
+ return list;
+}
+
+static void aws_test_suite()
+{
+ int ret;
+ time_t t;
+ char *region = NULL;
+ char *access_key = NULL;
+ char *service = NULL;
+ char *secret_key = NULL;
+ flb_sds_t signature;
+ struct mk_list *head;
+ struct mk_list *tests;
+ struct flb_config *config;
+ struct aws_test *awt;
+ struct flb_aws_provider *provider;
+
+ config = flb_calloc(1, sizeof(struct flb_config));
+ if (!config) {
+ flb_errno();
+ return;
+ }
+ mk_list_init(&config->upstreams);
+
+ /* Get a list of tests */
+ tests = aws_tests_create(config);
+ TEST_CHECK(tests != NULL);
+ if (!tests) {
+ flb_free(config);
+ return;
+ }
+
+ /* Convert static '20150830T123600Z' to unix timestamp */
+ t = 1440938160;
+ region = "us-east-1";
+ access_key = "AKIDEXAMPLE";
+ service = "service";
+ secret_key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY";
+
+ /* credentials */
+ ret = setenv(AWS_ACCESS_KEY_ID, access_key, 1);
+ if (ret < 0) {
+ flb_errno();
+ return;
+ }
+ ret = setenv(AWS_SECRET_ACCESS_KEY, secret_key, 1);
+ if (ret < 0) {
+ flb_errno();
+ return;
+ }
+ provider = flb_aws_env_provider_create();
+ if (!provider) {
+ flb_errno();
+ return;
+ }
+
+ /* Iterate tests and sign the requests */
+ mk_list_foreach(head, tests) {
+ awt = mk_list_entry(head, struct aws_test, _head);
+ fprintf(stderr, "[AWS Signv4 Unit Test] %-50s", awt->name);
+ signature = flb_signv4_do(awt->c,
+ FLB_TRUE, /* normalize URI ? */
+ FLB_FALSE, /* add x-amz-date header ? */
+ t, region, service,
+ 0, NULL,
+ provider);
+ TEST_CHECK(signature != NULL);
+ if (signature) {
+ ret = strncmp(awt->authz, signature, flb_sds_len(awt->authz));
+ TEST_CHECK(ret == 0);
+ if (ret != 0) {
+ fprintf(stderr, "\t\tFAIL");
+ fprintf(stderr,
+ ">\n> signature check failed...\n received: %s\n expected: %s\n",
+ signature, awt->authz);
+ }
+ else {
+ fprintf(stderr, "PASS");
+ }
+ flb_sds_destroy(signature);
+ }
+ fprintf(stderr, "\n");
+ }
+
+ aws_tests_destroy(tests);
+ flb_aws_provider_destroy(provider);
+ flb_free(config);
+}
+
+static void check_normalize(char *s, size_t len, char *out)
+{
+ flb_sds_t o;
+
+ o = flb_signv4_uri_normalize_path(s, len);
+ TEST_CHECK(strcmp(o, out) == 0);
+ flb_sds_destroy(o);
+}
+
+void normalize()
+{
+ /* get-relative */
+ check_normalize("/example/..", 11, "/");
+
+ /* get-relative-relative */
+ check_normalize("/example1/example2/../..", 24, "/");
+
+ /* get-slash */
+ check_normalize("//", 2, "/");
+
+ /* get-slash-dot-slash */
+ check_normalize("/./", 3, "/");
+
+ /* get-slashes */
+ check_normalize("//example//", 11, "/example/");
+
+ /* get-slash-pointless-dot */
+ check_normalize("/./example", 10, "/example");
+}
+
+TEST_LIST = {
+ { "aws_test_suite", aws_test_suite},
+ { "normalize", normalize},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/slist.c b/fluent-bit/tests/internal/slist.c
new file mode 100644
index 00000000..8c786fc8
--- /dev/null
+++ b/fluent-bit/tests/internal/slist.c
@@ -0,0 +1,205 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_slist.h>
+
+#include "flb_tests_internal.h"
+
+void token_check(struct mk_list *list, int id, char *str)
+{
+ int i = 0;
+ int len;
+ int ret;
+ struct mk_list *head;
+ struct flb_slist_entry *e = NULL;
+
+ mk_list_foreach(head, list) {
+ if (i == id) {
+ e = mk_list_entry(head, struct flb_slist_entry, _head);
+ break;
+ }
+ e = NULL;
+ i++;
+ }
+ TEST_CHECK(e != NULL);
+
+ len = strlen(str);
+ ret = flb_sds_cmp(e->str, str, len);
+ TEST_CHECK(ret == 0);
+ if (ret != 0) {
+ fprintf(stderr, "[token %i] expected '%s', got '%s'\n\n",
+ i, str, e->str);
+ exit(EXIT_FAILURE);
+ }
+}
+
+void test_slist_add()
+{
+ int ret;
+ struct mk_list list;
+ struct flb_slist_entry *e;
+
+ ret = flb_slist_create(&list);
+ TEST_CHECK(ret == 0);
+
+ ret = flb_slist_add(&list, "");
+ TEST_CHECK(ret == -1);
+
+ ret = flb_slist_add(&list, NULL);
+ TEST_CHECK(ret == -1);
+
+ TEST_CHECK(mk_list_is_empty(&list) == 0);
+
+ ret = flb_slist_add(&list, "test");
+ TEST_CHECK(ret == 0);
+
+ e = mk_list_entry_last(&list, struct flb_slist_entry, _head);
+ TEST_CHECK(flb_sds_len(e->str) == 4);
+
+ ret = flb_slist_add_n(&list, "test", 3);
+ TEST_CHECK(ret == 0);
+
+ e = mk_list_entry_last(&list, struct flb_slist_entry, _head);
+ TEST_CHECK(flb_sds_len(e->str) == 3);
+
+ flb_slist_destroy(&list);
+}
+
+void test_slist_split_string()
+{
+ int ret;
+ struct mk_list list;
+ struct flb_slist_entry *e;
+
+ ret = flb_slist_create(&list);
+ TEST_CHECK(ret == 0);
+
+ /* Simple string without separator */
+ ret = flb_slist_split_string(&list, "abcdefg", ' ', -1);
+ TEST_CHECK(ret == 1);
+ TEST_CHECK(mk_list_size(&list) == 1);
+ token_check(&list, 0, "abcdefg");
+ flb_slist_destroy(&list);
+
+ /* Separated strings */
+ ret = flb_slist_split_string(&list, "a bc defg", ' ', -1);
+ TEST_CHECK(ret == 3);
+ TEST_CHECK(mk_list_size(&list) == 3);
+ token_check(&list, 0, "a");
+ token_check(&list, 1, "bc");
+ token_check(&list, 2, "defg");
+ flb_slist_destroy(&list);
+
+ /* One char with empty spaces */
+ ret = flb_slist_split_string(&list, " a ", ' ', 2);
+ TEST_CHECK(ret == 1);
+ TEST_CHECK(mk_list_size(&list) == 1);
+ token_check(&list, 0, "a");
+ flb_slist_destroy(&list);
+
+ /* Two separated characters */
+ ret = flb_slist_split_string(&list, " a b ", ' ', 2);
+ TEST_CHECK(ret == 2);
+ TEST_CHECK(mk_list_size(&list) == 2);
+ token_check(&list, 0, "a");
+ token_check(&list, 1, "b");
+ flb_slist_destroy(&list);
+
+ /* Comma separated strings */
+ ret = flb_slist_split_string(&list, ",,,a ,, b,c ,d,,e ,f,g, , ,", ',', -1);
+ TEST_CHECK(ret == 7);
+ TEST_CHECK(mk_list_size(&list) == 7);
+ token_check(&list, 0, "a");
+ token_check(&list, 1, "b");
+ token_check(&list, 2, "c");
+ token_check(&list, 3, "d");
+ token_check(&list, 4, "e");
+ token_check(&list, 5, "f");
+ token_check(&list, 6, "g");
+ flb_slist_destroy(&list);
+
+ /* Comma seperated strings for real world NO_PROXY example */
+ ret = flb_slist_split_string(&list, "127.0.0.1, localhost, kubernetes.default.svc.cluster.local", ',', -1);
+ TEST_CHECK(ret == 3);
+ TEST_CHECK(mk_list_size(&list) == 3);
+ token_check(&list, 0, "127.0.0.1");
+ token_check(&list, 1, "localhost");
+ token_check(&list, 2, "kubernetes.default.svc.cluster.local");
+ flb_slist_destroy(&list);
+
+ /* Comma separated strings with limit */
+ ret = flb_slist_split_string(&list, ",,,a ,, b, c ,d,,e ,f,g, , ,", ',', 2);
+ TEST_CHECK(ret == 3);
+ TEST_CHECK(mk_list_size(&list) == 3);
+ e = mk_list_entry_last(&list, struct flb_slist_entry, _head);
+ TEST_CHECK(flb_sds_len(e->str) == 22);
+ flb_slist_destroy(&list);
+
+ /* Nothing */
+ ret = flb_slist_split_string(&list, ",,, ,, , , , ", ',', 2);
+ TEST_CHECK(ret == 0);
+ TEST_CHECK(mk_list_size(&list) == 0);
+}
+
+void test_slist_split_tokens()
+{
+ struct mk_list list;
+ char *txt = \
+ " this \"is a tokens parser\" \" apples \", "
+ "no\"quoted \"this is \\\"quoted\\\"\" "
+ "don't escape insi\\\"de q\\\"uoted strings\\\"";
+
+ mk_list_init(&list);
+ flb_slist_split_tokens(&list, txt, -1);
+
+ token_check(&list, 0, "this");
+ token_check(&list, 1, "is a tokens parser");
+ token_check(&list, 2, " apples ");
+ token_check(&list, 3, ",");
+ token_check(&list, 4, "no\"quoted");
+ token_check(&list, 5, "this is \"quoted\"");
+ token_check(&list, 6, "don't");
+ token_check(&list, 7, "escape");
+ token_check(&list, 8, "insi\\\"de");
+ token_check(&list, 9, "q\\\"uoted");
+ token_check(&list, 10, "strings\\\"");
+
+ flb_slist_destroy(&list);
+
+ mk_list_init(&list);
+ flb_slist_split_string(&list, "aaa bbb ccc ddd eee", ' ', 3);
+ token_check(&list, 3, "ddd eee");
+ flb_slist_destroy(&list);
+
+ mk_list_init(&list);
+ flb_slist_split_tokens(&list, "aaa bbb ccc ddd eee", 3);
+ token_check(&list, 3, "ddd eee");
+ flb_slist_destroy(&list);
+
+}
+
+void test_bugs()
+{
+ int ret;
+ struct mk_list list;
+ struct flb_slist_entry *e;
+
+ ret = flb_slist_create(&list);
+
+ /* Bug found during #293 development */
+ ret = flb_slist_split_string(&list, "$key2 ab final-tag true", ' ', 4);
+ TEST_CHECK(ret == 4);
+ e = flb_slist_entry_get(&list, 2);
+ TEST_CHECK(*e->str == 'f');
+
+ flb_slist_destroy(&list);
+}
+
+TEST_LIST = {
+ { "add" , test_slist_add},
+ { "split_string", test_slist_split_string},
+ { "split_tokens", test_slist_split_tokens},
+ { "bugs" , test_bugs},
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/stream_processor.c b/fluent-bit/tests/internal/stream_processor.c
new file mode 100644
index 00000000..9afda0bf
--- /dev/null
+++ b/fluent-bit/tests/internal/stream_processor.c
@@ -0,0 +1,934 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2020 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit.h>
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_pack.h>
+#include <fluent-bit/flb_error.h>
+#include <fluent-bit/flb_router.h>
+#include <fluent-bit/flb_storage.h>
+#include <fluent-bit/stream_processor/flb_sp.h>
+#include <fluent-bit/stream_processor/flb_sp_parser.h>
+#include <fluent-bit/stream_processor/flb_sp_stream.h>
+#include <fluent-bit/stream_processor/flb_sp_window.h>
+#include <msgpack.h>
+
+#include "flb_tests_internal.h"
+#include "include/sp_invalid_queries.h"
+#include "include/sp_select_keys.h"
+#include "include/sp_select_subkeys.h"
+#include "include/sp_window.h"
+#include "include/sp_snapshot.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef _WIN32
+#include <fluent-bit/flb_compat.h>
+#else
+#include <unistd.h>
+#endif
+
+#define DATA_SAMPLES \
+ FLB_TESTS_DATA_PATH "/data/stream_processor/samples.mp"
+
+#define DATA_SAMPLES_SUBKEYS \
+ FLB_TESTS_DATA_PATH "/data/stream_processor/samples-subkeys.mp"
+
+#define DATA_SAMPLES_HOPPING_WINDOW_PATH \
+ FLB_TESTS_DATA_PATH "/data/stream_processor/samples-hw/"
+
+#define MP_UOK MSGPACK_UNPACK_SUCCESS
+
+int flb_sp_fd_event_test(int fd, struct flb_sp_task *task, struct sp_buffer *out_buf)
+{
+ char *tag = NULL;
+ int tag_len = 0;
+
+ if (task->window.type != FLB_SP_WINDOW_DEFAULT) {
+ if (fd == task->window.fd) {
+ if (task->window.records > 0) {
+ /* find input tag from task source */
+ package_results(tag, tag_len, &out_buf->buffer, &out_buf->size, task);
+ if (task->stream) {
+ flb_sp_stream_append_data(out_buf->buffer, out_buf->size, task->stream);
+ }
+ else {
+ flb_pack_print(out_buf->buffer, out_buf->size);
+ }
+ }
+
+ flb_sp_window_prune(task);
+ }
+ else if (fd == task->window.fd_hop) {
+ sp_process_hopping_slot(tag, tag_len, task);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Do data processing for internal unit tests, no engine required, set
+ * results on out_data/out_size variables.
+ */
+int flb_sp_do_test(struct flb_sp *sp, struct flb_sp_task *task,
+ const char *tag, int tag_len,
+ struct sp_buffer *data_buf, struct sp_buffer *out_buf)
+{
+ int ret;
+ int records;
+ struct flb_sp_cmd *cmd;
+
+ cmd = task->cmd;
+ if (cmd->source_type == FLB_SP_TAG) {
+ ret = flb_router_match(tag, tag_len, cmd->source_name, NULL);
+ if (ret == FLB_FALSE) {
+ out_buf->buffer = NULL;
+ out_buf->size = 0;
+ return 0;
+ }
+ }
+
+ if (task->aggregate_keys == FLB_TRUE) {
+ ret = sp_process_data_aggr(data_buf->buffer, data_buf->size,
+ tag, tag_len,
+ task, sp, FLB_TRUE);
+ if (ret == -1) {
+ flb_error("[sp] error error processing records for '%s'",
+ task->name);
+ return -1;
+ }
+
+ if (flb_sp_window_populate(task, data_buf->buffer, data_buf->size) == -1) {
+ flb_error("[sp] error populating window for '%s'",
+ task->name);
+ return -1;
+ }
+ if (task->window.type == FLB_SP_WINDOW_DEFAULT || task->window.type == FLB_SP_WINDOW_TUMBLING) {
+ package_results(tag, tag_len, &out_buf->buffer, &out_buf->size, task);
+ }
+
+ records = task->window.records;
+ }
+ else {
+ ret = sp_process_data(tag, tag_len,
+ data_buf->buffer, data_buf->size,
+ &out_buf->buffer, &out_buf->size,
+ task, sp);
+ if (ret == -1) {
+ flb_error("[sp] error processing records for '%s'",
+ task->name);
+ return -1;
+ }
+ records = ret;
+ }
+
+ if (records == 0) {
+ out_buf->buffer = NULL;
+ out_buf->size = 0;
+ return 0;
+ }
+
+ return 0;
+}
+
+/* this function reads the content of a file containing MessagePack data
+ into an input buffer
+*/
+static int file_to_buf(char *path, struct sp_buffer *out_buf)
+{
+ char *buf;
+ int ret;
+ long bytes;
+ FILE *fp;
+ struct stat st;
+
+ ret = stat(path, &st);
+ if (ret == -1) {
+ return -1;
+ }
+
+ fp = fopen(path, "r");
+ if (!fp) {
+ return -1;
+ }
+
+ buf = flb_malloc(st.st_size);
+ if (!buf) {
+ flb_errno();
+ fclose(fp);
+ return -1;
+ }
+
+ bytes = fread(buf, st.st_size, 1, fp);
+ if (bytes != 1) {
+ flb_errno();
+ flb_free(buf);
+ fclose(fp);
+ return -1;
+ }
+
+ fclose(fp);
+ out_buf->buffer = buf;
+ out_buf->size = st.st_size;
+
+ return 0;
+}
+
+static void invalid_queries()
+{
+ int i;
+ int checks;
+ struct flb_config *config;
+ struct flb_sp *sp;
+ struct flb_sp_task *task;
+
+ flb_init_env();
+
+ /* Total number of checks for invalid queries */
+ checks = sizeof(invalid_query_checks) / sizeof(char *);
+
+ config = flb_calloc(1, sizeof(struct flb_config));
+ if (!config) {
+ flb_errno();
+ return;
+ }
+
+ mk_list_init(&config->inputs);
+ mk_list_init(&config->stream_processor_tasks);
+
+ /* Create a stream processor context */
+ sp = flb_sp_create(config);
+ if (!sp) {
+ flb_error("[sp test] cannot create stream processor context");
+ flb_free(config);
+ return;
+ }
+
+ for (i = 0; i < checks; i++) {
+ task = flb_sp_task_create(sp, "invalid_query", invalid_query_checks[i]);
+ TEST_CHECK(task == NULL);
+ }
+
+ flb_sp_destroy(sp);
+ flb_free(config);
+}
+
+static void test_select_keys()
+{
+ int i;
+ int checks;
+ int ret;
+ struct sp_buffer data_buf;
+ struct sp_buffer out_buf;
+ struct flb_config *config;
+ struct flb_sp *sp;
+ struct task_check *check;
+ struct flb_sp_task *task;
+#ifdef _WIN32
+ WSADATA wsa_data;
+#endif
+
+ flb_init_env();
+
+ config = flb_calloc(1, sizeof(struct flb_config));
+ if (!config) {
+ flb_errno();
+ return;
+ }
+#ifdef _WIN32
+ WSAStartup(0x0201, &wsa_data);
+#endif
+ mk_list_init(&config->inputs);
+ mk_list_init(&config->stream_processor_tasks);
+
+ /* Create event loop */
+ config->evl = mk_event_loop_create(256);
+
+ /* Create a stream processor context */
+ sp = flb_sp_create(config);
+ if (!sp) {
+ flb_error("[sp test] cannot create stream processor context");
+ flb_free(config);
+ return;
+ }
+
+ ret = file_to_buf(DATA_SAMPLES, &data_buf);
+ if (ret == -1) {
+ flb_error("[sp test] cannot open DATA_SAMPLES file %s", DATA_SAMPLES);
+ flb_free(config);
+ return;
+ }
+
+ /* Total number of checks for select_keys */
+ checks = (sizeof(select_keys_checks) / sizeof(struct task_check));
+
+ /* Run every test */
+ for (i = 0; i < checks; i++) {
+ check = (struct task_check *) &select_keys_checks[i];
+
+ task = flb_sp_task_create(sp, check->name, check->exec);
+ if (!task) {
+ flb_error("[sp test] wrong check '%s', fix it!", check->name);
+ continue;
+ }
+
+ out_buf.buffer = NULL;
+
+ ret = flb_sp_do_test(sp, task,
+ "samples", strlen("samples"),
+ &data_buf, &out_buf);
+ if (ret == -1) {
+ flb_error("[sp test] error processing check '%s'", check->name);
+ flb_sp_task_destroy(task);
+ continue;
+ }
+
+ /* */
+ flb_sp_fd_event_test(task->window.fd, task, &out_buf);
+
+ flb_info("[sp test] id=%i, SQL => '%s'", check->id, check->exec);
+ check->cb_check(check->id, check, out_buf.buffer, out_buf.size);
+ flb_pack_print(out_buf.buffer, out_buf.size);
+ flb_free(out_buf.buffer);
+ }
+
+ flb_free(data_buf.buffer);
+ flb_sp_destroy(sp);
+ mk_event_loop_destroy(config->evl);
+ flb_free(config);
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+static void test_select_subkeys()
+{
+ int i;
+ int checks;
+ int ret;
+ struct sp_buffer out_buf;
+ struct sp_buffer data_buf;
+ struct task_check *check;
+ struct flb_config *config;
+ struct flb_sp *sp;
+ struct flb_sp_task *task;
+#ifdef _WIN32
+ WSADATA wsa_data;
+#endif
+
+ flb_init_env();
+
+ config = flb_calloc(1, sizeof(struct flb_config));
+ if (!config) {
+ flb_errno();
+ return;
+ }
+#ifdef _WIN32
+ WSAStartup(0x0201, &wsa_data);
+#endif
+ mk_list_init(&config->inputs);
+ mk_list_init(&config->stream_processor_tasks);
+
+ config->evl = mk_event_loop_create(256);
+
+ sp = flb_sp_create(config);
+ if (!sp) {
+ flb_error("[sp test] cannot create stream processor context");
+ flb_free(config);
+ return;
+ }
+
+ ret = file_to_buf(DATA_SAMPLES_SUBKEYS, &data_buf);
+ if (ret == -1) {
+ flb_error("[sp test] cannot open DATA_SAMPLES file %s",
+ DATA_SAMPLES_SUBKEYS);
+ flb_free(config);
+ return;
+ }
+
+ /* Total number of checks for select_subkeys */
+ checks = (sizeof(select_subkeys_checks) / sizeof(struct task_check));
+
+ /* Run every test */
+ for (i = 0; i < checks; i++) {
+ check = (struct task_check *) &select_subkeys_checks[i];
+
+ task = flb_sp_task_create(sp, check->name, check->exec);
+ if (!task) {
+ flb_error("[sp test] wrong check '%s', fix it!", check->name);
+ continue;
+ }
+
+ out_buf.buffer = NULL;
+ out_buf.size = 0;
+
+ ret = flb_sp_do_test(sp, task,
+ "samples", strlen("samples"),
+ &data_buf, &out_buf);
+ if (ret == -1) {
+ flb_error("[sp test] error processing check '%s'", check->name);
+ flb_sp_task_destroy(task);
+ continue;
+ }
+
+ flb_sp_fd_event_test(task->window.fd, task, &out_buf);
+
+ flb_info("[sp test] id=%i, SQL => '%s'", check->id, check->exec);
+ check->cb_check(check->id, check, out_buf.buffer, out_buf.size);
+ flb_pack_print(out_buf.buffer, out_buf.size);
+ flb_free(out_buf.buffer);
+ }
+
+ flb_free(data_buf.buffer);
+ flb_sp_destroy(sp);
+ mk_event_loop_destroy(config->evl);
+ flb_free(config);
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+void set_record_timestamps(struct sp_buffer *data_buf, double *record_timestamp)
+{
+ /* unpacker variables */
+ int ok;
+ size_t off = 0;
+ msgpack_object root;
+ msgpack_object map;
+ msgpack_unpacked result;
+ struct flb_time tm;
+
+ /* packer variables */
+ msgpack_sbuffer mp_sbuf;
+ msgpack_packer mp_pck;
+
+ ok = MSGPACK_UNPACK_SUCCESS;
+ msgpack_unpacked_init(&result);
+
+ msgpack_sbuffer_init(&mp_sbuf);
+ msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
+
+ /* Iterate incoming records */
+ while (msgpack_unpack_next(&result, data_buf->buffer, data_buf->size, &off) == ok) {
+ root = result.data;
+
+ map = root.via.array.ptr[1];
+
+ msgpack_pack_array(&mp_pck, 2);
+ flb_time_set(&tm, *record_timestamp, 0);
+ flb_time_append_to_msgpack(&tm, &mp_pck, 0);
+ msgpack_pack_object(&mp_pck, map);
+
+ *record_timestamp = *record_timestamp + 1;
+ }
+
+ msgpack_unpacked_destroy(&result);
+ flb_free(data_buf->buffer);
+
+ data_buf->buffer = mp_sbuf.data;
+ data_buf->size = mp_sbuf.size;
+}
+
+static void test_window()
+{
+ int i;
+ int t;
+ int checks;
+ int ret;
+ char datafile[PATH_MAX];
+ struct sp_buffer data_buf;
+ struct sp_buffer out_buf;
+ struct task_check *check;
+ struct flb_config *config;
+ struct flb_sp *sp;
+ struct flb_sp_task *task;
+#ifdef _WIN32
+ WSADATA wsa_data;
+#endif
+
+ flb_init_env();
+
+ config = flb_calloc(1, sizeof(struct flb_config));
+ if (!config) {
+ flb_errno();
+ return;
+ }
+#ifdef _WIN32
+ WSAStartup(0x0201, &wsa_data);
+#endif
+ mk_list_init(&config->inputs);
+ mk_list_init(&config->stream_processor_tasks);
+ config->evl = mk_event_loop_create(256);
+
+ sp = flb_sp_create(config);
+ if (!sp) {
+ flb_error("[sp test] cannot create stream processor context");
+ flb_free(config);
+ return;
+ }
+
+ /* Total number of checks for select_keys */
+ checks = (sizeof(window_checks) / sizeof(struct task_check));
+
+ /* Run every test */
+ for (i = 0; i < checks; i++) {
+ check = (struct task_check *) &window_checks[i];
+
+ task = flb_sp_task_create(sp, check->name, check->exec);
+ TEST_CHECK(task != NULL);
+
+ out_buf.buffer = NULL;
+ out_buf.size = 0;
+
+ double record_timestamp = 1.0;
+ if (check->window_type == FLB_SP_WINDOW_TUMBLING) {
+ ret = file_to_buf(DATA_SAMPLES, &data_buf);
+ if (ret == -1) {
+ flb_error("[sp test] cannot open DATA_SAMPLES file %s", DATA_SAMPLES);
+ flb_free(config);
+ return;
+ }
+
+ set_record_timestamps(&data_buf, &record_timestamp);
+
+ /* We ingest the buffer every second */
+ for (t = 0; t < check->window_size_sec; t++) {
+ if (out_buf.buffer != NULL) {
+ flb_free(out_buf.buffer);
+
+ out_buf.buffer = NULL;
+ out_buf.size = 0;
+ }
+
+ ret = flb_sp_do_test(sp, task,
+ "samples", strlen("samples"),
+ &data_buf, &out_buf);
+ if (ret == -1) {
+ flb_error("[sp test] error processing check '%s'",
+ check->name);
+ flb_sp_task_destroy(task);
+ return;
+ }
+
+ /* Sleep for 0.8 seconds, give some delta to the engine */
+ usleep(800000);
+ }
+
+ if (out_buf.buffer != NULL) {
+ flb_free(out_buf.buffer);
+
+ out_buf.buffer = NULL;
+ out_buf.size = 0;
+ }
+
+ flb_sp_fd_event_test(task->window.fd, task, &out_buf);
+
+ flb_info("[sp test] id=%i, SQL => '%s'", check->id, check->exec);
+ check->cb_check(check->id, check, out_buf.buffer, out_buf.size);
+ flb_pack_print(out_buf.buffer, out_buf.size);
+ flb_free(out_buf.buffer);
+ }
+ else if (check->window_type == FLB_SP_WINDOW_HOPPING) {
+ /* Ingest the buffer every second */
+ task->window.fd = 0;
+ task->window.fd_hop = 1;
+ double record_timestamp = 1.0;
+ for (t = 0; t < check->window_size_sec + check->window_hop_sec; t++) {
+ ret = snprintf(datafile, sizeof(datafile)-1, "%s%d.mp",
+ DATA_SAMPLES_HOPPING_WINDOW_PATH, t + 1);
+ if (!TEST_CHECK(ret <= sizeof(datafile)-1)) {
+ exit(1);
+ }
+ ret = file_to_buf(datafile, &data_buf);
+ if (ret == -1) {
+ flb_error("[sp test] cannot open DATA_SAMPLES file %s", datafile);
+ flb_free(config);
+ return;
+ }
+
+ /* Replace record timestamps with test timestamps */
+ set_record_timestamps(&data_buf, &record_timestamp);
+
+ ret = flb_sp_do_test(sp, task,
+ "samples", strlen("samples"),
+ &data_buf, &out_buf);
+ if (ret == -1) {
+ flb_error("[sp test] error processing check '%s'",
+ check->name);
+ flb_sp_task_destroy(task);
+ return;
+ }
+
+ /* Sleep for 0.8 seconds, give some delta to the engine */
+ usleep(800000);
+
+ /* Hopping event */
+ if ((t + 1) % check->window_hop_sec == 0) {
+ if (out_buf.buffer != NULL) {
+ flb_free(out_buf.buffer);
+
+ out_buf.buffer = NULL;
+ out_buf.size = 0;
+ }
+
+ flb_sp_fd_event_test(task->window.fd_hop, task, &out_buf);
+ }
+
+ /* Window event */
+ if ((t + 1) % check->window_size_sec == 0 ||
+ (t + 1 > check->window_size_sec && (t + 1 - check->window_size_sec) % check->window_hop_sec == 0)) {
+ if (out_buf.buffer != NULL) {
+ flb_free(out_buf.buffer);
+
+ out_buf.buffer = NULL;
+ out_buf.size = 0;
+ }
+
+ flb_sp_fd_event_test(task->window.fd, task, &out_buf);
+ }
+ flb_free(data_buf.buffer);
+ data_buf.buffer = NULL;
+ }
+
+ flb_info("[sp test] id=%i, SQL => '%s'", check->id, check->exec);
+ check->cb_check(check->id, check, out_buf.buffer, out_buf.size);
+ flb_pack_print(out_buf.buffer, out_buf.size);
+ flb_free(out_buf.buffer);
+ }
+
+ flb_free(data_buf.buffer);
+ }
+
+ flb_sp_destroy(sp);
+ mk_event_loop_destroy(config->evl);
+ flb_free(config);
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+static void test_snapshot()
+{
+ int i;
+ int t;
+ int checks;
+ int ret;
+ char datafile[PATH_MAX];
+ char stream_name[100];
+ char window_val[3];
+ struct sp_buffer data_buf;
+ struct sp_buffer out_buf;
+ struct task_check *check;
+ struct task_check *check_flush;
+ struct flb_config *config;
+ struct flb_sp *sp;
+ struct flb_sp_task *task;
+ struct flb_sp_task *task_flush;
+
+#ifdef _WIN32
+ WSADATA wsa_data;
+#endif
+
+ flb_init_env();
+
+ config = flb_calloc(1, sizeof(struct flb_config));
+ if (!config) {
+ flb_errno();
+ return;
+ }
+#ifdef _WIN32
+ WSAStartup(0x0201, &wsa_data);
+#endif
+ mk_list_init(&config->inputs);
+ mk_list_init(&config->stream_processor_tasks);
+ config->evl = mk_event_loop_create(256);
+
+ sp = flb_sp_create(config);
+ if (!sp) {
+ flb_error("[sp test] cannot create stream processor context");
+ flb_free(config);
+ return;
+ }
+
+ ret = file_to_buf(DATA_SAMPLES, &data_buf);
+ if (ret == -1) {
+ flb_error("[sp test] cannot open DATA_SAMPLES file %s", DATA_SAMPLES);
+ flb_free(config);
+ return;
+ }
+
+ /* Total number of checks for select_keys */
+ checks = (sizeof(snapshot_checks) / (sizeof(struct task_check) * 2));
+
+ /* Run every test */
+ for (i = 0; i < checks; i++) {
+ /* Snapshot Create */
+ check = (struct task_check *) &snapshot_checks[i][0];
+
+ task = flb_sp_task_create(sp, check->name, check->exec);
+ if (!task) {
+ flb_error("[sp test] wrong check '%s', fix it!", check->name);
+ continue;
+ }
+
+ snprintf(stream_name, 100, "%s-%d", "SNAPSHOT", i);
+ task->cmd->stream_name = flb_sds_create(stream_name);
+ task->cmd->type = FLB_SP_CREATE_SNAPSHOT;
+ if (check->window_size_sec > 0) {
+ snprintf(window_val, 3, "%d", check->window_size_sec);
+ flb_sp_cmd_stream_prop_add(task->cmd, "seconds", window_val);
+ }
+
+ if (flb_sp_snapshot_create(task) == -1) {
+ flb_error("[sp test] error initializing snapshot for check '%s'!", check->name);
+ continue;
+ }
+
+ out_buf.buffer = NULL;
+ out_buf.size = 0;
+
+ /* Read 1.mp -> 5.mp message pack buffers created for window tests */
+ for (t = 0; t < 5; t++) {
+
+ ret = snprintf(datafile, sizeof(datafile)-1, "%s%d.mp",
+ DATA_SAMPLES_HOPPING_WINDOW_PATH, t + 1);
+ if (!TEST_CHECK(ret <= sizeof(datafile)-1)) {
+ exit(1);
+ }
+
+ if (data_buf.buffer) {
+ flb_free(data_buf.buffer);
+ data_buf.buffer = NULL;
+ }
+
+ ret = file_to_buf(datafile, &data_buf);
+ if (ret == -1) {
+ flb_error("[sp test] cannot open DATA_SAMPLES file %s", datafile);
+ flb_free(config);
+ return;
+ }
+
+ ret = flb_sp_do_test(sp, task,
+ "samples", strlen("samples"),
+ &data_buf, &out_buf);
+
+ if (ret == -1) {
+ flb_error("[sp test] error processing check '%s'", check->name);
+ flb_sp_task_destroy(task);
+ continue;
+ }
+ }
+
+ flb_sp_fd_event_test(task->window.fd, task, &out_buf);
+
+ flb_info("[sp test] id=%i, SQL => '%s'", check->id, check->exec);
+ check->cb_check(check->id, check, out_buf.buffer, out_buf.size);
+ flb_pack_print(out_buf.buffer, out_buf.size);
+ flb_free(out_buf.buffer);
+
+ /* Snapshot flush */
+ check_flush = (struct task_check *) &snapshot_checks[i][1];
+
+ task_flush = flb_sp_task_create(sp, check_flush->name, check_flush->exec);
+ if (!task_flush) {
+ flb_error("[sp test] wrong check '%s', fix it!", check_flush->name);
+ continue;
+ }
+
+ snprintf(stream_name, 100, "%s-%d", "__flush_SNAPSHOT", i);
+ task_flush->cmd->stream_name = flb_sds_create(stream_name);
+ task_flush->cmd->type = FLB_SP_FLUSH_SNAPSHOT;
+
+ out_buf.buffer = NULL;
+ out_buf.size = 0;
+
+ ret = flb_sp_do_test(sp, task_flush,
+ "samples", strlen("samples"),
+ &data_buf, &out_buf);
+ if (ret == -1) {
+ flb_error("[sp test] error processing check '%s'", check_flush->name);
+ flb_sp_task_destroy(task_flush);
+ continue;
+ }
+
+ flb_sp_fd_event_test(task->window.fd, task_flush, &out_buf);
+
+ flb_info("[sp test] id=%i, SQL => '%s'", check_flush->id, check_flush->exec);
+ check_flush->cb_check(check_flush->id, check_flush, out_buf.buffer, out_buf.size);
+ flb_pack_print(out_buf.buffer, out_buf.size);
+ flb_free(out_buf.buffer);
+
+ flb_free(data_buf.buffer);
+ data_buf.buffer = NULL;
+ }
+
+ flb_free(data_buf.buffer);
+ flb_sp_destroy(sp);
+ mk_event_loop_destroy(config->evl);
+ flb_free(config);
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+static void test_conv_from_str_to_num()
+{
+ struct flb_config *config = NULL;
+ struct flb_sp *sp = NULL;
+ struct flb_sp_task *task = NULL;
+ struct sp_buffer out_buf;
+ struct sp_buffer data_buf;
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ msgpack_unpacked result;
+ size_t off = 0;
+ char json[4096] = {0};
+ int ret;
+
+#ifdef _WIN32
+ WSADATA wsa_data;
+
+ WSAStartup(0x0201, &wsa_data);
+#endif
+ out_buf.buffer = NULL;
+
+ flb_init_env();
+
+ config = flb_config_init();
+ config->evl = mk_event_loop_create(256);
+
+ ret = flb_storage_create(config);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_storage_create failed");
+ flb_config_exit(config);
+ return;
+ }
+
+ sp = flb_sp_create(config);
+ if (!TEST_CHECK(sp != NULL)) {
+ TEST_MSG("[sp test] cannot create stream processor context");
+ goto test_conv_from_str_to_num_end;
+ }
+
+ task = flb_sp_task_create(sp, "tail.0", "CREATE STREAM test WITH (tag=\'test\') AS SELECT word, num, COUNT(*) FROM STREAM:tail.0 WINDOW TUMBLING (1 SECOND) GROUP BY word, num;");
+ if (!TEST_CHECK(task != NULL)) {
+ TEST_MSG("[sp test] wrong check 'conv', fix it!");
+ goto test_conv_from_str_to_num_end;
+ }
+
+ /* Create input data */
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+ msgpack_pack_array(&pck, 2);
+ flb_pack_time_now(&pck);
+ msgpack_pack_map(&pck, 2);
+
+ msgpack_pack_str(&pck, 4);
+ msgpack_pack_str_body(&pck, "word", 4);
+ msgpack_pack_str(&pck, 4);
+ msgpack_pack_str_body(&pck, "hoge", 4);
+
+ msgpack_pack_str(&pck, 3);
+ msgpack_pack_str_body(&pck, "num", 3);
+ msgpack_pack_str(&pck, 6);
+ msgpack_pack_str_body(&pck, "123456", 6);
+
+ data_buf.buffer = sbuf.data;
+ data_buf.size = sbuf.size;
+
+ out_buf.buffer = NULL;
+ out_buf.size = 0;
+
+ /* Exec stream processor */
+ ret = flb_sp_do_test(sp, task, "tail.0", strlen("tail.0"), &data_buf, &out_buf);
+ if (!TEST_CHECK(ret == 0)) {
+ TEST_MSG("flb_sp_do_test failed");
+ msgpack_sbuffer_destroy(&sbuf);
+ goto test_conv_from_str_to_num_end;
+ }
+
+ if (!TEST_CHECK(out_buf.size > 0)) {
+ TEST_MSG("out_buf size is 0");
+ msgpack_sbuffer_destroy(&sbuf);
+ goto test_conv_from_str_to_num_end;
+ }
+
+
+ /* Check output buffer. It should contain a number 123456 not a string "123456" */
+
+ msgpack_unpacked_init(&result);
+ ret = msgpack_unpack_next(&result, out_buf.buffer, out_buf.size, &off);
+ if (!TEST_CHECK(ret == MSGPACK_UNPACK_SUCCESS)) {
+ TEST_MSG("failed to unpack ret=%d", ret);
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&sbuf);
+ goto test_conv_from_str_to_num_end;
+ }
+
+ ret = flb_msgpack_to_json(&json[0], sizeof(json), &result.data);
+ if (!TEST_CHECK(ret > 0)) {
+ TEST_MSG("flb_msgpack_to_json failed");
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&sbuf);
+ goto test_conv_from_str_to_num_end;
+ }
+
+ if (!TEST_CHECK(strstr(json,"123456") != NULL)) {
+ TEST_MSG("number not found");
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&sbuf);
+ goto test_conv_from_str_to_num_end;
+ }
+ if (!TEST_CHECK(strstr(json,"\"123456\"") == NULL)) {
+ TEST_MSG("output should be number type");
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&sbuf);
+ goto test_conv_from_str_to_num_end;
+ }
+
+ msgpack_unpacked_destroy(&result);
+ msgpack_sbuffer_destroy(&sbuf);
+
+ test_conv_from_str_to_num_end:
+ if (out_buf.buffer != NULL) {
+ flb_free(out_buf.buffer);
+ }
+
+#ifdef _WIN32
+ WSACleanup();
+#endif
+ if (sp != NULL) {
+ flb_sp_destroy(sp);
+ }
+ flb_storage_destroy(config);
+ flb_config_exit(config);
+}
+
+TEST_LIST = {
+ { "invalid_queries", invalid_queries},
+ { "select_keys", test_select_keys},
+ { "select_subkeys", test_select_subkeys},
+ { "window", test_window},
+ { "snapshot", test_snapshot},
+ { "conv_from_str_to_num", test_conv_from_str_to_num},
+ { NULL }
+};
diff --git a/fluent-bit/tests/internal/typecast.c b/fluent-bit/tests/internal/typecast.c
new file mode 100644
index 00000000..511d8cf5
--- /dev/null
+++ b/fluent-bit/tests/internal/typecast.c
@@ -0,0 +1,359 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2019-2021 The Fluent Bit Authors
+ * Copyright (C) 2015-2018 Treasure Data Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_typecast.h>
+#include "flb_tests_internal.h"
+#include <msgpack.h>
+#include <string.h>
+
+void str_to_int()
+{
+ char *input = "1234";
+
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ struct flb_typecast_rule *rule = NULL;
+ struct flb_typecast_value *val = NULL;
+
+ /* create input object */
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_str(&pck, strlen(input));
+ msgpack_pack_str_body(&pck, input, strlen(input));
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, sbuf.data, sbuf.size, &off);
+
+ /* create rule */
+ rule = flb_typecast_rule_create("string", 6, "int", 3);
+ if (!TEST_CHECK(rule != NULL)) {
+ TEST_MSG("failed to create rule");
+ exit(EXIT_FAILURE);
+ }
+
+ val = flb_typecast_value_create(result.data, rule);
+ if(!TEST_CHECK(val != NULL)){
+ TEST_MSG("failed to create value");
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(val->type == FLB_TYPECAST_TYPE_INT);
+ if(!TEST_CHECK(val->val.i_num == 1234)) {
+ TEST_MSG("got %ld. expect 1234", val->val.i_num);
+ }
+
+ flb_typecast_rule_destroy(rule);
+ flb_typecast_value_destroy(val);
+
+ msgpack_sbuffer_destroy(&sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void int_to_str()
+{
+ int input = 1234;
+
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ struct flb_typecast_rule *rule = NULL;
+ struct flb_typecast_value *val = NULL;
+
+ /* create input object */
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_int64(&pck, input);
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, sbuf.data, sbuf.size, &off);
+
+ /* create rule */
+ rule = flb_typecast_rule_create("int", 3, "string", 6);
+ if (!TEST_CHECK(rule != NULL)) {
+ TEST_MSG("failed to create rule");
+ exit(EXIT_FAILURE);
+ }
+
+ val = flb_typecast_value_create(result.data, rule);
+ if(!TEST_CHECK(val != NULL)){
+ TEST_MSG("failed to create value");
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(val->type == FLB_TYPECAST_TYPE_STR);
+ if(!TEST_CHECK(!strcmp(val->val.str, "1234"))) {
+ TEST_MSG("got %s. expect \"1234\"", val->val.str);
+ }
+
+ flb_typecast_rule_destroy(rule);
+ flb_typecast_value_destroy(val);
+
+ msgpack_sbuffer_destroy(&sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void bool_to_str()
+{
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ struct flb_typecast_rule *rule = NULL;
+ struct flb_typecast_value *val = NULL;
+
+ /* create input object */
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_true(&pck);
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, sbuf.data, sbuf.size, &off);
+
+ /* create rule */
+ rule = flb_typecast_rule_create("bool", 4, "string", 6);
+ if (!TEST_CHECK(rule != NULL)) {
+ TEST_MSG("failed to create rule");
+ exit(EXIT_FAILURE);
+ }
+
+ val = flb_typecast_value_create(result.data, rule);
+ if(!TEST_CHECK(val != NULL)){
+ TEST_MSG("failed to create value");
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(val->type == FLB_TYPECAST_TYPE_STR);
+ if(!TEST_CHECK(!strcmp(val->val.str, "true"))) {
+ TEST_MSG("got %s. expect \"true\"", val->val.str);
+ }
+
+ flb_typecast_rule_destroy(rule);
+ flb_typecast_value_destroy(val);
+
+ msgpack_sbuffer_destroy(&sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void str_to_bool()
+{
+ char *input = "true";
+
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ struct flb_typecast_rule *rule = NULL;
+ struct flb_typecast_value *val = NULL;
+
+ /* create input object */
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_str(&pck, strlen(input));
+ msgpack_pack_str_body(&pck, input, strlen(input));
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, sbuf.data, sbuf.size, &off);
+
+ /* create rule */
+ rule = flb_typecast_rule_create("string", 6, "bool", 4);
+ if (!TEST_CHECK(rule != NULL)) {
+ TEST_MSG("failed to create rule");
+ exit(EXIT_FAILURE);
+ }
+
+ val = flb_typecast_value_create(result.data, rule);
+ if(!TEST_CHECK(val != NULL)){
+ TEST_MSG("failed to create value");
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(val->type == FLB_TYPECAST_TYPE_BOOL);
+ if(!TEST_CHECK(val->val.boolean == FLB_TRUE)) {
+ TEST_MSG("got %d. expect FLB_TRUE", val->val.boolean);
+ }
+
+ flb_typecast_rule_destroy(rule);
+ flb_typecast_value_destroy(val);
+
+ msgpack_sbuffer_destroy(&sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void str_to_hex()
+{
+ char *input = "0xdeadbeef";
+
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ struct flb_typecast_rule *rule = NULL;
+ struct flb_typecast_value *val = NULL;
+
+ /* create input object */
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_str(&pck, strlen(input));
+ msgpack_pack_str_body(&pck, input, strlen(input));
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, sbuf.data, sbuf.size, &off);
+
+ /* create rule */
+ rule = flb_typecast_rule_create("string", 6, "hex", 3);
+ if (!TEST_CHECK(rule != NULL)) {
+ TEST_MSG("failed to create rule");
+ exit(EXIT_FAILURE);
+ }
+
+ val = flb_typecast_value_create(result.data, rule);
+ if(!TEST_CHECK(val != NULL)){
+ TEST_MSG("failed to create value");
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(val->type == FLB_TYPECAST_TYPE_HEX);
+ if(!TEST_CHECK(val->val.ui_num == 0xdeadbeef)) {
+ TEST_MSG("got 0x%lx. expect 0xdeadbeef", val->val.ui_num);
+ }
+
+ flb_typecast_rule_destroy(rule);
+ flb_typecast_value_destroy(val);
+
+ msgpack_sbuffer_destroy(&sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void str_to_float()
+{
+ char *input = "1234.567";
+
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ struct flb_typecast_rule *rule = NULL;
+ struct flb_typecast_value *val = NULL;
+
+ /* create input object */
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_str(&pck, strlen(input));
+ msgpack_pack_str_body(&pck, input, strlen(input));
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, sbuf.data, sbuf.size, &off);
+
+ /* create rule */
+ rule = flb_typecast_rule_create("string", 6, "float", 5);
+ if (!TEST_CHECK(rule != NULL)) {
+ TEST_MSG("failed to create rule");
+ exit(EXIT_FAILURE);
+ }
+
+ val = flb_typecast_value_create(result.data, rule);
+ if(!TEST_CHECK(val != NULL)){
+ TEST_MSG("failed to create value");
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(val->type == FLB_TYPECAST_TYPE_FLOAT);
+ if(!TEST_CHECK(val->val.d_num == 1234.567)) {
+ TEST_MSG("got %f. expect 1234.567", val->val.d_num);
+ }
+
+ flb_typecast_rule_destroy(rule);
+ flb_typecast_value_destroy(val);
+
+ msgpack_sbuffer_destroy(&sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+void float_to_str()
+{
+ double input = 1234.567;
+
+ msgpack_sbuffer sbuf;
+ msgpack_packer pck;
+ msgpack_unpacked result;
+ size_t off = 0;
+
+ struct flb_typecast_rule *rule = NULL;
+ struct flb_typecast_value *val = NULL;
+
+ /* create input object */
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_float(&pck, input);
+
+ msgpack_unpacked_init(&result);
+ msgpack_unpack_next(&result, sbuf.data, sbuf.size, &off);
+
+ /* create rule */
+ rule = flb_typecast_rule_create("float", 5, "string", 6);
+ if (!TEST_CHECK(rule != NULL)) {
+ TEST_MSG("failed to create rule");
+ exit(EXIT_FAILURE);
+ }
+
+ val = flb_typecast_value_create(result.data, rule);
+ if(!TEST_CHECK(val != NULL)){
+ TEST_MSG("failed to create value");
+ exit(EXIT_FAILURE);
+ }
+
+ TEST_CHECK(val->type == FLB_TYPECAST_TYPE_STR);
+ if(!TEST_CHECK(strstr(val->val.str, "1234.567") != NULL)) {
+ TEST_MSG("got %s. expect \"1234.567\"", val->val.str);
+ }
+
+ flb_typecast_rule_destroy(rule);
+ flb_typecast_value_destroy(val);
+
+ msgpack_sbuffer_destroy(&sbuf);
+ msgpack_unpacked_destroy(&result);
+}
+
+TEST_LIST = {
+ {"str_to_int", str_to_int},
+ {"int_to_str", int_to_str},
+ {"str_to_float", str_to_float},
+ {"float_to_str", float_to_str},
+ {"bool_to_str", bool_to_str},
+ {"str_to_bool", str_to_bool},
+ {"str_to_hex", str_to_hex},
+ {NULL, NULL}
+};
diff --git a/fluent-bit/tests/internal/unit_sizes.c b/fluent-bit/tests/internal/unit_sizes.c
new file mode 100644
index 00000000..d009bafb
--- /dev/null
+++ b/fluent-bit/tests/internal/unit_sizes.c
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_utils.h>
+
+#include "flb_tests_internal.h"
+
+#define to_bytes flb_utils_size_to_bytes
+
+void test_unit_sizes()
+{
+ int64_t KB = 1000;
+ int64_t MB = 1000 * KB;
+ int64_t GB = 1000 * MB;
+
+ /* Bytes, no prefixes */
+ TEST_CHECK(to_bytes("1") == 1);
+ TEST_CHECK(to_bytes("50") == 50);
+ TEST_CHECK(to_bytes("1000") == 1*KB);
+
+ /* Decimal prefix: KB */
+ TEST_CHECK(to_bytes("1KB") == KB);
+ TEST_CHECK(to_bytes("1K") == KB);
+ TEST_CHECK(to_bytes("1kB") == KB);
+ TEST_CHECK(to_bytes("1kb") == KB);
+ TEST_CHECK(to_bytes("1k") == KB);
+
+ /* Decimal prefix: MB */
+ TEST_CHECK(to_bytes("1MB") == MB);
+ TEST_CHECK(to_bytes("1M") == MB);
+ TEST_CHECK(to_bytes("1mB") == MB);
+ TEST_CHECK(to_bytes("1mb") == MB);
+ TEST_CHECK(to_bytes("1m") == MB);
+ TEST_CHECK(to_bytes("5m") == 5*MB);
+
+ /* Decimal prefix: GB */
+ TEST_CHECK(to_bytes("1GB") == GB);
+ TEST_CHECK(to_bytes("1G") == GB);
+ TEST_CHECK(to_bytes("1gB") == GB);
+ TEST_CHECK(to_bytes("1gb") == GB);
+ TEST_CHECK(to_bytes("1g") == GB);
+ TEST_CHECK(to_bytes("5g") == 5*GB);
+ TEST_CHECK(to_bytes("32g") == 32*GB);
+
+ /* Invalid values */
+ TEST_CHECK(to_bytes("aabb") == -1);
+ TEST_CHECK(to_bytes("") == -1);
+
+ /* Invlid prefixes */
+ TEST_CHECK(to_bytes("1kX") == -1);
+ TEST_CHECK(to_bytes("1kX") == -1);
+ TEST_CHECK(to_bytes("1MX") == -1);
+ TEST_CHECK(to_bytes("1GX") == -1);
+}
+
+TEST_LIST = {
+ { "unit_sizes", test_unit_sizes },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/uri.c b/fluent-bit/tests/internal/uri.c
new file mode 100644
index 00000000..2b533b37
--- /dev/null
+++ b/fluent-bit/tests/internal/uri.c
@@ -0,0 +1,87 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Fluent Bit
+ * ==========
+ * Copyright (C) 2015-2023 The Fluent Bit Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_uri.h>
+#include <string.h>
+
+#include "flb_tests_internal.h"
+
+void uri_create_destroy()
+{
+ struct flb_uri *uri;
+ const char *uri_str = "https://fluentbit.io";
+
+ uri = flb_uri_create(uri_str);
+ if (!TEST_CHECK(uri != NULL)) {
+ TEST_MSG("flb_uri_create failed");
+ return;
+ }
+
+ flb_uri_destroy(uri);
+}
+
+void uri_get()
+{
+ struct flb_uri *uri;
+ struct flb_uri_field *field;
+ const char *uri_str = "https://fluentbit.io";
+
+ uri = flb_uri_create(uri_str);
+ if (!TEST_CHECK(uri != NULL)) {
+ TEST_MSG("flb_uri_create failed");
+ return;
+ }
+
+ field = flb_uri_get(uri, 0);
+ if (!TEST_CHECK(field != NULL)) {
+ TEST_MSG("flb_uri_get failed");
+ return;
+ }
+
+ field = flb_uri_get(uri, -1);
+ if (!TEST_CHECK(field == NULL)) {
+ TEST_MSG("flb_uri_get should fail");
+ return;
+ }
+
+ flb_uri_destroy(uri);
+}
+
+void uri_encode()
+{
+ flb_sds_t encoded_uri;
+ const char *input = "&# ";
+ const char *expect = "%26%23%20";
+
+ encoded_uri = flb_uri_encode(input, strlen(input));
+ if (!TEST_CHECK(encoded_uri != NULL)) {
+ TEST_MSG("flb_uri_encode failed");
+ return;
+ }
+
+ flb_sds_destroy(encoded_uri);
+}
+
+TEST_LIST = {
+ { "uri_create_destroy", uri_create_destroy },
+ { "uri_get", uri_get },
+ { "uri_encode", uri_encode },
+ { 0 }
+};
diff --git a/fluent-bit/tests/internal/utils.c b/fluent-bit/tests/internal/utils.c
new file mode 100644
index 00000000..119094bd
--- /dev/null
+++ b/fluent-bit/tests/internal/utils.c
@@ -0,0 +1,622 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <fluent-bit/flb_info.h>
+#include <fluent-bit/flb_mem.h>
+#include <fluent-bit/flb_utils.h>
+#include <stdarg.h>
+#include "flb_tests_internal.h"
+#include "fluent-bit/flb_macros.h"
+
+
+struct url_check {
+ int ret;
+ char *url; /* full URL */
+ char *prot; /* expected protocol */
+ char *host; /* expected host */
+ char *port; /* expected port */
+ char *uri; /* expected uri */
+};
+
+struct write_str_case {
+ char *input;
+ int input_len;
+ char *output;
+ int ret;
+};
+
+struct url_check url_checks[] = {
+ {0, "https://fluentbit.io/something",
+ "https", "fluentbit.io", "443", "/something"},
+ {0, "http://fluentbit.io/something",
+ "http", "fluentbit.io", "80", "/something"},
+ {0, "https://fluentbit.io", "https", "fluentbit.io", "443", "/"},
+ {0, "https://fluentbit.io:1234/something",
+ "https", "fluentbit.io", "1234", "/something"},
+ {0, "https://fluentbit.io:1234", "https", "fluentbit.io", "1234", "/"},
+ {0, "https://fluentbit.io:1234/", "https", "fluentbit.io", "1234", "/"},
+ {0, "https://fluentbit.io:1234/v", "https", "fluentbit.io", "1234", "/v"},
+ {-1, "://", NULL, NULL, NULL, NULL},
+};
+
+void test_url_split()
+{
+ int i;
+ int ret;
+ int size;
+ char *protocol;
+ char *host;
+ char *port;
+ char *uri;
+ struct url_check *u;
+
+ size = sizeof(url_checks) / sizeof(struct url_check);
+ for (i = 0; i < size; i ++) {
+ u = &url_checks[i];
+
+ protocol = NULL;
+ host = NULL;
+ port = NULL;
+ uri = NULL;
+
+ ret = flb_utils_url_split(u->url, &protocol, &host, &port, &uri);
+ TEST_CHECK(ret == u->ret);
+ if (ret == -1) {
+ continue;
+ }
+
+ /* protocol */
+ if (u->prot) {
+ TEST_CHECK(protocol != NULL);
+
+ ret = strcmp(u->prot, protocol);
+ TEST_CHECK(ret == 0);
+ }
+ else {
+ TEST_CHECK(protocol == NULL);
+ }
+
+ /* host */
+ if (u->host) {
+ TEST_CHECK(host != NULL);
+ ret = strcmp(u->host, host);
+ TEST_CHECK(ret == 0);
+ }
+ else {
+ TEST_CHECK(host == NULL);
+ }
+
+ /* port */
+ if (u->port) {
+ TEST_CHECK(port != NULL);
+ ret = strcmp(u->port, port);
+ TEST_CHECK(ret == 0);
+ }
+ else {
+ TEST_CHECK(port == NULL);
+ }
+
+ /* uri */
+ if (u->uri) {
+ TEST_CHECK(uri != NULL);
+ ret = strcmp(u->uri, uri);
+ TEST_CHECK(ret == 0);
+ }
+ else {
+ TEST_CHECK(uri == NULL);
+ }
+
+ if (protocol) {
+ flb_free(protocol);
+ }
+ if (host) {
+ flb_free(host);
+ }
+ if (port) {
+ flb_free(port);
+ }
+ if (uri) {
+ flb_free(uri);
+ }
+ }
+}
+
+/* test case loop for flb_utils_write_str */
+static void write_str_test_cases_w_buf_size(struct write_str_case *cases, int buf_size);
+static void write_str_test_cases(struct write_str_case *cases) {
+ write_str_test_cases_w_buf_size(cases, 100);
+}
+
+/* test case loop for flb_utils_write_str */
+static void write_str_test_cases_w_buf_size(struct write_str_case *cases, int buf_size) {
+ char *buf = flb_calloc(buf_size + 1, sizeof(char));
+ int size = buf_size + 1;
+ int off;
+ int ret;
+
+ struct write_str_case *tcase = cases;
+ while (!(tcase->input == 0 && tcase->output == 0)) {
+ memset(buf, 0, size);
+ off = 0;
+ ret = flb_utils_write_str(buf, &off, buf_size, tcase->input, tcase->input_len);
+
+ if(!TEST_CHECK(ret == tcase->ret)) {
+ TEST_MSG("Input string: %s", tcase->input);
+ TEST_MSG("| Expected return value: %s", (tcase->ret == FLB_TRUE) ? "FLB_TRUE"
+ : "FLB_FALSE");
+ TEST_MSG("| Produced return value: %s", (ret == FLB_TRUE) ? "FLB_TRUE"
+ : "FLB_FALSE");
+ }
+ if(!TEST_CHECK(memcmp(buf, tcase->output, off) == 0)) {
+ TEST_MSG("Input string: %s", tcase->input);
+ TEST_MSG("| Expected output: %s", tcase->output);
+ TEST_MSG("| Produced output: %s", buf);
+ }
+ if (!TEST_CHECK(strlen(buf) == strlen(tcase->output))) {
+ TEST_MSG("Input string: %s", tcase->input);
+ TEST_MSG("| Expected length: %zu", strlen(tcase->output));
+ TEST_MSG("| Produced length: %zu", strlen(buf));
+ TEST_MSG("| Expected output: %s", tcase->output);
+ TEST_MSG("| Produced output: %s", buf);
+ }
+ if (!TEST_CHECK(buf[size-1] == 0)) {
+ TEST_MSG("Out buffer overwrite detected '%c'", buf[size-1]);
+ }
+
+ ++tcase;
+ }
+
+ flb_free(buf);
+}
+
+void test_write_str()
+{
+ char buf[10];
+ char japanese_a[4] = {0xe3, 0x81, 0x82};
+ int size = sizeof(buf);
+ int off;
+ int ret;
+
+ off = 0;
+ ret = flb_utils_write_str(buf, &off, size, "a", 1);
+ TEST_CHECK(ret == FLB_TRUE);
+ TEST_CHECK(memcmp(buf, "a", off) == 0);
+
+ off = 0;
+ ret = flb_utils_write_str(buf, &off, size, "\n", 1);
+ TEST_CHECK(ret == FLB_TRUE);
+ TEST_CHECK(memcmp(buf, "\\n", off) == 0);
+
+ off = 0;
+ ret = flb_utils_write_str(buf, &off, size, "\xe3\x81\x82", 3);
+ TEST_CHECK(ret == FLB_TRUE);
+ TEST_CHECK(memcmp(buf, japanese_a, off) == 0);
+
+ // Truncated bytes
+ off = 0;
+ ret = flb_utils_write_str(buf, &off, size, "\xe3\x81\x82\xe3", 1);
+ TEST_CHECK(ret == FLB_TRUE);
+ TEST_CHECK(memcmp(buf, japanese_a, off) == 0);
+
+ // Error: buffer too small
+ off = 0;
+ ret = flb_utils_write_str(buf, &off, size, "aaaaaaaaaaa", 11);
+ TEST_CHECK(ret == FLB_FALSE);
+}
+
+void test_write_str_invalid_trailing_bytes()
+{
+ struct write_str_case cases[] = {
+ /* Invalid unicode (one bad trailing bytes) */
+ {
+ "\xe3\x81\x01""abc", 6, /* note that 0x01 is an invalid byte */
+ "\xee\x83\xa3" /* e3 fragment */ /* replace invalid unicode */
+ "\xee\x82\x81" /* 81 fragment */
+ "\\u0001abc",
+ FLB_TRUE
+ },
+ /*
+ * Invalid unicode (two bad trailing bytes)
+ */
+ {
+ "\xe3\x01\x01""abc", 6,
+ "\xee\x83\xa3" /* e3 fragment */
+ "\\u0001\\u0001abc",
+ FLB_TRUE
+ },
+ { 0 }
+ };
+
+ write_str_test_cases(cases);
+}
+
+void test_write_str_invalid_leading_byte()
+{
+
+ struct write_str_case cases[] = {
+ /*
+ * Escaped leading hex (two hex, one valid unicode)
+ */
+ {
+ "\x00\x01\xe3\x81\x82""abc", 8, /* note that 0x01 is an invalid byte */
+ "\\u0000\\u0001""\xe3\x81\x82""abc", /* escape hex */
+ FLB_TRUE
+ },
+ /*
+ * Invalid unicode fragment (two byte fragment)
+ * note that 0xf3 is a leading byte with 3 trailing bytes. note that 0xe3 is also a
+ * leading byte with 2 trailing bytes. This should not be consumed by 0xf3 invalid
+ * unicode character
+ */
+ {
+ "\xf3\x81\x81\xe3\x81\x82""abc", 9, /* note that 0xf3 0x81 0x81 is an invalid fragment */
+ "\xee\x83\xb3" /* f3 fragment */ /* replace invalid unicode */
+ "\xee\x82\x81" /* 81 fragment */
+ "\xee\x82\x81" /* 81 fragment */
+ "\xe3\x81\x82""abc", /* valid unicode */
+ FLB_TRUE
+ },
+ /*
+ * Invalid unicode (one bad leading byte + one bad trailing byte)
+ * note that 0xf3 is a leading byte with 3 trailing bytes. 0x01 is an invalid byte
+ */
+ {
+ "\xf3\x81\x01\xe3\x81\x82""abc", 9, /* note that 0x01 is an invalid byte */
+ "\xee\x83\xb3" /* f3 fragment */ /* replace invalid unicode */
+ "\xee\x82\x81" /* 81 fragment */
+ "\\u0001""\xe3\x81\x82""abc",
+ FLB_TRUE
+ },
+ { 0 }
+ };
+
+ write_str_test_cases(cases);
+}
+
+void test_write_str_invalid_leading_byte_case_2()
+{
+
+ struct write_str_case cases[] = {
+ /* Invalid leading bytes */
+ {
+ "\x81\x82""abc", 5, /* note that 0x81 & 0x82 are invalid leading bytes */
+ "\xee\x82\x81" /* 81 fragment */ /* replace invalid unicode */
+ "\xee\x82\x82" /* 82 fragment */
+ "abc",
+ FLB_TRUE
+ },
+ /*
+ * Invalid unicode (one bad leading byte + one bad trailing byte + one bad leading byte)
+ * note that 0xf3 is a leading byte with 3 trailing bytes. 0x01 is an invalid byte
+ * 0x81 & 0x82 are invalid leading bytes
+ */
+ {
+ "\xf3\x81\x01\x81\x82""abc", 8, /* note that 0x81 & 0x82 are invalid leading bytes */
+ "\xee\x83\xb3" /* f3 fragment */ /* replace invalid unicode */
+ "\xee\x82\x81" /* 81 fragment */
+ "\\u0001" /* 0x01 hex escape */
+ "\xee\x82\x81" /* 81 fragment */
+ "\xee\x82\x82" /* 82 fragment */
+ "abc",
+ FLB_TRUE
+ },
+ { 0 }
+ };
+
+ write_str_test_cases(cases);
+}
+
+void test_write_str_edge_cases()
+{
+ struct write_str_case cases[] = {
+ /* Invalid unicode (one bad leading byte) */
+ {
+ "\xf3", 1, /* will this buffer overrun? */
+ "", /* discard invalid unicode */
+ FLB_TRUE
+ },
+ { 0 }
+ };
+
+ write_str_test_cases(cases);
+}
+
+void test_write_str_buffer_overrun()
+{
+ struct write_str_case cases[] = {
+ {
+ "aa""\x81", 3,
+ "aa"
+ "\xee\x82\x81", /* just enough space for 81 fragment */
+ FLB_TRUE
+ },
+ {
+ "aaa""\x81", 4, /* out buffer size: 5, needed bytes: 2 + 3 + 3 = 8 */
+ "aaa",
+ /* "\xee\x82\x81", */ /* 81 fragment -- would overrun */
+ FLB_FALSE
+ },
+ {
+ "aaa"
+ "\xe3\x81\x82", 6, /* required is already grater than buffer */
+ "",
+ FLB_FALSE
+ },
+ {
+ "\""
+ "\xe3\x81\x82", 4, /* valid unicode */
+ "\\\"""\xe3\x81\x82", /* just enough space for valid unicode */
+ FLB_TRUE
+ },
+ {
+ "\x81"
+ "\xe3\x81\x82", 4, /* valid unicode */
+ "\xee\x82\x81", /* 81 fragment */
+ /* not enough space for valid unicode fragment "\xe3\x81\x82" */
+ FLB_FALSE
+ },
+ { 0 }
+ };
+ write_str_test_cases_w_buf_size(cases, 5);
+}
+
+struct proxy_url_check {
+ int ret;
+ char *url; /* full URL */
+ char *prot; /* expected protocol */
+ char *host; /* expected host */
+ char *port; /* expected port */
+ char *username; /* expected username */
+ char *password; /* expected password */
+};
+
+struct proxy_url_check proxy_url_checks[] = {
+ {0, "http://foo:bar@proxy.com:8080",
+ "http", "proxy.com", "8080", "foo", "bar"},
+ {0, "http://proxy.com",
+ "http", "proxy.com", "80", NULL, NULL},
+ {0, "http://proxy.com:8080",
+ "http", "proxy.com", "8080", NULL, NULL},
+ /* issue #5530. Password contains @ */
+ {0, "http://example_user:example_pass_w_@_char@proxy.com:8080",
+ "http", "proxy.com", "8080", "example_user", "example_pass_w_@_char"},
+ {-1, "https://proxy.com:8080",
+ NULL, NULL, NULL, NULL, NULL}
+
+};
+
+void test_proxy_url_split() {
+ int i;
+ int ret;
+ int size;
+ char *protocol;
+ char *host;
+ char *port;
+ char *username;
+ char *password;
+ struct proxy_url_check *u;
+
+ size = sizeof(proxy_url_checks) / sizeof(struct proxy_url_check);
+ for (i = 0; i < size; i++) {
+ u = &proxy_url_checks[i];
+
+ protocol = NULL;
+ host = NULL;
+ port = NULL;
+ username = NULL;
+ password = NULL;
+
+ ret = flb_utils_proxy_url_split(u->url, &protocol, &username, &password, &host, &port);
+ TEST_CHECK(ret == u->ret);
+ if (ret == -1) {
+ continue;
+ }
+
+ /* Protocol */
+ TEST_CHECK(protocol != NULL);
+ ret = strcmp(u->prot, protocol);
+ TEST_CHECK(ret == 0);
+ TEST_MSG("Expected protocol: %s", u->prot);
+ TEST_MSG("Produced protocol: %s", protocol);
+
+ /* Host */
+ TEST_CHECK(host != NULL);
+ ret = strcmp(u->host, host);
+ TEST_CHECK(ret == 0);
+ TEST_MSG("Expected host: %s", u->host);
+ TEST_MSG("Produced host: %s", host);
+
+ /* Port */
+ TEST_CHECK(port != NULL);
+ ret = strcmp(u->port, port);
+ TEST_CHECK(ret == 0);
+ TEST_MSG("Expected port: %s", u->port);
+ TEST_MSG("Produced port: %s", port);
+
+ /* Username */
+ if (u->username) {
+ TEST_CHECK(port != NULL);
+ ret = strcmp(u->port, port);
+ TEST_CHECK(ret == 0);
+ TEST_MSG("Expected username: %s", u->username);
+ TEST_MSG("Produced username: %s", username);
+
+ }
+ else {
+ TEST_CHECK(username == NULL);
+ }
+
+ /* Password */
+ if (u->password) {
+ TEST_CHECK(port != NULL);
+ ret = strcmp(u->port, port);
+ TEST_CHECK(ret == 0);
+ TEST_MSG("Expected password: %s", u->password);
+ TEST_MSG("Produced password: %s", password);
+ }
+ else {
+ TEST_CHECK(password == NULL);
+ }
+
+ if (protocol) {
+ flb_free(protocol);
+ }
+ if (host) {
+ flb_free(host);
+ }
+ if (port) {
+ flb_free(port);
+ }
+ if (username) {
+ flb_free(username);
+ }
+ if (password) {
+ flb_free(password);
+ }
+ }
+}
+
+static int compare_split_entry(const char* input, int separator, int max_split, int quoted, ...)
+{
+ va_list ap;
+ int count = 1;
+ char *expect;
+ struct mk_list *split = NULL;
+ struct mk_list *tmp_list = NULL;
+ struct mk_list *head = NULL;
+ struct flb_split_entry *entry = NULL;
+
+ if (quoted) {
+ split = flb_utils_split_quoted(input, separator, max_split);
+ }
+ else {
+ split = flb_utils_split(input, separator, max_split);
+ }
+
+ if (!TEST_CHECK(split != NULL)) {
+ TEST_MSG("flb_utils_split failed. input=%s", input);
+ return -1;
+ }
+ if (!TEST_CHECK(mk_list_is_empty(split) != 0)) {
+ TEST_MSG("list is empty. input=%s", input);
+ return -1;
+ }
+
+ va_start(ap, quoted);
+ mk_list_foreach_safe(head, tmp_list, split) {
+ if (max_split > 0 && !TEST_CHECK(count <= max_split) ) {
+ TEST_MSG("count error. got=%d expect=%d input=%s", count, max_split, input);
+ }
+
+ expect = va_arg(ap, char*);
+ entry = mk_list_entry(head, struct flb_split_entry, _head);
+ if (!TEST_CHECK(entry != NULL)) {
+ TEST_MSG("entry is NULL. input=%s", input);
+ goto comp_end;
+ }
+ /*
+ printf("%d:%s\n", count, entry->value);
+ */
+ if (!TEST_CHECK(strcmp(expect, entry->value) == 0)) {
+ TEST_MSG("mismatch. got=%s expect=%s. input=%s", entry->value, expect, input);
+ goto comp_end;
+ }
+ count++;
+ }
+ comp_end:
+ if (split != NULL) {
+ flb_utils_split_free(split);
+ }
+ va_end(ap);
+ return 0;
+}
+
+void test_flb_utils_split()
+{
+ compare_split_entry("aa,bb", ',', 2, FLB_FALSE, "aa","bb" );
+ compare_split_entry("localhost:12345", ':', 2, FLB_FALSE, "localhost","12345" );
+ compare_split_entry("https://fluentbit.io/announcements/", '/', -1, FLB_FALSE, "https:", "fluentbit.io","announcements" );
+
+ /* /proc/net/dev example */
+ compare_split_entry("enp0s3: 1955136 1768 0 0 0 0 0 0 89362 931 0 0 0 0 0 0",
+ ' ', 256, FLB_FALSE,
+ "enp0s3:", "1955136", "1768", "0", "0", "0", "0", "0", "0", "89362", "931", "0", "0", "0", "0", "0", "0", "0");
+
+ /* filter_grep configuration */
+ compare_split_entry("Regex test *a*", ' ', 3, FLB_FALSE, "Regex", "test", "*a*");
+
+ /* filter_modify configuration */
+ compare_split_entry("Condition Key_Value_Does_Not_Equal cpustats KNOWN", ' ', 4,
+ FLB_FALSE, "Condition", "Key_Value_Does_Not_Equal", "cpustats", "KNOWN");
+
+ /* nginx_exporter_metrics example */
+ compare_split_entry("Active connections: 1\nserver accepts handled requests\n 10 10 10\nReading: 0 Writing: 1 Waiting: 0", '\n', 4,
+ FLB_FALSE, "Active connections: 1", "server accepts handled requests", " 10 10 10","Reading: 0 Writing: 1 Waiting: 0");
+
+ /* out_cloudwatch_logs example */
+ compare_split_entry("dimension_1,dimension_2;dimension_3", ';', 256,
+ FLB_FALSE, "dimension_1,dimension_2", "dimension_3");
+ /* separator is not contained */
+ compare_split_entry("aa,bb", '/', 2, FLB_FALSE, "aa,bb");
+
+ /* do not parse quotes when tokenizing */
+ compare_split_entry("aa \"bb cc\" dd", ' ', 256, FLB_FALSE, "aa", "\"bb", "cc\"", "dd");
+}
+
+void test_flb_utils_split_quoted()
+{
+ /* Tokens quoted with "..." */
+ compare_split_entry("aa \"double quote\" bb", ' ', 256, FLB_TRUE, "aa", "double quote", "bb");
+ compare_split_entry("\"begin with double quote\" aa", ' ', 256, FLB_TRUE, "begin with double quote", "aa");
+ compare_split_entry("aa \"end with double quote\"", ' ', 256, FLB_TRUE, "aa", "end with double quote");
+
+ /* Tokens quoted with '...' */
+ compare_split_entry("aa bb 'single quote' cc", ' ', 256, FLB_TRUE, "aa", "bb", "single quote", "cc");
+ compare_split_entry("'begin with single quote' aa", ' ', 256, FLB_TRUE, "begin with single quote", "aa");
+ compare_split_entry("aa 'end with single quote'", ' ', 256, FLB_TRUE, "aa", "end with single quote");
+
+ /* Tokens surrounded by more than one separator character */
+ compare_split_entry(" aa \" spaces bb \" cc ' spaces dd ' ff", ' ', 256, FLB_TRUE,
+ "aa", " spaces bb ", "cc", " spaces dd ", "ff");
+
+ /* Escapes within quoted token */
+ compare_split_entry("aa \"escaped \\\" quote\" bb", ' ', 256, FLB_TRUE, "aa", "escaped \" quote", "bb");
+ compare_split_entry("aa 'escaped \\' quote\' bb", ' ', 256, FLB_TRUE, "aa", "escaped \' quote", "bb");
+ compare_split_entry("aa \"\\\"escaped balanced quotes\\\"\" bb", ' ', 256, FLB_TRUE,
+ "aa", "\"escaped balanced quotes\"", "bb");
+ compare_split_entry("aa '\\'escaped balanced quotes\\'\' bb", ' ', 256, FLB_TRUE,
+ "aa", "'escaped balanced quotes'", "bb");
+ compare_split_entry("aa 'escaped \\\\ escape\' bb", ' ', 256, FLB_TRUE, "aa", "escaped \\ escape", "bb");
+
+ /* Escapes that are not processed */
+ compare_split_entry("\\\"aa bb", ' ', 256, FLB_TRUE, "\\\"aa", "bb");
+ compare_split_entry("\\'aa bb", ' ', 256, FLB_TRUE, "\\'aa", "bb");
+ compare_split_entry("\\\\aa bb", ' ', 256, FLB_TRUE, "\\\\aa", "bb");
+ compare_split_entry("aa\\ bb", ' ', 256, FLB_TRUE, "aa\\", "bb");
+
+}
+
+void test_flb_utils_split_quoted_errors()
+{
+ struct mk_list *split = NULL;
+
+ split = flb_utils_split_quoted("aa \"unbalanced quotes should fail", ' ', 256);
+ TEST_CHECK(split == NULL);
+ split = flb_utils_split_quoted("aa 'unbalanced quotes should fail", ' ', 256);
+ TEST_CHECK(split == NULL);
+}
+
+TEST_LIST = {
+ /* JSON maps iteration */
+ { "url_split", test_url_split },
+ { "write_str", test_write_str },
+ { "test_write_str_invalid_trailing_bytes", test_write_str_invalid_trailing_bytes },
+ { "test_write_str_invalid_leading_byte", test_write_str_invalid_leading_byte },
+ { "test_write_str_edge_cases", test_write_str_edge_cases },
+ { "test_write_str_invalid_leading_byte_case_2", test_write_str_invalid_leading_byte_case_2 },
+ { "test_write_str_buffer_overrun", test_write_str_buffer_overrun },
+ { "proxy_url_split", test_proxy_url_split },
+ { "test_flb_utils_split", test_flb_utils_split },
+ { "test_flb_utils_split_quoted", test_flb_utils_split_quoted},
+ { "test_flb_utils_split_quoted_errors", test_flb_utils_split_quoted_errors},
+ { 0 }
+};