From 215e383be1f47cd18c235855d0cee0485f6cb423 Mon Sep 17 00:00:00 2001 From: lijia Date: Mon, 8 Apr 2024 09:48:13 +0800 Subject: Separate from stellar-on-sapp project. --- .gitignore | 8 + .gitlab-ci.yml | 241 ++ CMakeLists.txt | 92 + autorevision.sh | 1260 +++++++++++ ci/.gitkeep | 0 ci/get-nprocessors.sh | 48 + ci/travis.sh | 49 + cmake/.gitkeep | 0 cmake/Package.cmake | 43 + cmake/PostInstall.in | 1 + cmake/PostUninstall.in | 1 + cmake/PreInstall.in | 22 + cmake/PreUninstall.in | 6 + cmake/Version.cmake | 47 + conf/.gitkeep | 0 conf/http_decoder.toml | 15 + deps/.gitkeep | 0 deps/mempool/nmx_alloc.c | 44 + deps/mempool/nmx_alloc.h | 16 + deps/mempool/nmx_palloc.c | 262 +++ deps/mempool/nmx_palloc.h | 58 + deps/toml/CMakeLists.txt | 3 + deps/toml/toml.c | 2379 ++++++++++++++++++++ deps/toml/toml.h | 175 ++ deps/uthash/utarray.h | 248 ++ deps/uthash/uthash.h | 1138 ++++++++++ deps/uthash/utlist.h | 1073 +++++++++ deps/uthash/utringbuffer.h | 108 + deps/uthash/utstack.h | 88 + deps/uthash/utstring.h | 407 ++++ include/http_decoder.h | 118 + readme.md | 3 +- src/.gitkeep | 0 src/CMakeLists.txt | 18 + src/http_content_decompress.c | 268 +++ src/http_content_decompress.h | 52 + src/http_decoder.c | 858 +++++++ src/http_decoder_half.c | 1034 +++++++++ src/http_decoder_half.h | 119 + src/http_decoder_inc.h | 53 + src/http_decoder_result_queue.c | 172 ++ src/http_decoder_result_queue.h | 73 + src/http_decoder_string.c | 277 +++ src/http_decoder_string.h | 95 + src/http_decoder_table.c | 556 +++++ src/http_decoder_table.h | 99 + src/http_decoder_utils.c | 25 + src/http_decoder_utils.h | 66 + src/version.map | 9 + test/.gitkeep | 0 test/CMakeLists.txt | 149 ++ test/http_decoder_driver.cpp | 923 ++++++++ test/http_decoder_gtest.cpp | 370 +++ test/http_decoder_gtest.h | 114 + test/http_decoder_stub.cpp | 250 ++ test/http_pcap/http_6over4_single_trans.pcap | Bin 0 -> 1424 bytes test/http_pcap/http_chunked_res_gzip.pcap | Bin 0 -> 29517 bytes test/http_pcap/http_connect_flood.pcap | Bin 0 -> 31672 bytes test/http_pcap/http_get_encoded_uri.pcap | Bin 0 -> 35174 bytes test/http_pcap/http_get_long_cookie.pcap | Bin 0 -> 4489 bytes test/http_pcap/http_get_malformed.pcap | Bin 0 -> 2737 bytes test/http_pcap/http_get_multi_trans.pcap | Bin 0 -> 35841 bytes test/http_pcap/http_get_req_pipeline.pcap | Bin 0 -> 2251 bytes test/http_pcap/http_get_single_trans.pcap | Bin 0 -> 1359 bytes test/http_pcap/http_hdr_truncated_after_kv.pcap | Bin 0 -> 155839 bytes test/http_pcap/http_hdr_truncated_in_kv.pcap | Bin 0 -> 5493 bytes test/http_pcap/http_hdr_value_empty.pcap | Bin 0 -> 1718 bytes test/http_pcap/http_hdrs_exceed_maximum.pcap | Bin 0 -> 5029 bytes test/http_pcap/http_multi_parse_error.pcap | Bin 0 -> 1980 bytes test/http_pcap/http_no_content_length.pcap | Bin 0 -> 1944 bytes test/http_pcap/http_over_pppoe.pcap | Bin 0 -> 1991 bytes test/http_pcap/http_over_tcp_keepalive.pcap | Bin 0 -> 20213 bytes test/http_pcap/http_over_tls.pcap | Bin 0 -> 232820 bytes test/http_pcap/http_post_multipart_form_data.pcap | Bin 0 -> 4824 bytes test/http_pcap/http_req_1byte_sliding_window.pcap | Bin 0 -> 31724 bytes test/http_pcap/http_res_1byte_sliding_window.pcap | Bin 0 -> 55449 bytes test/http_pcap/http_res_gzip.pcap | Bin 0 -> 1707 bytes test/http_pcap/http_trans_pipeline.pcap | Bin 0 -> 6832 bytes test/http_pcap/http_tunnel_for_pop3.pcap | Bin 0 -> 3673 bytes test/http_pcap/http_upgrade_http2.pcap | Bin 0 -> 1617 bytes test/http_pcap/http_upgrade_websocket.pcap | Bin 0 -> 7851 bytes test/http_pcap/http_url_test_with_host.pcap | Bin 0 -> 1362 bytes test/http_pcap/http_url_test_without_host.pcap | Bin 0 -> 1624 bytes test/http_pcap/non_http.pcap | Bin 0 -> 27850 bytes test/md5.c | 356 +++ test/md5.h | 70 + test/test_env/conflist.inf | 9 + test/test_env/sapp4.el8.x86_64.rpm | Bin 0 -> 1052968 bytes test/test_env/spec.toml | 11 + test/test_env/start_loader.inf | 17 + test/test_env/tsg_l7_protocol.conf | 57 + .../test_result_json/http_6over4_single_trans.json | 33 + test/test_result_json/http_chunked_res_gzip.json | 41 + test/test_result_json/http_connect_flood.json | 686 ++++++ test/test_result_json/http_get_encoded_uri.json | 71 + test/test_result_json/http_get_long_cookie.json | 24 + test/test_result_json/http_get_malformed.json | 28 + test/test_result_json/http_get_multi_trans.json | 139 ++ test/test_result_json/http_get_req_pipeline.json | 67 + test/test_result_json/http_get_single_trans.json | 29 + .../http_hdr_truncated_after_kv.json | 285 +++ .../test_result_json/http_hdr_truncated_in_kv.json | 53 + test/test_result_json/http_hdr_value_empty.json | 35 + .../test_result_json/http_hdrs_exceed_maximum.json | 46 + test/test_result_json/http_multi_parse_error.json | 35 + test/test_result_json/http_no_content_length.json | 45 + test/test_result_json/http_over_pppoe.json | 35 + test/test_result_json/http_over_tcp_keepalive.json | 40 + test/test_result_json/http_over_tls.json | 2 + .../http_post_multipart_form_data.json | 101 + .../http_req_1byte_sliding_window.json | 34 + .../http_res_1byte_sliding_window.json | 34 + test/test_result_json/http_res_gzip.json | 44 + test/test_result_json/http_trans_pipeline.json | 368 +++ test/test_result_json/http_tunnel_for_pop3.json | 33 + test/test_result_json/http_upgrade_http2.json | 42 + test/test_result_json/http_upgrade_websocket.json | 42 + test/test_result_json/http_url_test_with_host.json | 35 + .../http_url_test_without_host.json | 29 + test/test_result_json/non_http.json | 1 + vendor/.gitkeep | 0 vendor/CMakeLists.txt | 53 + vendor/googletest-release-1.8.0.tar.gz | Bin 0 -> 1281617 bytes vendor/libcjson_v1.7.17.tar.gz | Bin 0 -> 354666 bytes vendor/llhttp-release-v9.1.3.tar.gz | Bin 0 -> 37771 bytes 125 files changed, 16562 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml create mode 100644 CMakeLists.txt create mode 100644 autorevision.sh create mode 100644 ci/.gitkeep create mode 100644 ci/get-nprocessors.sh create mode 100644 ci/travis.sh create mode 100644 cmake/.gitkeep create mode 100644 cmake/Package.cmake create mode 100644 cmake/PostInstall.in create mode 100644 cmake/PostUninstall.in create mode 100644 cmake/PreInstall.in create mode 100644 cmake/PreUninstall.in create mode 100644 cmake/Version.cmake create mode 100644 conf/.gitkeep create mode 100644 conf/http_decoder.toml create mode 100644 deps/.gitkeep create mode 100644 deps/mempool/nmx_alloc.c create mode 100644 deps/mempool/nmx_alloc.h create mode 100644 deps/mempool/nmx_palloc.c create mode 100644 deps/mempool/nmx_palloc.h create mode 100644 deps/toml/CMakeLists.txt create mode 100644 deps/toml/toml.c create mode 100644 deps/toml/toml.h create mode 100644 deps/uthash/utarray.h create mode 100644 deps/uthash/uthash.h create mode 100644 deps/uthash/utlist.h create mode 100644 deps/uthash/utringbuffer.h create mode 100644 deps/uthash/utstack.h create mode 100644 deps/uthash/utstring.h create mode 100644 include/http_decoder.h create mode 100644 src/.gitkeep create mode 100644 src/CMakeLists.txt create mode 100644 src/http_content_decompress.c create mode 100644 src/http_content_decompress.h create mode 100644 src/http_decoder.c create mode 100644 src/http_decoder_half.c create mode 100644 src/http_decoder_half.h create mode 100644 src/http_decoder_inc.h create mode 100644 src/http_decoder_result_queue.c create mode 100644 src/http_decoder_result_queue.h create mode 100644 src/http_decoder_string.c create mode 100644 src/http_decoder_string.h create mode 100644 src/http_decoder_table.c create mode 100644 src/http_decoder_table.h create mode 100644 src/http_decoder_utils.c create mode 100644 src/http_decoder_utils.h create mode 100644 src/version.map create mode 100644 test/.gitkeep create mode 100644 test/CMakeLists.txt create mode 100644 test/http_decoder_driver.cpp create mode 100644 test/http_decoder_gtest.cpp create mode 100644 test/http_decoder_gtest.h create mode 100644 test/http_decoder_stub.cpp create mode 100644 test/http_pcap/http_6over4_single_trans.pcap create mode 100644 test/http_pcap/http_chunked_res_gzip.pcap create mode 100644 test/http_pcap/http_connect_flood.pcap create mode 100644 test/http_pcap/http_get_encoded_uri.pcap create mode 100644 test/http_pcap/http_get_long_cookie.pcap create mode 100644 test/http_pcap/http_get_malformed.pcap create mode 100644 test/http_pcap/http_get_multi_trans.pcap create mode 100644 test/http_pcap/http_get_req_pipeline.pcap create mode 100644 test/http_pcap/http_get_single_trans.pcap create mode 100644 test/http_pcap/http_hdr_truncated_after_kv.pcap create mode 100644 test/http_pcap/http_hdr_truncated_in_kv.pcap create mode 100644 test/http_pcap/http_hdr_value_empty.pcap create mode 100644 test/http_pcap/http_hdrs_exceed_maximum.pcap create mode 100644 test/http_pcap/http_multi_parse_error.pcap create mode 100644 test/http_pcap/http_no_content_length.pcap create mode 100644 test/http_pcap/http_over_pppoe.pcap create mode 100644 test/http_pcap/http_over_tcp_keepalive.pcap create mode 100644 test/http_pcap/http_over_tls.pcap create mode 100644 test/http_pcap/http_post_multipart_form_data.pcap create mode 100644 test/http_pcap/http_req_1byte_sliding_window.pcap create mode 100644 test/http_pcap/http_res_1byte_sliding_window.pcap create mode 100644 test/http_pcap/http_res_gzip.pcap create mode 100644 test/http_pcap/http_trans_pipeline.pcap create mode 100644 test/http_pcap/http_tunnel_for_pop3.pcap create mode 100644 test/http_pcap/http_upgrade_http2.pcap create mode 100644 test/http_pcap/http_upgrade_websocket.pcap create mode 100644 test/http_pcap/http_url_test_with_host.pcap create mode 100644 test/http_pcap/http_url_test_without_host.pcap create mode 100644 test/http_pcap/non_http.pcap create mode 100644 test/md5.c create mode 100644 test/md5.h create mode 100644 test/test_env/conflist.inf create mode 100644 test/test_env/sapp4.el8.x86_64.rpm create mode 100644 test/test_env/spec.toml create mode 100644 test/test_env/start_loader.inf create mode 100644 test/test_env/tsg_l7_protocol.conf create mode 100644 test/test_result_json/http_6over4_single_trans.json create mode 100644 test/test_result_json/http_chunked_res_gzip.json create mode 100644 test/test_result_json/http_connect_flood.json create mode 100644 test/test_result_json/http_get_encoded_uri.json create mode 100644 test/test_result_json/http_get_long_cookie.json create mode 100644 test/test_result_json/http_get_malformed.json create mode 100644 test/test_result_json/http_get_multi_trans.json create mode 100644 test/test_result_json/http_get_req_pipeline.json create mode 100644 test/test_result_json/http_get_single_trans.json create mode 100644 test/test_result_json/http_hdr_truncated_after_kv.json create mode 100644 test/test_result_json/http_hdr_truncated_in_kv.json create mode 100644 test/test_result_json/http_hdr_value_empty.json create mode 100644 test/test_result_json/http_hdrs_exceed_maximum.json create mode 100644 test/test_result_json/http_multi_parse_error.json create mode 100644 test/test_result_json/http_no_content_length.json create mode 100644 test/test_result_json/http_over_pppoe.json create mode 100644 test/test_result_json/http_over_tcp_keepalive.json create mode 100644 test/test_result_json/http_over_tls.json create mode 100644 test/test_result_json/http_post_multipart_form_data.json create mode 100644 test/test_result_json/http_req_1byte_sliding_window.json create mode 100644 test/test_result_json/http_res_1byte_sliding_window.json create mode 100644 test/test_result_json/http_res_gzip.json create mode 100644 test/test_result_json/http_trans_pipeline.json create mode 100644 test/test_result_json/http_tunnel_for_pop3.json create mode 100644 test/test_result_json/http_upgrade_http2.json create mode 100644 test/test_result_json/http_upgrade_websocket.json create mode 100644 test/test_result_json/http_url_test_with_host.json create mode 100644 test/test_result_json/http_url_test_without_host.json create mode 100644 test/test_result_json/non_http.json create mode 100644 vendor/.gitkeep create mode 100644 vendor/CMakeLists.txt create mode 100644 vendor/googletest-release-1.8.0.tar.gz create mode 100644 vendor/libcjson_v1.7.17.tar.gz create mode 100644 vendor/llhttp-release-v9.1.3.tar.gz diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fe44c80 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.idea/ +.vscode +build/ +build7 +build8 +cmake-build* +.VSCodeCounter +.cache diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..010d649 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,241 @@ +variables: + GIT_STRATEGY: "clone" + BUILD_PADDING_PREFIX: /tmp/padding_for_CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX_PREFIX_PREFIX_PREFIX_PREFIX_PREFIX/ + BUILD_IMAGE_CENTOS7: "git.mesalab.cn:7443/mesa_platform/build-env:master" + BUILD_IMAGE_CENTOS8: "git.mesalab.cn:7443/mesa_platform/build-env:rockylinux" + INSTALL_DEPENDENCY_LIBRARY: sapp-devel framework_env libMESA_prof_load-devel + libMESA_htable-devel libMESA_jump_layer libMESA_jump_layer-devel + libMESA_handle_logger-devel libMESA_field_stat2-devel + libfieldstat3-devel libfieldstat4-devel libbreakpad_mini-devel + zlib-devel brotli brotli-devel stellar-c-devel + SYMBOL_TARGET: http_decoder + TEST_NAME: gtest_http_decoder + INSTALL_PREFIX: "/opt/tsg/" + +stages: +- cppcheck +- build +- test +- upload + +.everything_before_script: &everything_before_script + - mkdir -p $BUILD_PADDING_PREFIX/$CI_PROJECT_NAMESPACE/ + - ln -s $CI_PROJECT_DIR $BUILD_PADDING_PREFIX/$CI_PROJECT_PATH + - cd $BUILD_PADDING_PREFIX/$CI_PROJECT_PATH + - chmod +x ./ci/travis.sh + - yum makecache #--disablerepo="*" --enablerepo="framework,platform,protocol,stellar" + - yum install -y $INSTALL_DEPENDENCY_LIBRARY + - source /etc/profile.d/framework.sh +############################################################################### +# cppcheck +############################################################################### +.cppcheck_script: + variables: + BUILD_TYPE: Debug + stage: cppcheck + script: + - mkdir build; cd build; cmake3 -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .. + - > + cppcheck --project=compile_commands.json + --enable=all + --error-exitcode=1 + --suppress=unusedFunction + --suppress=missingInclude + --suppress=uselessAssignmentPtrArg + --suppress=unmatchedSuppression + --suppress=variableScope + --suppress=unreadVariable + --suppress=cstyleCast + --suppress=memleakOnRealloc + --suppress=constParameter + --suppress=uselessAssignmentArg + --suppress=uninitvar + --suppress=unusedStructMember + tags: + - share + +run_cppcheck_for_centos7: + extends: .cppcheck_script + image: $BUILD_IMAGE_CENTOS7 + +run_cppcheck_for_centos8: + extends: .cppcheck_script + image: $BUILD_IMAGE_CENTOS8 + +############################################################################### +# build +############################################################################### + +.build_before_script: + before_script: *everything_before_script + script: + - ./ci/travis.sh + variables: + BUILD_TEST: "ON" + BUILD_TYPE: Debug + tags: + - share + +.build_by_travis_for_centos7: + extends: .build_before_script + stage: build + image: $BUILD_IMAGE_CENTOS7 + + +develop_build_for_centos7: + extends: .build_by_travis_for_centos7 + variables: + BUILD_TYPE: RelWithDebInfo + artifacts: + name: "$SYMBOL_TARGET-$CI_COMMIT_REF_NAME-debug" + paths: + - build/* + except: + - tags + +release_build_debug_for_centos7: + extends: .build_by_travis_for_centos7 + variables: + BUILD_TYPE: Debug + PACKAGE: 1 + ASAN_OPTION: "ADDRESS" + script: + - source /opt/rh/devtoolset-7/enable || true + - ./ci/travis.sh + artifacts: + name: "$SYMBOL_TARGET-$CI_COMMIT_REF_NAME-release" + paths: + - build/* + only: + - tags + +release_build_for_centos7: + extends: .build_by_travis_for_centos7 + variables: + BUILD_TYPE: RelWithDebInfo + PACKAGE: 1 + artifacts: + name: "$SYMBOL_TARGET-$CI_COMMIT_REF_NAME-release" + paths: + - build/* + only: + - tags + +.build_by_travis_for_centos8: + stage: build + image: $BUILD_IMAGE_CENTOS8 + extends: .build_before_script + +develop_build_for_centos8: + extends: .build_by_travis_for_centos8 + variables: + BUILD_TYPE: RelWithDebInfo + artifacts: + name: "$SYMBOL_TARGET-$CI_COMMIT_REF_NAME-debug" + paths: + - build/* + except: + - tags + +release_build_debug_for_centos8: + extends: .build_by_travis_for_centos8 + variables: + BUILD_TYPE: Debug + PACKAGE: 1 + ASAN_OPTION: "ADDRESS" + artifacts: + name: "$SYMBOL_TARGET-$CI_COMMIT_REF_NAME-release" + paths: + - build/* + only: + - tags + +release_build_for_centos8: + extends: .build_by_travis_for_centos8 + variables: + BUILD_TYPE: RelWithDebInfo + PACKAGE: 1 + artifacts: + name: "$SYMBOL_TARGET-$CI_COMMIT_REF_NAME-release" + paths: + - build/* + only: + - tags + +############################################################################### +# test +############################################################################### +test_in_centos7: + stage: test + image: $BUILD_IMAGE_CENTOS7 + allow_failure: false + script: + - *everything_before_script + - ls -l /opt/MESA/lib && echo "/opt/MESA/lib" >> /etc/ld.so.conf + - cd build; make test + dependencies: + - develop_build_for_centos7 + - release_build_for_centos7 + tags: + - share + +test_in_centos8: + stage: test + image: $BUILD_IMAGE_CENTOS8 + allow_failure: false + script: + - *everything_before_script + - ls -l /opt/MESA/lib && echo "/opt/MESA/lib" >> /etc/ld.so.conf + - cd build; make test + dependencies: + - develop_build_for_centos8 + - release_build_for_centos8 + tags: + - share + +############################################################################### +# upload +############################################################################### +.define_before_upload_centos7: + stage: upload + image: $BUILD_IMAGE_CENTOS7 + before_script: + - pwd; ls -l ; cd build ; ls -l + - cp /root/rpm_upload_tools.py ./ + variables: + PULP3_REPO_NAME: protocol-stable-x86_64.el7 + PULP3_DIST_NAME: protocol-stable-x86_64.el7 + only: + - tags + tags: + - share + +rpm_upload_for_centos7: + extends: .define_before_upload_centos7 + dependencies: + - release_build_debug_for_centos7 + - release_build_for_centos7 + script: + - python3 rpm_upload_tools.py $PULP3_REPO_NAME $PULP3_DIST_NAME *.rpm + +.define_before_upload_centos8: + stage: upload + image: $BUILD_IMAGE_CENTOS8 + before_script: + - pwd; ls -l ; cd build ; ls -l + - cp /root/rpm_upload_tools.py ./ + variables: + PULP3_REPO_NAME: protocol-stable-x86_64.el8 + PULP3_DIST_NAME: protocol-stable-x86_64.el8 + only: + - tags + tags: + - share + +rpm_upload_for_centos8: + extends: .define_before_upload_centos8 + dependencies: + - release_build_debug_for_centos8 + - release_build_for_centos8 + script: + - python3 rpm_upload_tools.py $PULP3_REPO_NAME $PULP3_DIST_NAME *.rpm diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a3e100c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,92 @@ +cmake_minimum_required(VERSION 3.12) +set(lib_name http_decoder) + +project (${lib_name}) + + +set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) + +include(Version) + +add_definitions(-D_GNU_SOURCE) +add_definitions(-D__USE_MISC) +add_definitions(-D__FAVOR_BSD) +add_definitions(-D__USE_BSD) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_C_STANDARD 11) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE RelWithDebInfo) +endif() + +if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set (CMAKE_INSTALL_PREFIX "/opt/tsg/" CACHE PATH "default install path" FORCE) +endif() + +LINK_DIRECTORIES(/opt/MESA/lib /usr/lib64) + +find_program(CMAKE_CXX_CPPCHECK NAMES cppcheck) +if (CMAKE_CXX_CPPCHECK) + list( + APPEND CMAKE_CXX_CPPCHECK + "--enable=all" + "--error-exitcode=1" + "--suppress=unusedFunction" + "--suppress=missingInclude" + "--suppress=uselessAssignmentPtrArg" + "--suppress=unmatchedSuppression" + "--suppress=variableScope" + "--suppress=unreadVariable" + "--suppress=cstyleCast" + "--suppress=memleakOnRealloc" + "--suppress=constParameter" + "--suppress=uselessAssignmentArg" + "--suppress=uninitvar" + "--suppress=unusedStructMember" + "--suppress=unreachableCode" + "--suppress=internalAstError" + "--suppress=nullPointerRedundantCheck" + "--suppress=ctunullpointer" + "--suppress=redundantAssignment" + "--suppress=duplicateValueTernary" + ) + set(CMAKE_C_CPPCHECK ${CMAKE_CXX_CPPCHECK}) +else() + message(FATAL_ERROR "Could not find the program cppcheck.") +endif() + +#ASAN option +set(ASAN_OPTION "OFF" CACHE STRING + " set asan type chosen by the user, using OFF as default") +set_property(CACHE ASAN_OPTION PROPERTY STRINGS OFF ADDRESS THREAD) +message(STATUS "ASAN_OPTION='${ASAN_OPTION}'") + +if(ASAN_OPTION STREQUAL "ADDRESS") + set(CMAKE_C_FLAGS "${CMAKADDRESS} -g -DCMAKE_BUILD_TYPE=Debug -fsanitize=address -fno-omit-frame-pointer") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -DCMAKE_BUILD_TYPE=Debug -fsanitize=address -fno-omit-frame-pointer") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lasan") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lasan") + add_definitions(-DASAN_ENABLED=1) +elseif(ASAN_OPTION STREQUAL "THREAD") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -DCMAKE_BUILD_TYPE=Debug -fsanitize=thread -fno-omit-frame-pointer") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -DCMAKE_BUILD_TYPE=Debug -fsanitize=thread -fno-omit-frame-pointer") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lasan") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lasan") + add_definitions(-DASAN_ENABLED=1) +endif() + +include_directories(${CMAKE_SOURCE_DIR}) +include_directories(${CMAKE_SOURCE_DIR}/include) + +add_subdirectory(vendor) +add_subdirectory(src) + +enable_testing() +add_subdirectory(test) + +set(CPACK_RPM_LIBRARIES_USER_FILELIST "%config(noreplace) ${CMAKE_INSTALL_PREFIX}/etc/http/http_decoder.toml") + +install(FILES conf/http_decoder.toml DESTINATION ${CMAKE_INSTALL_PREFIX}/etc/http COMPONENT PROFILE) +install(FILES include/http_decoder.h DESTINATION ${CMAKE_INSTALL_PREFIX}/framework/include/http_decoder COMPONENT Headers) + +include(Package) \ No newline at end of file diff --git a/autorevision.sh b/autorevision.sh new file mode 100644 index 0000000..80d0712 --- /dev/null +++ b/autorevision.sh @@ -0,0 +1,1260 @@ +#!/bin/sh + +# Copyright (c) 2012 - 2016 dak180 and contributors. See +# https://opensource.org/licenses/mit-license.php or the included +# COPYING.md for licence terms. +# +# autorevision - extracts metadata about the head version from your +# repository. + +# Usage message. +arUsage() { + cat >"/dev/stderr" <&2 + exit 1 +elif [ -z "${VAROUT}" ] && [ -z "${AFILETYPE}" ]; then + # If neither -s or -t are specified: + arUsage +elif [ -z "${CACHEFILE}" ] && [ "${CACHEFORCE}" = "1" ]; then + # If -f is specified without -o: + arUsage +elif [ ! -f "${CACHEFILE}" ] && [ "${CACHEFORCE}" = "1" ]; then + # If we are forced to use the cache but it does not exist. + echo "error: Cache forced but no cache found." 1>&2 + exit 1 +fi + +# Make sure that the path we are given is one we can source +# (dash, we are looking at you). +if [ ! -z "${CACHEFILE}" ] && ! echo "${CACHEFILE}" | grep -q '^\.*/'; then + CACHEFILE="./${CACHEFILE}" +fi + +GENERATED_HEADER="Generated by autorevision - do not hand-hack!" + +# Functions to extract data from different repo types. +# For git repos +# shellcheck disable=SC2039,SC2164,SC2155 +gitRepo() { + local oldPath="${PWD}" + + cd "$(git rev-parse --show-toplevel)" + + VCS_TYPE="git" + + VCS_BASENAME="$(basename "${PWD}")" + + VCS_UUID="$(git rev-list --max-parents=0 --date-order --reverse HEAD 2>/dev/null | sed -n 1p)" + if [ -z "${VCS_UUID}" ]; then + VCS_UUID="$(git rev-list --topo-order HEAD | tail -n 1)" + fi + + # Is the working copy clean? + test -z "$(git status --untracked-files=normal --porcelain)" + VCS_WC_MODIFIED="${?}" + + # Enumeration of changesets + VCS_NUM="$(git rev-list --count HEAD 2>/dev/null)" + if [ -z "${VCS_NUM}" ]; then + echo "warning: Counting the number of revisions may be slower due to an outdated git version less than 1.7.2.3. If something breaks, please update it." 1>&2 + VCS_NUM="$(git rev-list HEAD | wc -l)" + fi + + # This may be a git-svn remote. If so, report the Subversion revision. + if [ -z "$(git config svn-remote.svn.url 2>/dev/null)" ]; then + # The full revision hash + VCS_FULL_HASH="$(git rev-parse HEAD)" + + # The short hash + VCS_SHORT_HASH="$(echo "${VCS_FULL_HASH}" | cut -b 1-7)" + else + # The git-svn revision number + VCS_FULL_HASH="$(git svn find-rev HEAD)" + VCS_SHORT_HASH="${VCS_FULL_HASH}" + fi + + # Current branch + VCS_BRANCH="$(git rev-parse --symbolic-full-name --verify "$(git name-rev --name-only --no-undefined HEAD 2>/dev/null)" 2>/dev/null | sed -e 's:refs/heads/::' | sed -e 's:refs/::')" + + # Cache the description + local DESCRIPTION="$(git describe --long --tags 2>/dev/null)" + + # Current or last tag ancestor (empty if no tags) + VCS_TAG="$(echo "${DESCRIPTION}" | sed -e "s:-g${VCS_SHORT_HASH}\$::" -e 's:-[0-9]*$::')" + + # Distance to last tag or an alias of VCS_NUM if there is no tag + if [ ! -z "${DESCRIPTION}" ]; then + VCS_TICK="$(echo "${DESCRIPTION}" | sed -e "s:${VCS_TAG}-::" -e "s:-g${VCS_SHORT_HASH}::")" + else + VCS_TICK="${VCS_NUM}" + fi + + # Date of the current commit + VCS_DATE="$(TZ=UTC git show -s --date=iso-strict-local --pretty=format:%ad | sed -e 's|+00:00|Z|')" + if [ -z "${VCS_DATE}" ]; then + echo "warning: Action stamps require git version 2.7+." 1>&2 + VCS_DATE="$(git log -1 --pretty=format:%ci | sed -e 's: :T:' -e 's: ::' -e 's|+00:00|Z|')" + local ASdis="1" + fi + + # Action Stamp + if [ -z "${ASdis}" ]; then + VCS_ACTION_STAMP="${VCS_DATE}!$(git show -s --pretty=format:%cE)" + else + VCS_ACTION_STAMP="" + fi + + cd "${oldPath}" +} + +# For hg repos +# shellcheck disable=SC2039,SC2164 +hgRepo() { + local oldPath="${PWD}" + + cd "$(hg root)" + + VCS_TYPE="hg" + + VCS_BASENAME="$(basename "${PWD}")" + + VCS_UUID="$(hg log -r "0" -l 1 --template '{node}\n')" + + # Is the working copy clean? + test -z "$(hg status -duram)" + VCS_WC_MODIFIED="${?}" + + # Enumeration of changesets + VCS_NUM="$(hg id -n | tr -d '+')" + + # The full revision hash + VCS_FULL_HASH="$(hg log -r "${VCS_NUM}" -l 1 --template '{node}\n')" + + # The short hash + VCS_SHORT_HASH="$(hg id -i | tr -d '+')" + + # Current bookmark (bookmarks are roughly equivalent to git's branches) + # or branch if no bookmark + VCS_BRANCH="$(hg id -B | cut -d ' ' -f 1)" + # Fall back to the branch if there are no bookmarks + if [ -z "${VCS_BRANCH}" ]; then + VCS_BRANCH="$(hg id -b)" + fi + + # Current or last tag ancestor (excluding auto tags, empty if no tags) + VCS_TAG="$(hg log -r "${VCS_NUM}" -l 1 --template '{latesttag}\n' 2>/dev/null | sed -e 's:qtip::' -e 's:tip::' -e 's:qbase::' -e 's:qparent::' -e "s:$(hg --config 'extensions.color=' --config 'extensions.mq=' --color never qtop 2>/dev/null)::" | cut -d ' ' -f 1)" + + # Distance to last tag or an alias of VCS_NUM if there is no tag + if [ ! -z "${VCS_TAG}" ]; then + VCS_TICK="$(hg log -r "${VCS_NUM}" -l 1 --template '{latesttagdistance}\n' 2>/dev/null)" + else + VCS_TICK="${VCS_NUM}" + fi + + # Date of the current commit + VCS_DATE="$(hg log -r "${VCS_NUM}" -l 1 --template '{date|isodatesec}\n' 2>/dev/null | sed -e 's: :T:' -e 's: ::' -e 's|+00:00|Z|')" + + # Action Stamp + VCS_ACTION_STAMP="$(TZ=UTC hg log -r "${VCS_NUM}" -l 1 --template '{date|localdate|rfc3339date}\n' 2>/dev/null | sed -e 's|+00:00|Z|')!$(hg log -r "${VCS_NUM}" -l 1 --template '{author|email}\n' 2>/dev/null)" + + cd "${oldPath}" +} + +# For bzr repos +# shellcheck disable=SC2039,SC2164 +bzrRepo() { + local oldPath="${PWD}" + + cd "$(bzr root)" + + VCS_TYPE="bzr" + + VCS_BASENAME="$(basename "${PWD}")" + + # Currently unimplemented because more investigation is needed. + VCS_UUID="" + + # Is the working copy clean? + bzr version-info --custom --template='{clean}\n' | grep -q '1' + VCS_WC_MODIFIED="${?}" + + # Enumeration of changesets + VCS_NUM="$(bzr revno)" + + # The full revision hash + VCS_FULL_HASH="$(bzr version-info --custom --template='{revision_id}\n')" + + # The short hash + VCS_SHORT_HASH="${VCS_NUM}" + + # Nick of the current branch + VCS_BRANCH="$(bzr nick)" + + # Current or last tag ancestor (excluding auto tags, empty if no tags) + VCS_TAG="$(bzr tags --sort=time | sed '/?$/d' | tail -n1 | cut -d ' ' -f1)" + + # Distance to last tag or an alias of VCS_NUM if there is no tag + if [ ! -z "${VCS_TAG}" ]; then + VCS_TICK="$(bzr log --line -r "tag:${VCS_TAG}.." | tail -n +2 | wc -l | sed -e 's:^ *::')" + else + VCS_TICK="${VCS_NUM}" + fi + + # Date of the current commit + VCS_DATE="$(bzr version-info --custom --template='{date}\n' | sed -e 's: :T:' -e 's: ::')" + + # Action Stamp + # Currently unimplemented because more investigation is needed. + VCS_ACTION_STAMP="" + + cd "${oldPath}" +} + +# For svn repos +# shellcheck disable=SC2039,SC2164,SC2155 +svnRepo() { + local oldPath="${PWD}" + + VCS_TYPE="svn" + + case "${PWD}" in + /*trunk* | /*branches* | /*tags*) + local fn="${PWD}" + while [ "$(basename "${fn}")" != 'trunk' ] && [ "$(basename "${fn}")" != 'branches' ] && [ "$(basename "${fn}")" != 'tags' ] && [ "$(basename "${fn}")" != '/' ]; do + local fn="$(dirname "${fn}")" + done + local fn="$(dirname "${fn}")" + if [ "${fn}" = '/' ]; then + VCS_BASENAME="$(basename "${PWD}")" + else + VCS_BASENAME="$(basename "${fn}")" + fi + ;; + *) VCS_BASENAME="$(basename "${PWD}")" ;; + esac + + VCS_UUID="$(svn info --xml | sed -n -e 's:::' -e 's:::p')" + + # Cache svnversion output + local SVNVERSION="$(svnversion)" + + # Is the working copy clean? + echo "${SVNVERSION}" | grep -q "M" + case "${?}" in + 0) + VCS_WC_MODIFIED="1" + ;; + 1) + if [ ! -z "${UNTRACKEDFILES}" ]; then + # `svnversion` does not detect untracked files and `svn status` is really slow, so only run it if we really have to. + if [ -z "$(svn status)" ]; then + VCS_WC_MODIFIED="0" + else + VCS_WC_MODIFIED="1" + fi + else + VCS_WC_MODIFIED="0" + fi + ;; + esac + + # Enumeration of changesets + VCS_NUM="$(echo "${SVNVERSION}" | cut -d : -f 1 | sed -e 's:M::' -e 's:S::' -e 's:P::')" + + # The full revision hash + VCS_FULL_HASH="${SVNVERSION}" + + # The short hash + VCS_SHORT_HASH="${VCS_NUM}" + + # Current branch + case "${PWD}" in + /*trunk* | /*branches* | /*tags*) + local lastbase="" + local fn="${PWD}" + while :; do + base="$(basename "${fn}")" + if [ "${base}" = 'trunk' ]; then + VCS_BRANCH='trunk' + break + elif [ "${base}" = 'branches' ] || [ "${base}" = 'tags' ]; then + VCS_BRANCH="${lastbase}" + break + elif [ "${base}" = '/' ]; then + VCS_BRANCH="" + break + fi + local lastbase="${base}" + local fn="$(dirname "${fn}")" + done + ;; + *) VCS_BRANCH="" ;; + esac + + # Current or last tag ancestor (empty if no tags). But "current + # tag" can't be extracted reliably because Subversion doesn't + # have tags the way other VCSes do. + VCS_TAG="" + VCS_TICK="" + + # Date of the current commit + VCS_DATE="$(svn info --xml | sed -n -e 's:::' -e 's:::p')" + + # Action Stamp + VCS_ACTION_STAMP="${VCS_DATE}!$(svn log --xml -l 1 -r "${VCS_SHORT_HASH}" | sed -n -e 's:::' -e 's:::p')" + + cd "${oldPath}" +} + +# Functions to output data in different formats. +# For bash output +shOutput() { + cat >"${TARGETFILE}" <"${TARGETFILE}" <"${TARGETFILE}" <"${TARGETFILE}" <"${TARGETFILE}" <"${TARGETFILE}" <"${TARGETFILE}" <"${TARGETFILE}" < "${VCS_TYPE}", + "VCS_BASENAME" => "${VCS_BASENAME}", + "VCS_UUID" => "${VCS_UUID}", + "VCS_NUM" => ${VCS_NUM}, + "VCS_DATE" => "${VCS_DATE}", + "VCS_BRANCH" => "${VCS_BRANCH}", + "VCS_TAG" => "${VCS_TAG}", + "VCS_TICK" => ${VCS_TICK}, + "VCS_EXTRA" => "${VCS_EXTRA}", + "VCS_ACTION_STAMP" => "${VCS_ACTION_STAMP}", + "VCS_FULL_HASH" => "${VCS_FULL_HASH}", + "VCS_SHORT_HASH" => "${VCS_SHORT_HASH}", + "VCS_WC_MODIFIED" => ${VCS_WC_MODIFIED} +); + +# end +?> +EOF +} + +# For ini output +iniOutput() { + case "${VCS_WC_MODIFIED}" in + 0) VCS_WC_MODIFIED="false" ;; + 1) VCS_WC_MODIFIED="true" ;; + esac + cat >"${TARGETFILE}" <"${TARGETFILE}" <"${TARGETFILE}" <"${TARGETFILE}" <"${TARGETFILE}" <"${TARGETFILE}" <"${TARGETFILE}" <"${TARGETFILE}" <"${TARGETFILE}" <"${TARGETFILE}" <"${TARGETFILE}" < + +namespace $(echo "${NAMESPACE}" | tr '[:upper:]' '[:lower:]') +{ + const std::string VCS_TYPE = "${VCS_TYPE}"; + const std::string VCS_BASENAME = "${VCS_BASENAME}"; + const std::string VCS_UUID = "${VCS_UUID}"; + const int VCS_NUM = ${VCS_NUM}; + const std::string VCS_DATE = "${VCS_DATE}"; + const std::string VCS_BRANCH = "${VCS_BRANCH}"; + const std::string VCS_TAG = "${VCS_TAG}"; + const int VCS_TICK = ${VCS_TICK}; + const std::string VCS_EXTRA = "${VCS_EXTRA}"; + + const std::string VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}"; + const std::string VCS_FULL_HASH = "${VCS_FULL_HASH}"; + const std::string VCS_SHORT_HASH = "${VCS_SHORT_HASH}"; + + const int VCS_WC_MODIFIED = ${VCS_WC_MODIFIED}; +} + +#endif + +/* end */ +EOF +} + +matlabOutput() { + case "${VCS_WC_MODIFIED}" in + 0) VCS_WC_MODIFIED="FALSE" ;; + 1) VCS_WC_MODIFIED="TRUE" ;; + esac + cat >"${TARGETFILE}" <"${TARGETFILE}" <"${TARGETFILE}" </dev/null)" ]; then + local gitPath="$(git rev-parse --show-toplevel)" + local gitDepth="$(pathSegment "${gitPath}")" + REPONUM="$((REPONUM + 1))" + else + local gitDepth="0" + fi + if [ ! -z "$(hg root 2>/dev/null)" ]; then + local hgPath="$(hg root 2>/dev/null)" + local hgDepth="$(pathSegment "${hgPath}")" + REPONUM="$((REPONUM + 1))" + else + local hgDepth="0" + fi + if [ ! -z "$(bzr root 2>/dev/null)" ]; then + local bzrPath="$(bzr root 2>/dev/null)" + local bzrDepth="$(pathSegment "${bzrPath}")" + REPONUM="$((REPONUM + 1))" + else + local bzrDepth="0" + fi + if [ ! -z "$(svn info 2>/dev/null)" ]; then + local stringz="" + local stringx="" + local svnPath="$(svn info --xml | sed -n -e "s:${stringz}::" -e "s:${stringx}::p")" + # An old enough svn will not be able give us a path; default + # to 1 for that case. + if [ -z "${svnPath}" ]; then + local svnDepth="1" + else + local svnDepth="$(pathSegment "${svnPath}")" + fi + REPONUM="$((REPONUM + 1))" + else + local svnDepth="0" + fi + + # Do not do more work then we have to. + if [ "${REPONUM}" = "0" ]; then + return + fi + + # Figure out which repo is the deepest and use it. + local wonRepo="$(multiCompare "${gitDepth}" "${hgDepth}" "${bzrDepth}" "${svnDepth}")" + if [ "${wonRepo}" = "${gitDepth}" ]; then + gitRepo + elif [ "${wonRepo}" = "${hgDepth}" ]; then + hgRepo + elif [ "${wonRepo}" = "${bzrDepth}" ]; then + bzrRepo + elif [ "${wonRepo}" = "${svnDepth}" ]; then + svnRepo + fi +} + +# Detect which repos we are in and gather data. +# shellcheck source=/dev/null +if [ -f "${CACHEFILE}" ] && [ "${CACHEFORCE}" = "1" ]; then + # When requested only read from the cache to populate our symbols. + . "${CACHEFILE}" +else + # If a value is not set through the environment set VCS_EXTRA to nothing. + : "${VCS_EXTRA:=""}" + repoTest + + if [ -f "${CACHEFILE}" ] && [ "${REPONUM}" = "0" ]; then + # We are not in a repo; try to use a previously generated cache to populate our symbols. + . "${CACHEFILE}" + # Do not overwrite the cache if we know we are not going to write anything new. + CACHEFORCE="1" + elif [ "${REPONUM}" = "0" ]; then + echo "error: No repo or cache detected." 1>&2 + exit 1 + fi +fi + +# -s output is handled here. +if [ ! -z "${VAROUT}" ]; then + if [ "${VAROUT}" = "VCS_TYPE" ]; then + echo "${VCS_TYPE}" + elif [ "${VAROUT}" = "VCS_BASENAME" ]; then + echo "${VCS_BASENAME}" + elif [ "${VAROUT}" = "VCS_NUM" ]; then + echo "${VCS_NUM}" + elif [ "${VAROUT}" = "VCS_DATE" ]; then + echo "${VCS_DATE}" + elif [ "${VAROUT}" = "VCS_BRANCH" ]; then + echo "${VCS_BRANCH}" + elif [ "${VAROUT}" = "VCS_TAG" ]; then + echo "${VCS_TAG}" + elif [ "${VAROUT}" = "VCS_TICK" ]; then + echo "${VCS_TICK}" + elif [ "${VAROUT}" = "VCS_FULL_HASH" ]; then + echo "${VCS_FULL_HASH}" + elif [ "${VAROUT}" = "VCS_SHORT_HASH" ]; then + echo "${VCS_SHORT_HASH}" + elif [ "${VAROUT}" = "VCS_WC_MODIFIED" ]; then + echo "${VCS_WC_MODIFIED}" + elif [ "${VAROUT}" = "VCS_ACTION_STAMP" ]; then + echo "${VCS_ACTION_STAMP}" + else + echo "error: Not a valid output symbol." 1>&2 + exit 1 + fi +fi + +# Detect requested output type and use it. +if [ ! -z "${AFILETYPE}" ]; then + if [ "${AFILETYPE}" = "c" ]; then + cOutput + elif [ "${AFILETYPE}" = "h" ]; then + hOutput + elif [ "${AFILETYPE}" = "xcode" ]; then + xcodeOutput + elif [ "${AFILETYPE}" = "swift" ]; then + swiftOutput + elif [ "${AFILETYPE}" = "sh" ]; then + shOutput + elif [ "${AFILETYPE}" = "py" ] || [ "${AFILETYPE}" = "python" ]; then + pyOutput + elif [ "${AFILETYPE}" = "pl" ] || [ "${AFILETYPE}" = "perl" ]; then + plOutput + elif [ "${AFILETYPE}" = "lua" ]; then + luaOutput + elif [ "${AFILETYPE}" = "php" ]; then + phpOutput + elif [ "${AFILETYPE}" = "ini" ]; then + iniOutput + elif [ "${AFILETYPE}" = "js" ]; then + jsOutput + elif [ "${AFILETYPE}" = "json" ]; then + jsonOutput + elif [ "${AFILETYPE}" = "java" ]; then + javaOutput + elif [ "${AFILETYPE}" = "javaprop" ]; then + javapropOutput + elif [ "${AFILETYPE}" = "tex" ]; then + texOutput + elif [ "${AFILETYPE}" = "m4" ]; then + m4Output + elif [ "${AFILETYPE}" = "scheme" ]; then + schemeOutput + elif [ "${AFILETYPE}" = "clojure" ]; then + clojureOutput + elif [ "${AFILETYPE}" = "rpm" ]; then + rpmOutput + elif [ "${AFILETYPE}" = "hpp" ]; then + hppOutput + elif [ "${AFILETYPE}" = "matlab" ]; then + matlabOutput + elif [ "${AFILETYPE}" = "octave" ]; then + octaveOutput + elif [ "${AFILETYPE}" = "cmake" ]; then + cmakeOutput + else + echo "error: Not a valid output type." 1>&2 + exit 1 + fi +fi + +# If requested, make a cache file. +if [ ! -z "${CACHEFILE}" ] && [ ! "${CACHEFORCE}" = "1" ]; then + TARGETFILE="${CACHEFILE}.tmp" + shOutput + + # Check to see if there have been any actual changes. + if [ ! -f "${CACHEFILE}" ]; then + mv -f "${CACHEFILE}.tmp" "${CACHEFILE}" + elif cmp -s "${CACHEFILE}.tmp" "${CACHEFILE}"; then + rm -f "${CACHEFILE}.tmp" + else + mv -f "${CACHEFILE}.tmp" "${CACHEFILE}" + fi +fi diff --git a/ci/.gitkeep b/ci/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/ci/get-nprocessors.sh b/ci/get-nprocessors.sh new file mode 100644 index 0000000..8a754cf --- /dev/null +++ b/ci/get-nprocessors.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# Copyright 2017 Google Inc. +# All Rights Reserved. +# +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# This file is typically sourced by another script. +# if possible, ask for the precise number of processors, +# otherwise take 2 processors as reasonable default; see +# https://docs.travis-ci.com/user/speeding-up-the-build/#Makefile-optimization +if [ -x /usr/bin/getconf ]; then + NPROCESSORS=$(/usr/bin/getconf _NPROCESSORS_ONLN) +else + NPROCESSORS=2 +fi + +# as of 2017-09-04 Travis CI reports 32 processors, but GCC build +# crashes if parallelized too much (maybe memory consumption problem), +# so limit to 4 processors for the time being. +if [ $NPROCESSORS -gt 4 ]; then + echo "$0:Note: Limiting processors to use by make from $NPROCESSORS to 4." + NPROCESSORS=4 +fi diff --git a/ci/travis.sh b/ci/travis.sh new file mode 100644 index 0000000..a5d12f6 --- /dev/null +++ b/ci/travis.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env sh +set -evx + +chmod +x ci/get-nprocessors.sh +. ci/get-nprocessors.sh + +# if possible, ask for the precise number of processors, +# otherwise take 2 processors as reasonable default; see +# https://docs.travis-ci.com/user/speeding-up-the-build/#Makefile-optimization +if [ -x /usr/bin/getconf ]; then + NPROCESSORS=$(/usr/bin/getconf _NPROCESSORS_ONLN) +else + NPROCESSORS=2 +fi + +# as of 2017-09-04 Travis CI reports 32 processors, but GCC build +# crashes if parallelized too much (maybe memory consumption problem), +# so limit to 4 processors for the time being. +if [ $NPROCESSORS -gt 4 ]; then + echo "$0:Note: Limiting processors to use by make from $NPROCESSORS to 4." + NPROCESSORS=4 +fi + +# Tell make to use the processors. No preceding '-' required. +MAKEFLAGS="j${NPROCESSORS}" +export MAKEFLAGS + +env | sort + +# Set default values to OFF for these variables if not specified. +: "${NO_EXCEPTION:=OFF}" +: "${NO_RTTI:=OFF}" +: "${COMPILER_IS_GNUCXX:=OFF}" + +mkdir build || true +cd build + +cmake3 -DCMAKE_CXX_FLAGS=$CXX_FLAGS \ + -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ + -DASAN_OPTION=$ASAN_OPTION \ + -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX \ + -DTFE_VERSION_DAILY_BUILD=$TESTING_VERSION_BUILD \ + .. + +make + +if [ -n "${PACKAGE}" ]; then + make package +fi \ No newline at end of file diff --git a/cmake/.gitkeep b/cmake/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cmake/Package.cmake b/cmake/Package.cmake new file mode 100644 index 0000000..0ad6569 --- /dev/null +++ b/cmake/Package.cmake @@ -0,0 +1,43 @@ +cmake_minimum_required(VERSION 3.12) + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CPACK_PACKAGE_NAME ${PROJECT_NAME}-debug) +else() + set(CPACK_PACKAGE_NAME ${PROJECT_NAME}) +endif() + +message(STATUS "Package: ${CPACK_PACKAGE_NAME}") + +set(CPACK_PACKAGE_VENDOR "TSG") +set(CPACK_PACKAGE_VERSION_MAJOR "${HTTP_DECODER_VERSION_MAJOR}") +set(CPACK_PACKAGE_VERSION_MINOR "${HTTP_DECODER_VERSION_MINOR}") +set(CPACK_PACKAGE_VERSION_PATCH "${HTTP_DECODER_VERSION_PATCH}.${HTTP_DECODER_DESCRIBE}") +set(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) + + +# RPM Build +set(CPACK_GENERATOR "RPM") +set(CPACK_RPM_AUTO_GENERATED_FILE_NAME ON) +set(CPACK_RPM_FILE_NAME "RPM-DEFAULT") +set(CPACK_RPM_PACKAGE_AUTOREQPROV "no") +set(CPACK_RPM_PACKAGE_RELEASE_DIST on) +set(CPACK_RPM_DEBUGINFO_PACKAGE on) +set(CPACK_BUILD_SOURCE_DIRS "${CMAKE_SOURCE_DIR}") + +set(CPACK_RPM_COMPONENT_INSTALL ON) +set(CPACK_COMPONENTS_ALL Libraries Headers) + +set(CPACK_RPM_HEADERS_PACKAGE_NAME "${CPACK_PACKAGE_NAME}-devel") +set(CPACK_RPM_LIBRARIES_PACKAGE_NAME "${CPACK_PACKAGE_NAME}") + +set(CPACK_RPM_PACKAGE_AUTOREQPROV "no") +set(CPACK_RPM_PACKAGE_AUTOREQ "no") + +set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE ${CMAKE_SOURCE_DIR}/cmake/PostInstall.in) +set(CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE ${CMAKE_SOURCE_DIR}/cmake/PostUninstall.in) +set(CPACK_RPM_PRE_INSTALL_SCRIPT_FILE ${CMAKE_SOURCE_DIR}/cmake/PreInstall.in) +set(CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE ${CMAKE_SOURCE_DIR}/cmake/PreUninstall.in) + +set(CPACK_RPM_HEADERS_PACKAGE_CONFLICTS ${CPACK_RPM_HEADERS_PACKAGE_NAME}) +set(CPACK_RPM_LIBRARIES_PACKAGE_CONFLICTS ${CPACK_RPM_LIBRARIES_PACKAGE_NAME}) +include(CPack) diff --git a/cmake/PostInstall.in b/cmake/PostInstall.in new file mode 100644 index 0000000..d601599 --- /dev/null +++ b/cmake/PostInstall.in @@ -0,0 +1 @@ +/sbin/ldconfig \ No newline at end of file diff --git a/cmake/PostUninstall.in b/cmake/PostUninstall.in new file mode 100644 index 0000000..d601599 --- /dev/null +++ b/cmake/PostUninstall.in @@ -0,0 +1 @@ +/sbin/ldconfig \ No newline at end of file diff --git a/cmake/PreInstall.in b/cmake/PreInstall.in new file mode 100644 index 0000000..ea89e39 --- /dev/null +++ b/cmake/PreInstall.in @@ -0,0 +1,22 @@ +DST=${RPM_INSTALL_PREFIX}/sapp/ + +mkdir -p ${DST}/plug/stellar_on_sapp +mkdir -p ${DST}/stellar_plugin/ +touch ${DST}/stellar_plugin/spec.toml +touch ${DST}/plug/conflist.inf + +if ! grep -q '^\./plug/stellar_on_sapp/start_loader.inf$' "${DST}/plug/conflist.inf"; then + if grep -q '^\[platform\]$' "${DST}/plug/conflist.inf"; then + sed -i '/^\[platform\]$/a\./plug/stellar_on_sapp/start_loader.inf' "${DST}/plug/conflist.inf" + else + echo -e "[platform]\n./plug/stellar_on_sapp/start_loader.inf" >> "${DST}/plug/conflist.inf" + fi +fi + +if ! grep -q '^\./plug/stellar_on_sapp/defer_loader.inf$' "${DST}/plug/conflist.inf"; then + if grep -q '^\[business\]$' "${DST}/plug/conflist.inf"; then + sed -i '/^\[business\]$/a\./plug/stellar_on_sapp/defer_loader.inf' "${DST}/plug/conflist.inf" + else + echo -e "[business]\n./plug/stellar_on_sapp/defer_loader.inf" >> "${DST}/plug/conflist.inf" + fi +fi \ No newline at end of file diff --git a/cmake/PreUninstall.in b/cmake/PreUninstall.in new file mode 100644 index 0000000..dc0aa5e --- /dev/null +++ b/cmake/PreUninstall.in @@ -0,0 +1,6 @@ +if [ $1 == 0 ]; then + DST=${RPM_INSTALL_PREFIX}/sapp + sed -i '/start_loader.inf/d' ${DST}/plug/conflist.inf + sed -i '/defer_loader.inf/d' ${DST}/plug/conflist.inf + rm ${DST}/stellar_plugin -rf +fi diff --git a/cmake/Version.cmake b/cmake/Version.cmake new file mode 100644 index 0000000..2492869 --- /dev/null +++ b/cmake/Version.cmake @@ -0,0 +1,47 @@ +# Using autorevision.sh to generate version information +set(__SOURCE_AUTORESIVISION ${CMAKE_SOURCE_DIR}/autorevision.sh) +set(__AUTORESIVISION ${CMAKE_BINARY_DIR}/autorevision.sh) +set(__VERSION_CACHE ${CMAKE_BINARY_DIR}/version.txt) +set(__VERSION_CONFIG ${CMAKE_BINARY_DIR}/version.cmake) + +file(COPY ${__SOURCE_AUTORESIVISION} DESTINATION ${CMAKE_BINARY_DIR} + FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE) + +# Execute autorevision.sh to generate version information +execute_process(COMMAND ${__AUTORESIVISION} -t cmake -o ${__VERSION_CACHE} OUTPUT_FILE ${__VERSION_CONFIG}) +include(${__VERSION_CONFIG}) + +# Extract major, minor, patch version from git tag +string(REGEX REPLACE "^v([0-9]+)\\..*" "\\1" HTTP_DECODER_VERSION_MAJOR "${VCS_TAG}") +string(REGEX REPLACE "^v[0-9]+\\.([0-9]+).*" "\\1" HTTP_DECODER_VERSION_MINOR "${VCS_TAG}") +string(REGEX REPLACE "^v[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" HTTP_DECODER_VERSION_PATCH "${VCS_TAG}") +string(REGEX REPLACE "[T\\:\\+\\-]" "" HTTP_DECODER_VERSION_DATE "${VCS_DATE}") + +if(STELLAR_VERSION_DAILY_BUILD) + set(HTTP_DECODER_VERSION_PATCH ${HTTP_DECODER_VERSION_PATCH}.${HTTP_DECODER_VERSION_DATE}) +endif() + +if(NOT HTTP_DECODER_VERSION_MAJOR) + set(HTTP_DECODER_VERSION_MAJOR 1) +endif() + +if(NOT HTTP_DECODER_VERSION_MINOR) + set(HTTP_DECODER_VERSION_MINOR 0) +endif() + +if(NOT HTTP_DECODER_VERSION_PATCH) + set(HTTP_DECODER_VERSION_PATCH 0) +endif() + +set(HTTP_DECODER_DESCRIBE "${VCS_SHORT_HASH}") +set(STELLAR_VERSION "${HTTP_DECODER_VERSION_MAJOR}.${HTTP_DECODER_VERSION_MINOR}.${HTTP_DECODER_VERSION_PATCH}") +set(HTTP_DECODER_GIT_VERSION "${HTTP_DECODER_VERSION_MAJOR}.${HTTP_DECODER_VERSION_MINOR}.${HTTP_DECODER_VERSION_PATCH}-${HTTP_DECODER_DESCRIBE}") + +# Replace .- with _ +string(REGEX REPLACE "[\\.\\-]" "_" STELLAR_VAR_VERSION "${HTTP_DECODER_GIT_VERSION}") + +# Print information +message(STATUS "Welcome to stateful network function development platform, Version: ${HTTP_DECODER_GIT_VERSION}") +add_definitions(-DHTTP_DECODER_GIT_VERSION=\"${HTTP_DECODER_GIT_VERSION}\") +add_definitions(-DHTTP_DECODER_VAR_VERSION=${HTTP_DECODER_VAR_VERSION}) \ No newline at end of file diff --git a/conf/.gitkeep b/conf/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/conf/http_decoder.toml b/conf/http_decoder.toml new file mode 100644 index 0000000..70ed3bd --- /dev/null +++ b/conf/http_decoder.toml @@ -0,0 +1,15 @@ +[basic] +# Switch for decompress body, open(1) closed(0), default(0) +decompress=1 + +# per session mempool size, default(32Ki) +mempool_size=32768 + +# per session http parsed result queue length +result_queue_len=16 + +# call fieldstat every stat_interval_pkts +stat_interval_pkts=1000 + +# fieldstat output interval +stat_output_interval=1 \ No newline at end of file diff --git a/deps/.gitkeep b/deps/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/deps/mempool/nmx_alloc.c b/deps/mempool/nmx_alloc.c new file mode 100644 index 0000000..083f555 --- /dev/null +++ b/deps/mempool/nmx_alloc.c @@ -0,0 +1,44 @@ +#include "nmx_alloc.h" +#include + +void *nmx_alloc(size_t size) +{ + void *p; + + p = malloc(size); + + return p; +} + +void *nmx_calloc(size_t size) +{ + void *p; + + p = nmx_alloc(size); + + if (p) { + memset(p,0,size); + } + + return p; +} + +void *nmx_realloc(void *p, size_t size){ + + if(p) { + return realloc (p, size); + } + + return NULL; + +} + +void *nmx_memalign(size_t alignment, size_t size) +{ + void *p; + int err; + + err = posix_memalign(&p, alignment, size); + + return p; +} \ No newline at end of file diff --git a/deps/mempool/nmx_alloc.h b/deps/mempool/nmx_alloc.h new file mode 100644 index 0000000..123d5bf --- /dev/null +++ b/deps/mempool/nmx_alloc.h @@ -0,0 +1,16 @@ +#ifndef __nmx_alloc_H_ +#define __nmx_alloc_H_ +#include + +void *nmx_alloc(size_t size); +void *nmx_calloc(size_t size); +void *nmx_realloc(void *p, size_t size); + +#define nmx_free free + +#define nmx_align_ptr(p, a) \ + (unsigned char *) (((unsigned long ) (p) + ((unsigned long ) a - 1)) & ~((unsigned long ) a - 1)) + +void *nmx_memalign(size_t alignment, size_t size); + +#endif //nmx_alloc_H_ diff --git a/deps/mempool/nmx_palloc.c b/deps/mempool/nmx_palloc.c new file mode 100644 index 0000000..307e2f6 --- /dev/null +++ b/deps/mempool/nmx_palloc.c @@ -0,0 +1,262 @@ +#include "nmx_palloc.h" + +#include +#include + +size_t nmx_pagesize = 0; + +//#define nmx_MAX_ALLOC_FROM_POOL (nmx_pagesize - 1) + +#define NMX_MAX_ALLOC_FROM_POOL (nmx_pagesize == 0 ? (nmx_pagesize = getpagesize() -1) : (nmx_pagesize)) + +#define NMX_POOL_ALIGNMENT 16 + +static void *nmx_palloc_block(nmx_pool_t *pool, size_t size); +static void *nmx_palloc_large(nmx_pool_t *pool, size_t size); + +nmx_pool_t *nmx_create_pool(size_t size) +{ + nmx_pool_t *p; + + p = (nmx_pool_t *)nmx_memalign(NMX_POOL_ALIGNMENT, size); + if (p == NULL) { + return NULL; + } + + p->d.last = (unsigned char *) p + sizeof(nmx_pool_t); + p->d.end = (unsigned char *) p + size; + p->d.next = NULL; + p->d.failed = 0; + + size = size - sizeof(nmx_pool_t); + p->max = (size < NMX_MAX_ALLOC_FROM_POOL) ? size : NMX_MAX_ALLOC_FROM_POOL; + + p->current = p; + p->large = NULL; + + return p; +} + +void nmx_destroy_pool(nmx_pool_t *pool) +{ + nmx_pool_t *p, *n; + nmx_pool_large_t *l; + + for (l = pool->large; l; l = l->next) { + + if (l->alloc) { + nmx_free(l->alloc); + } + } + + for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) { + nmx_free(p); + + if (n == NULL) { + break; + } + } +} + +void nmx_reset_pool(nmx_pool_t *pool) +{ + nmx_pool_t *p; + nmx_pool_large_t *l; + + for (l = pool->large; l; l = l->next) { + if (l->alloc) { + nmx_free(l->alloc); + } + } + + for (p = pool; p; p = p->d.next) { + p->d.last = (unsigned char *) p + sizeof(nmx_pool_t); + p->d.failed = 0; + } + + pool->current = pool; + pool->large = NULL; +} + +void *nmx_palloc(nmx_pool_t *pool, size_t size) +{ + unsigned char *m; + nmx_pool_t *p; + + if (size <= pool->max) { + + p = pool->current; + + do { + m = nmx_align_ptr(p->d.last, NMX_POOL_ALIGNMENT); + + if ((size_t) (p->d.end - m) >= size) { + p->d.last = m + size; + + return m; + } + + p = p->d.next; + + } while (p); + + return nmx_palloc_block(pool, size); + } + + return nmx_palloc_large(pool, size); +} + + +void *nmx_pnalloc(nmx_pool_t *pool, size_t size) +{ + unsigned char *m; + nmx_pool_t *p; + + if (size <= pool->max) { + + p = pool->current; + + do { + m = p->d.last; + + if ((size_t) (p->d.end - m) >= size) { + p->d.last = m + size; + + return m; + } + + p = p->d.next; + + } while (p); + + return nmx_palloc_block(pool, size); + } + + return nmx_palloc_large(pool, size); +} + + +static void * +nmx_palloc_block(nmx_pool_t *pool, size_t size) +{ + unsigned char *m; + size_t psize; + nmx_pool_t *p, *new; + + psize = (size_t) (pool->d.end - (unsigned char *) pool); + + m = (unsigned char *)nmx_memalign(NMX_POOL_ALIGNMENT, psize); + if (m == NULL) { + return NULL; + } + + new = (nmx_pool_t *) m; + + new->d.end = m + psize; + new->d.next = NULL; + new->d.failed = 0; + + m += sizeof(nmx_pool_data_t); + m = nmx_align_ptr(m, NMX_POOL_ALIGNMENT); + new->d.last = m + size; + + for (p = pool->current; p->d.next; p = p->d.next) { + if (p->d.failed++ > 4) { + pool->current = p->d.next; + } + } + + p->d.next = new; + + return m; +} + +static void * +nmx_palloc_large(nmx_pool_t *pool,size_t size) +{ + void *p; + int n; + nmx_pool_large_t *large; + + p = nmx_alloc(size); + if (p == NULL) { + return NULL; + } + + n = 0; + + for (large = pool->large; large; large = large->next) { + if (large->alloc == NULL) { + large->alloc = p; + return p; + } + + if (n++ > 3) { + break; + } + } + + large = nmx_palloc(pool, sizeof(nmx_pool_large_t)); + if (large == NULL) { + nmx_free(p); + return NULL; + } + + large->alloc = p; + large->next = pool->large; + pool->large = large; + + return p; +} + +void *nmx_pmemalign(nmx_pool_t *pool, size_t size, size_t alignment) +{ + void *p; + nmx_pool_large_t *large; + + p = nmx_memalign(alignment, size); + if (p == NULL) { + return NULL; + } + + large = nmx_palloc(pool, sizeof(nmx_pool_large_t)); + if (large == NULL) { + nmx_free(p); + return NULL; + } + + large->alloc = p; + large->next = pool->large; + pool->large = large; + + return p; +} + +int nmx_pfree(nmx_pool_t *pool, void *p) +{ + nmx_pool_large_t *l; + + for (l = pool->large; l; l = l->next) { + if (p == l->alloc) { + + nmx_free(l->alloc); + l->alloc = NULL; + + return 1; + } + } + + return 0; +} + +void *nmx_pcalloc(nmx_pool_t *pool, size_t size) +{ + void *p; + + p = nmx_palloc(pool, size); + if (p) { + memset(p, 0,size); + } + + return p; +} \ No newline at end of file diff --git a/deps/mempool/nmx_palloc.h b/deps/mempool/nmx_palloc.h new file mode 100644 index 0000000..0b69be1 --- /dev/null +++ b/deps/mempool/nmx_palloc.h @@ -0,0 +1,58 @@ +#ifndef __nmx_palloc_H_ +#define __nmx_palloc_H_ + +#include + +#include "nmx_alloc.h" + +typedef struct nmx_pool_large_s nmx_pool_large_t; +typedef struct nmx_pool_s nmx_pool_t; + +struct nmx_pool_large_s { + nmx_pool_large_t *next; + void *alloc; +}; + +typedef struct { + unsigned char *last; + unsigned char *end; + nmx_pool_t *next; + unsigned int failed; +} nmx_pool_data_t; + +struct nmx_pool_s { + + nmx_pool_data_t d; + size_t max; + nmx_pool_t *current; + nmx_pool_large_t *large; +}; + +/* ====================================== + * ++++++++ Library Open API ++++++++++ + * ====================================== + */ + +void *nmx_alloc (size_t size); + +void *nmx_calloc (size_t size); + +nmx_pool_t *nmx_create_pool (size_t size); + +void nmx_destroy_pool (nmx_pool_t *pool); + +void nmx_reset_pool (nmx_pool_t *pool); + +void *nmx_palloc (nmx_pool_t *pool, size_t size); + +void *nmx_pnalloc (nmx_pool_t *pool, size_t size); + +void *nmx_prealloc (nmx_pool_t *pool, void *p, size_t size); + +void *nmx_pcalloc (nmx_pool_t *pool, size_t size); + +void *nmx_pmemalign (nmx_pool_t *pool, size_t size, size_t alignment); + +int nmx_pfree (nmx_pool_t *pool, void *p); + +#endif //nmx_palloc.h_H_ diff --git a/deps/toml/CMakeLists.txt b/deps/toml/CMakeLists.txt new file mode 100644 index 0000000..504c0bf --- /dev/null +++ b/deps/toml/CMakeLists.txt @@ -0,0 +1,3 @@ +set(CMAKE_C_FLAGS "-std=c99") +add_definitions(-fPIC) +add_library(toml STATIC toml.c) \ No newline at end of file diff --git a/deps/toml/toml.c b/deps/toml/toml.c new file mode 100644 index 0000000..fafe0da --- /dev/null +++ b/deps/toml/toml.c @@ -0,0 +1,2379 @@ +/* + + MIT License + + Copyright (c) CK Tan + https://github.com/cktan/tomlc99 + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +*/ +#define _POSIX_C_SOURCE 200809L +#include "toml.h" +#include +#include +#include +#include +#include +#include +#include +#include + +static void *(*ppmalloc)(size_t) = malloc; +static void (*ppfree)(void *) = free; + +void toml_set_memutil(void *(*xxmalloc)(size_t), void (*xxfree)(void *)) { + if (xxmalloc) + ppmalloc = xxmalloc; + if (xxfree) + ppfree = xxfree; +} + +#define MALLOC(a) ppmalloc(a) +#define FREE(a) ppfree(a) + +#define malloc(x) error - forbidden - use MALLOC instead +#define free(x) error - forbidden - use FREE instead +#define calloc(x, y) error - forbidden - use CALLOC instead + +static void *CALLOC(size_t nmemb, size_t sz) { + int nb = sz * nmemb; + void *p = MALLOC(nb); + if (p) { + memset(p, 0, nb); + } + return p; +} + +// some old platforms define strdup macro -- drop it. +#undef strdup +#define strdup(x) error - forbidden - use STRDUP instead + +static char *STRDUP(const char *s) { + int len = strlen(s); + char *p = MALLOC(len + 1); + if (p) { + memcpy(p, s, len); + p[len] = 0; + } + return p; +} + +// some old platforms define strndup macro -- drop it. +#undef strndup +#define strndup(x) error - forbiden - use STRNDUP instead + +static char *STRNDUP(const char *s, size_t n) { + size_t len = strnlen(s, n); + char *p = MALLOC(len + 1); + if (p) { + memcpy(p, s, len); + p[len] = 0; + } + return p; +} + +/** + * Convert a char in utf8 into UCS, and store it in *ret. + * Return #bytes consumed or -1 on failure. + */ +int toml_utf8_to_ucs(const char *orig, int len, int64_t *ret) { + const unsigned char *buf = (const unsigned char *)orig; + unsigned i = *buf++; + int64_t v; + + /* 0x00000000 - 0x0000007F: + 0xxxxxxx + */ + if (0 == (i >> 7)) { + if (len < 1) + return -1; + v = i; + return *ret = v, 1; + } + /* 0x00000080 - 0x000007FF: + 110xxxxx 10xxxxxx + */ + if (0x6 == (i >> 5)) { + if (len < 2) + return -1; + v = i & 0x1f; + for (int j = 0; j < 1; j++) { + i = *buf++; + if (0x2 != (i >> 6)) + return -1; + v = (v << 6) | (i & 0x3f); + } + return *ret = v, (const char *)buf - orig; + } + + /* 0x00000800 - 0x0000FFFF: + 1110xxxx 10xxxxxx 10xxxxxx + */ + if (0xE == (i >> 4)) { + if (len < 3) + return -1; + v = i & 0x0F; + for (int j = 0; j < 2; j++) { + i = *buf++; + if (0x2 != (i >> 6)) + return -1; + v = (v << 6) | (i & 0x3f); + } + return *ret = v, (const char *)buf - orig; + } + + /* 0x00010000 - 0x001FFFFF: + 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + if (0x1E == (i >> 3)) { + if (len < 4) + return -1; + v = i & 0x07; + for (int j = 0; j < 3; j++) { + i = *buf++; + if (0x2 != (i >> 6)) + return -1; + v = (v << 6) | (i & 0x3f); + } + return *ret = v, (const char *)buf - orig; + } + + /* 0x00200000 - 0x03FFFFFF: + 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + if (0x3E == (i >> 2)) { + if (len < 5) + return -1; + v = i & 0x03; + for (int j = 0; j < 4; j++) { + i = *buf++; + if (0x2 != (i >> 6)) + return -1; + v = (v << 6) | (i & 0x3f); + } + return *ret = v, (const char *)buf - orig; + } + + /* 0x04000000 - 0x7FFFFFFF: + 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + if (0x7e == (i >> 1)) { + if (len < 6) + return -1; + v = i & 0x01; + for (int j = 0; j < 5; j++) { + i = *buf++; + if (0x2 != (i >> 6)) + return -1; + v = (v << 6) | (i & 0x3f); + } + return *ret = v, (const char *)buf - orig; + } + return -1; +} + +/** + * Convert a UCS char to utf8 code, and return it in buf. + * Return #bytes used in buf to encode the char, or + * -1 on error. + */ +int toml_ucs_to_utf8(int64_t code, char buf[6]) { + /* http://stackoverflow.com/questions/6240055/manually-converting-unicode-codepoints-into-utf-8-and-utf-16 + */ + /* The UCS code values 0xd800–0xdfff (UTF-16 surrogates) as well + * as 0xfffe and 0xffff (UCS noncharacters) should not appear in + * conforming UTF-8 streams. + */ + if (0xd800 <= code && code <= 0xdfff) + return -1; + if (0xfffe <= code && code <= 0xffff) + return -1; + + /* 0x00000000 - 0x0000007F: + 0xxxxxxx + */ + if (code < 0) + return -1; + if (code <= 0x7F) { + buf[0] = (unsigned char)code; + return 1; + } + + /* 0x00000080 - 0x000007FF: + 110xxxxx 10xxxxxx + */ + if (code <= 0x000007FF) { + buf[0] = (unsigned char) (0xc0 | (code >> 6)); + buf[1] = (unsigned char) (0x80 | (code & 0x3f)); + return 2; + } + + /* 0x00000800 - 0x0000FFFF: + 1110xxxx 10xxxxxx 10xxxxxx + */ + if (code <= 0x0000FFFF) { + buf[0] = (unsigned char) (0xe0 | (code >> 12)); + buf[1] = (unsigned char) (0x80 | ((code >> 6) & 0x3f)); + buf[2] = (unsigned char) (0x80 | (code & 0x3f)); + return 3; + } + + /* 0x00010000 - 0x001FFFFF: + 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + if (code <= 0x001FFFFF) { + buf[0] = (unsigned char) (0xf0 | (code >> 18)); + buf[1] = (unsigned char) (0x80 | ((code >> 12) & 0x3f)); + buf[2] = (unsigned char) (0x80 | ((code >> 6) & 0x3f)); + buf[3] = (unsigned char) (0x80 | (code & 0x3f)); + return 4; + } + + /* 0x00200000 - 0x03FFFFFF: + 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + if (code <= 0x03FFFFFF) { + buf[0] = (unsigned char) (0xf8 | (code >> 24)); + buf[1] = (unsigned char) (0x80 | ((code >> 18) & 0x3f)); + buf[2] = (unsigned char) (0x80 | ((code >> 12) & 0x3f)); + buf[3] = (unsigned char) (0x80 | ((code >> 6) & 0x3f)); + buf[4] = (unsigned char) (0x80 | (code & 0x3f)); + return 5; + } + + /* 0x04000000 - 0x7FFFFFFF: + 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + if (code <= 0x7FFFFFFF) { + buf[0] = (unsigned char) (0xfc | (code >> 30)); + buf[1] = (unsigned char) (0x80 | ((code >> 24) & 0x3f)); + buf[2] = (unsigned char) (0x80 | ((code >> 18) & 0x3f)); + buf[3] = (unsigned char) (0x80 | ((code >> 12) & 0x3f)); + buf[4] = (unsigned char) (0x80 | ((code >> 6) & 0x3f)); + buf[5] = (unsigned char) (0x80 | (code & 0x3f)); + return 6; + } + + return -1; +} + +/* + * TOML has 3 data structures: value, array, table. + * Each of them can have identification key. + */ +typedef struct toml_keyval_t toml_keyval_t; +struct toml_keyval_t { + const char *key; /* key to this value */ + const char *val; /* the raw value */ +}; + +typedef struct toml_arritem_t toml_arritem_t; +struct toml_arritem_t { + int valtype; /* for value kind: 'i'nt, 'd'ouble, 'b'ool, 's'tring, 't'ime, + 'D'ate, 'T'imestamp */ + char *val; + toml_array_t *arr; + toml_table_t *tab; +}; + +struct toml_array_t { + const char *key; /* key to this array */ + int kind; /* element kind: 'v'alue, 'a'rray, or 't'able, 'm'ixed */ + int type; /* for value kind: 'i'nt, 'd'ouble, 'b'ool, 's'tring, 't'ime, + 'D'ate, 'T'imestamp, 'm'ixed */ + + int nitem; /* number of elements */ + toml_arritem_t *item; +}; + +struct toml_table_t { + const char *key; /* key to this table */ + bool implicit; /* table was created implicitly */ + bool readonly; /* no more modification allowed */ + + /* key-values in the table */ + int nkval; + toml_keyval_t **kval; + + /* arrays in the table */ + int narr; + toml_array_t **arr; + + /* tables in the table */ + int ntab; + toml_table_t **tab; +}; + +static inline void xfree(const void *x) { + if (x) + FREE((void *)(intptr_t)x); +} + +enum tokentype_t { + INVALID, + DOT, + COMMA, + EQUAL, + LBRACE, + RBRACE, + NEWLINE, + LBRACKET, + RBRACKET, + STRING, +}; +typedef enum tokentype_t tokentype_t; + +typedef struct token_t token_t; +struct token_t { + tokentype_t tok; + int lineno; + char *ptr; /* points into context->start */ + int len; + int eof; +}; + +typedef struct context_t context_t; +struct context_t { + char *start; + char *stop; + char *errbuf; + int errbufsz; + + token_t tok; + toml_table_t *root; + toml_table_t *curtab; + + struct { + int top; + char *key[10]; + token_t tok[10]; + } tpath; +}; + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) +#define FLINE __FILE__ ":" TOSTRING(__LINE__) + +static int next_token(context_t *ctx, int dotisspecial); + +/* + Error reporting. Call when an error is detected. Always return -1. +*/ +static int e_outofmemory(context_t *ctx, const char *fline) { + snprintf(ctx->errbuf, ctx->errbufsz, "ERROR: out of memory (%s)", fline); + return -1; +} + +static int e_internal(context_t *ctx, const char *fline) { + snprintf(ctx->errbuf, ctx->errbufsz, "internal error (%s)", fline); + return -1; +} + +static int e_syntax(context_t *ctx, int lineno, const char *msg) { + snprintf(ctx->errbuf, ctx->errbufsz, "line %d: %s", lineno, msg); + return -1; +} + +static int e_badkey(context_t *ctx, int lineno) { + snprintf(ctx->errbuf, ctx->errbufsz, "line %d: bad key", lineno); + return -1; +} + +static int e_keyexists(context_t *ctx, int lineno) { + snprintf(ctx->errbuf, ctx->errbufsz, "line %d: key exists", lineno); + return -1; +} + +static int e_forbid(context_t *ctx, int lineno, const char *msg) { + snprintf(ctx->errbuf, ctx->errbufsz, "line %d: %s", lineno, msg); + return -1; +} + +static void *expand(void *p, int sz, int newsz) { + void *s = MALLOC(newsz); + if (!s) + return 0; + + memcpy(s, p, sz); + FREE(p); + return s; +} + +static void **expand_ptrarr(void **p, int n) { + void **s = MALLOC((n + 1) * sizeof(void *)); + if (!s) + return 0; + + s[n] = 0; + memcpy(s, p, n * sizeof(void *)); + FREE(p); + return s; +} + +static toml_arritem_t *expand_arritem(toml_arritem_t *p, int n) { + toml_arritem_t *pp = expand(p, n * sizeof(*p), (n + 1) * sizeof(*p)); + if (!pp) + return 0; + + memset(&pp[n], 0, sizeof(pp[n])); + return pp; +} + +static char *norm_lit_str(const char *src, int srclen, int multiline, + char *errbuf, int errbufsz) { + char *dst = 0; /* will write to dst[] and return it */ + int max = 0; /* max size of dst[] */ + int off = 0; /* cur offset in dst[] */ + const char *sp = src; + const char *sq = src + srclen; + int ch; + + /* scan forward on src */ + for (;;) { + if (off >= max - 10) { /* have some slack for misc stuff */ + int newmax = max + 50; + char *x = expand(dst, max, newmax); + if (!x) { + xfree(dst); + snprintf(errbuf, errbufsz, "out of memory"); + return 0; + } + dst = x; + max = newmax; + } + + /* finished? */ + if (sp >= sq) + break; + + ch = *sp++; + /* control characters other than tab is not allowed */ + if ((0 <= ch && ch <= 0x08) || (0x0a <= ch && ch <= 0x1f) || (ch == 0x7f)) { + if (!(multiline && (ch == '\r' || ch == '\n'))) { + xfree(dst); + snprintf(errbuf, errbufsz, "invalid char U+%04x", ch); + return 0; + } + } + + // a plain copy suffice + dst[off++] = ch; + } + + dst[off++] = 0; + return dst; +} + +/* + * Convert src to raw unescaped utf-8 string. + * Returns NULL if error with errmsg in errbuf. + */ +static char *norm_basic_str(const char *src, int srclen, int multiline, + char *errbuf, int errbufsz) { + char *dst = 0; /* will write to dst[] and return it */ + int max = 0; /* max size of dst[] */ + int off = 0; /* cur offset in dst[] */ + const char *sp = src; + const char *sq = src + srclen; + int ch; + + /* scan forward on src */ + for (;;) { + if (off >= max - 10) { /* have some slack for misc stuff */ + int newmax = max + 50; + char *x = expand(dst, max, newmax); + if (!x) { + xfree(dst); + snprintf(errbuf, errbufsz, "out of memory"); + return 0; + } + dst = x; + max = newmax; + } + + /* finished? */ + if (sp >= sq) + break; + + ch = *sp++; + if (ch != '\\') { + /* these chars must be escaped: U+0000 to U+0008, U+000A to U+001F, U+007F + */ + if ((0 <= ch && ch <= 0x08) || (0x0a <= ch && ch <= 0x1f) || + (ch == 0x7f)) { + if (!(multiline && (ch == '\r' || ch == '\n'))) { + xfree(dst); + snprintf(errbuf, errbufsz, "invalid char U+%04x", ch); + return 0; + } + } + + // a plain copy suffice + dst[off++] = ch; + continue; + } + + /* ch was backslash. we expect the escape char. */ + if (sp >= sq) { + snprintf(errbuf, errbufsz, "last backslash is invalid"); + xfree(dst); + return 0; + } + + /* for multi-line, we want to kill line-ending-backslash ... */ + if (multiline) { + + // if there is only whitespace after the backslash ... + if (sp[strspn(sp, " \t\r")] == '\n') { + /* skip all the following whitespaces */ + sp += strspn(sp, " \t\r\n"); + continue; + } + } + + /* get the escaped char */ + ch = *sp++; + switch (ch) { + case 'u': + case 'U': { + int64_t ucs = 0; + int nhex = (ch == 'u' ? 4 : 8); + for (int i = 0; i < nhex; i++) { + if (sp >= sq) { + snprintf(errbuf, errbufsz, "\\%c expects %d hex chars", ch, nhex); + xfree(dst); + return 0; + } + ch = *sp++; + int v = ('0' <= ch && ch <= '9') + ? ch - '0' + : (('A' <= ch && ch <= 'F') ? ch - 'A' + 10 : -1); + if (-1 == v) { + snprintf(errbuf, errbufsz, "invalid hex chars for \\u or \\U"); + xfree(dst); + return 0; + } + ucs = ucs * 16 + v; + } + int n = toml_ucs_to_utf8(ucs, &dst[off]); + if (-1 == n) { + snprintf(errbuf, errbufsz, "illegal ucs code in \\u or \\U"); + xfree(dst); + return 0; + } + off += n; + } + continue; + + case 'b': + ch = '\b'; + break; + case 't': + ch = '\t'; + break; + case 'n': + ch = '\n'; + break; + case 'f': + ch = '\f'; + break; + case 'r': + ch = '\r'; + break; + case '"': + ch = '"'; + break; + case '\\': + ch = '\\'; + break; + default: + snprintf(errbuf, errbufsz, "illegal escape char \\%c", ch); + xfree(dst); + return 0; + } + + dst[off++] = ch; + } + + // Cap with NUL and return it. + dst[off++] = 0; + return dst; +} + +/* Normalize a key. Convert all special chars to raw unescaped utf-8 chars. */ +static char *normalize_key(context_t *ctx, token_t strtok) { + const char *sp = strtok.ptr; + const char *sq = strtok.ptr + strtok.len; + int lineno = strtok.lineno; + char *ret; + int ch = *sp; + char ebuf[80]; + + /* handle quoted string */ + if (ch == '\'' || ch == '\"') { + /* if ''' or """, take 3 chars off front and back. Else, take 1 char off. */ + int multiline = 0; + if (sp[1] == ch && sp[2] == ch) { + sp += 3, sq -= 3; + multiline = 1; + } else + sp++, sq--; + + if (ch == '\'') { + /* for single quote, take it verbatim. */ + if (!(ret = STRNDUP(sp, sq - sp))) { + e_outofmemory(ctx, FLINE); + return 0; + } + } else { + /* for double quote, we need to normalize */ + ret = norm_basic_str(sp, sq - sp, multiline, ebuf, sizeof(ebuf)); + if (!ret) { + e_syntax(ctx, lineno, ebuf); + return 0; + } + } + + /* newlines are not allowed in keys */ + if (strchr(ret, '\n')) { + xfree(ret); + e_badkey(ctx, lineno); + return 0; + } + return ret; + } + + /* for bare-key allow only this regex: [A-Za-z0-9_-]+ */ + const char *xp; + for (xp = sp; xp != sq; xp++) { + int k = *xp; + if (isalnum(k)) + continue; + if (k == '_' || k == '-') + continue; + e_badkey(ctx, lineno); + return 0; + } + + /* dup and return it */ + if (!(ret = STRNDUP(sp, sq - sp))) { + e_outofmemory(ctx, FLINE); + return 0; + } + return ret; +} + +/* + * Look up key in tab. Return 0 if not found, or + * 'v'alue, 'a'rray or 't'able depending on the element. + */ +static int check_key(toml_table_t *tab, const char *key, + toml_keyval_t **ret_val, toml_array_t **ret_arr, + toml_table_t **ret_tab) { + int i; + void *dummy; + + if (!ret_tab) + ret_tab = (toml_table_t **)&dummy; + if (!ret_arr) + ret_arr = (toml_array_t **)&dummy; + if (!ret_val) + ret_val = (toml_keyval_t **)&dummy; + + *ret_tab = 0; + *ret_arr = 0; + *ret_val = 0; + + for (i = 0; i < tab->nkval; i++) { + if (0 == strcmp(key, tab->kval[i]->key)) { + *ret_val = tab->kval[i]; + return 'v'; + } + } + for (i = 0; i < tab->narr; i++) { + if (0 == strcmp(key, tab->arr[i]->key)) { + *ret_arr = tab->arr[i]; + return 'a'; + } + } + for (i = 0; i < tab->ntab; i++) { + if (0 == strcmp(key, tab->tab[i]->key)) { + *ret_tab = tab->tab[i]; + return 't'; + } + } + return 0; +} + +static int key_kind(toml_table_t *tab, const char *key) { + return check_key(tab, key, 0, 0, 0); +} + +/* Create a keyval in the table. + */ +static toml_keyval_t *create_keyval_in_table(context_t *ctx, toml_table_t *tab, + token_t keytok) { + /* first, normalize the key to be used for lookup. + * remember to free it if we error out. + */ + char *newkey = normalize_key(ctx, keytok); + if (!newkey) + return 0; + + /* if key exists: error out. */ + toml_keyval_t *dest = 0; + if (key_kind(tab, newkey)) { + xfree(newkey); + e_keyexists(ctx, keytok.lineno); + return 0; + } + + /* make a new entry */ + int n = tab->nkval; + toml_keyval_t **base; + if (0 == (base = (toml_keyval_t **)expand_ptrarr((void **)tab->kval, n))) { + xfree(newkey); + e_outofmemory(ctx, FLINE); + return 0; + } + tab->kval = base; + + if (0 == (base[n] = (toml_keyval_t *)CALLOC(1, sizeof(*base[n])))) { + xfree(newkey); + e_outofmemory(ctx, FLINE); + return 0; + } + dest = tab->kval[tab->nkval++]; + + /* save the key in the new value struct */ + dest->key = newkey; + return dest; +} + +/* Create a table in the table. + */ +static toml_table_t *create_keytable_in_table(context_t *ctx, toml_table_t *tab, + token_t keytok) { + /* first, normalize the key to be used for lookup. + * remember to free it if we error out. + */ + char *newkey = normalize_key(ctx, keytok); + if (!newkey) + return 0; + + /* if key exists: error out */ + toml_table_t *dest = 0; + if (check_key(tab, newkey, 0, 0, &dest)) { + xfree(newkey); /* don't need this anymore */ + + /* special case: if table exists, but was created implicitly ... */ + if (dest && dest->implicit) { + /* we make it explicit now, and simply return it. */ + dest->implicit = false; + return dest; + } + e_keyexists(ctx, keytok.lineno); + return 0; + } + + /* create a new table entry */ + int n = tab->ntab; + toml_table_t **base; + if (0 == (base = (toml_table_t **)expand_ptrarr((void **)tab->tab, n))) { + xfree(newkey); + e_outofmemory(ctx, FLINE); + return 0; + } + tab->tab = base; + + if (0 == (base[n] = (toml_table_t *)CALLOC(1, sizeof(*base[n])))) { + xfree(newkey); + e_outofmemory(ctx, FLINE); + return 0; + } + dest = tab->tab[tab->ntab++]; + + /* save the key in the new table struct */ + dest->key = newkey; + return dest; +} + +/* Create an array in the table. + */ +static toml_array_t *create_keyarray_in_table(context_t *ctx, toml_table_t *tab, + token_t keytok, char kind) { + /* first, normalize the key to be used for lookup. + * remember to free it if we error out. + */ + char *newkey = normalize_key(ctx, keytok); + if (!newkey) + return 0; + + /* if key exists: error out */ + if (key_kind(tab, newkey)) { + xfree(newkey); /* don't need this anymore */ + e_keyexists(ctx, keytok.lineno); + return 0; + } + + /* make a new array entry */ + int n = tab->narr; + toml_array_t **base; + if (0 == (base = (toml_array_t **)expand_ptrarr((void **)tab->arr, n))) { + xfree(newkey); + e_outofmemory(ctx, FLINE); + return 0; + } + tab->arr = base; + + if (0 == (base[n] = (toml_array_t *)CALLOC(1, sizeof(*base[n])))) { + xfree(newkey); + e_outofmemory(ctx, FLINE); + return 0; + } + toml_array_t *dest = tab->arr[tab->narr++]; + + /* save the key in the new array struct */ + dest->key = newkey; + dest->kind = kind; + return dest; +} + +static toml_arritem_t *create_value_in_array(context_t *ctx, + toml_array_t *parent) { + const int n = parent->nitem; + toml_arritem_t *base = expand_arritem(parent->item, n); + if (!base) { + e_outofmemory(ctx, FLINE); + return 0; + } + parent->item = base; + parent->nitem++; + return &parent->item[n]; +} + +/* Create an array in an array + */ +static toml_array_t *create_array_in_array(context_t *ctx, + toml_array_t *parent) { + const int n = parent->nitem; + toml_arritem_t *base = expand_arritem(parent->item, n); + if (!base) { + e_outofmemory(ctx, FLINE); + return 0; + } + toml_array_t *ret = (toml_array_t *)CALLOC(1, sizeof(toml_array_t)); + if (!ret) { + e_outofmemory(ctx, FLINE); + return 0; + } + base[n].arr = ret; + parent->item = base; + parent->nitem++; + return ret; +} + +/* Create a table in an array + */ +static toml_table_t *create_table_in_array(context_t *ctx, + toml_array_t *parent) { + int n = parent->nitem; + toml_arritem_t *base = expand_arritem(parent->item, n); + if (!base) { + e_outofmemory(ctx, FLINE); + return 0; + } + toml_table_t *ret = (toml_table_t *)CALLOC(1, sizeof(toml_table_t)); + if (!ret) { + e_outofmemory(ctx, FLINE); + return 0; + } + base[n].tab = ret; + parent->item = base; + parent->nitem++; + return ret; +} + +static int skip_newlines(context_t *ctx, int isdotspecial) { + while (ctx->tok.tok == NEWLINE) { + if (next_token(ctx, isdotspecial)) + return -1; + if (ctx->tok.eof) + break; + } + return 0; +} + +static int parse_keyval(context_t *ctx, toml_table_t *tab); + +static inline int eat_token(context_t *ctx, tokentype_t typ, int isdotspecial, + const char *fline) { + if (ctx->tok.tok != typ) + return e_internal(ctx, fline); + + if (next_token(ctx, isdotspecial)) + return -1; + + return 0; +} + +/* We are at '{ ... }'. + * Parse the table. + */ +static int parse_inline_table(context_t *ctx, toml_table_t *tab) { + if (eat_token(ctx, LBRACE, 1, FLINE)) + return -1; + + for (;;) { + if (ctx->tok.tok == NEWLINE) + return e_syntax(ctx, ctx->tok.lineno, + "newline not allowed in inline table"); + + /* until } */ + if (ctx->tok.tok == RBRACE) + break; + + if (ctx->tok.tok != STRING) + return e_syntax(ctx, ctx->tok.lineno, "expect a string"); + + if (parse_keyval(ctx, tab)) + return -1; + + if (ctx->tok.tok == NEWLINE) + return e_syntax(ctx, ctx->tok.lineno, + "newline not allowed in inline table"); + + /* on comma, continue to scan for next keyval */ + if (ctx->tok.tok == COMMA) { + if (eat_token(ctx, COMMA, 1, FLINE)) + return -1; + continue; + } + break; + } + + if (eat_token(ctx, RBRACE, 1, FLINE)) + return -1; + + tab->readonly = 1; + + return 0; +} + +static int valtype(const char *val) { + toml_timestamp_t ts; + if (*val == '\'' || *val == '"') + return 's'; + if (0 == toml_rtob(val, 0)) + return 'b'; + if (0 == toml_rtoi(val, 0)) + return 'i'; + if (0 == toml_rtod(val, 0)) + return 'd'; + if (0 == toml_rtots(val, &ts)) { + if (ts.year && ts.hour) + return 'T'; /* timestamp */ + if (ts.year) + return 'D'; /* date */ + return 't'; /* time */ + } + return 'u'; /* unknown */ +} + +/* We are at '[...]' */ +static int parse_array(context_t *ctx, toml_array_t *arr) { + if (eat_token(ctx, LBRACKET, 0, FLINE)) + return -1; + + for (;;) { + if (skip_newlines(ctx, 0)) + return -1; + + /* until ] */ + if (ctx->tok.tok == RBRACKET) + break; + + switch (ctx->tok.tok) { + case STRING: { + /* set array kind if this will be the first entry */ + if (arr->kind == 0) + arr->kind = 'v'; + else if (arr->kind != 'v') + arr->kind = 'm'; + + char *val = ctx->tok.ptr; + int vlen = ctx->tok.len; + + /* make a new value in array */ + toml_arritem_t *newval = create_value_in_array(ctx, arr); + if (!newval) + return e_outofmemory(ctx, FLINE); + + if (!(newval->val = STRNDUP(val, vlen))) + return e_outofmemory(ctx, FLINE); + + newval->valtype = valtype(newval->val); + + /* set array type if this is the first entry */ + if (arr->nitem == 1) + arr->type = newval->valtype; + else if (arr->type != newval->valtype) + arr->type = 'm'; /* mixed */ + + if (eat_token(ctx, STRING, 0, FLINE)) + return -1; + break; + } + + case LBRACKET: { /* [ [array], [array] ... ] */ + /* set the array kind if this will be the first entry */ + if (arr->kind == 0) + arr->kind = 'a'; + else if (arr->kind != 'a') + arr->kind = 'm'; + + toml_array_t *subarr = create_array_in_array(ctx, arr); + if (!subarr) + return -1; + if (parse_array(ctx, subarr)) + return -1; + break; + } + + case LBRACE: { /* [ {table}, {table} ... ] */ + /* set the array kind if this will be the first entry */ + if (arr->kind == 0) + arr->kind = 't'; + else if (arr->kind != 't') + arr->kind = 'm'; + + toml_table_t *subtab = create_table_in_array(ctx, arr); + if (!subtab) + return -1; + if (parse_inline_table(ctx, subtab)) + return -1; + break; + } + + default: + return e_syntax(ctx, ctx->tok.lineno, "syntax error"); + } + + if (skip_newlines(ctx, 0)) + return -1; + + /* on comma, continue to scan for next element */ + if (ctx->tok.tok == COMMA) { + if (eat_token(ctx, COMMA, 0, FLINE)) + return -1; + continue; + } + break; + } + + if (eat_token(ctx, RBRACKET, 1, FLINE)) + return -1; + return 0; +} + +/* handle lines like these: + key = "value" + key = [ array ] + key = { table } +*/ +static int parse_keyval(context_t *ctx, toml_table_t *tab) { + if (tab->readonly) { + return e_forbid(ctx, ctx->tok.lineno, + "cannot insert new entry into existing table"); + } + + token_t key = ctx->tok; + if (eat_token(ctx, STRING, 1, FLINE)) + return -1; + + if (ctx->tok.tok == DOT) { + /* handle inline dotted key. + e.g. + physical.color = "orange" + physical.shape = "round" + */ + toml_table_t *subtab = 0; + { + char *subtabstr = normalize_key(ctx, key); + if (!subtabstr) + return -1; + + subtab = toml_table_in(tab, subtabstr); + xfree(subtabstr); + } + if (!subtab) { + subtab = create_keytable_in_table(ctx, tab, key); + if (!subtab) + return -1; + } + if (next_token(ctx, 1)) + return -1; + if (parse_keyval(ctx, subtab)) + return -1; + return 0; + } + + if (ctx->tok.tok != EQUAL) { + return e_syntax(ctx, ctx->tok.lineno, "missing ="); + } + + if (next_token(ctx, 0)) + return -1; + + switch (ctx->tok.tok) { + case STRING: { /* key = "value" */ + toml_keyval_t *keyval = create_keyval_in_table(ctx, tab, key); + if (!keyval) + return -1; + token_t val = ctx->tok; + + assert(keyval->val == 0); + if (!(keyval->val = STRNDUP(val.ptr, val.len))) + return e_outofmemory(ctx, FLINE); + + if (next_token(ctx, 1)) + return -1; + + return 0; + } + + case LBRACKET: { /* key = [ array ] */ + toml_array_t *arr = create_keyarray_in_table(ctx, tab, key, 0); + if (!arr) + return -1; + if (parse_array(ctx, arr)) + return -1; + return 0; + } + + case LBRACE: { /* key = { table } */ + toml_table_t *nxttab = create_keytable_in_table(ctx, tab, key); + if (!nxttab) + return -1; + if (parse_inline_table(ctx, nxttab)) + return -1; + return 0; + } + + default: + return e_syntax(ctx, ctx->tok.lineno, "syntax error"); + } + return 0; +} + +typedef struct tabpath_t tabpath_t; +struct tabpath_t { + int cnt; + token_t key[10]; +}; + +/* at [x.y.z] or [[x.y.z]] + * Scan forward and fill tabpath until it enters ] or ]] + * There will be at least one entry on return. + */ +static int fill_tabpath(context_t *ctx) { + int lineno = ctx->tok.lineno; + int i; + + /* clear tpath */ + for (i = 0; i < ctx->tpath.top; i++) { + char **p = &ctx->tpath.key[i]; + xfree(*p); + *p = 0; + } + ctx->tpath.top = 0; + + for (;;) { + if (ctx->tpath.top >= 10) + return e_syntax(ctx, lineno, + "table path is too deep; max allowed is 10."); + + if (ctx->tok.tok != STRING) + return e_syntax(ctx, lineno, "invalid or missing key"); + + char *key = normalize_key(ctx, ctx->tok); + if (!key) + return -1; + ctx->tpath.tok[ctx->tpath.top] = ctx->tok; + ctx->tpath.key[ctx->tpath.top] = key; + ctx->tpath.top++; + + if (next_token(ctx, 1)) + return -1; + + if (ctx->tok.tok == RBRACKET) + break; + + if (ctx->tok.tok != DOT) + return e_syntax(ctx, lineno, "invalid key"); + + if (next_token(ctx, 1)) + return -1; + } + + if (ctx->tpath.top <= 0) + return e_syntax(ctx, lineno, "empty table selector"); + + return 0; +} + +/* Walk tabpath from the root, and create new tables on the way. + * Sets ctx->curtab to the final table. + */ +static int walk_tabpath(context_t *ctx) { + /* start from root */ + toml_table_t *curtab = ctx->root; + + for (int i = 0; i < ctx->tpath.top; i++) { + const char *key = ctx->tpath.key[i]; + + toml_keyval_t *nextval = 0; + toml_array_t *nextarr = 0; + toml_table_t *nexttab = 0; + switch (check_key(curtab, key, &nextval, &nextarr, &nexttab)) { + case 't': + /* found a table. nexttab is where we will go next. */ + break; + + case 'a': + /* found an array. nexttab is the last table in the array. */ + if (nextarr->kind != 't') + return e_internal(ctx, FLINE); + + if (nextarr->nitem == 0) + return e_internal(ctx, FLINE); + + nexttab = nextarr->item[nextarr->nitem - 1].tab; + break; + + case 'v': + return e_keyexists(ctx, ctx->tpath.tok[i].lineno); + + default: { /* Not found. Let's create an implicit table. */ + int n = curtab->ntab; + toml_table_t **base = + (toml_table_t **)expand_ptrarr((void **)curtab->tab, n); + if (0 == base) + return e_outofmemory(ctx, FLINE); + + curtab->tab = base; + + if (0 == (base[n] = (toml_table_t *)CALLOC(1, sizeof(*base[n])))) + return e_outofmemory(ctx, FLINE); + + if (0 == (base[n]->key = STRDUP(key))) + return e_outofmemory(ctx, FLINE); + + nexttab = curtab->tab[curtab->ntab++]; + + /* tabs created by walk_tabpath are considered implicit */ + nexttab->implicit = true; + } break; + } + + /* switch to next tab */ + curtab = nexttab; + } + + /* save it */ + ctx->curtab = curtab; + + return 0; +} + +/* handle lines like [x.y.z] or [[x.y.z]] */ +static int parse_select(context_t *ctx) { + assert(ctx->tok.tok == LBRACKET); + + /* true if [[ */ + int llb = (ctx->tok.ptr + 1 < ctx->stop && ctx->tok.ptr[1] == '['); + /* need to detect '[[' on our own because next_token() will skip whitespace, + and '[ [' would be taken as '[[', which is wrong. */ + + /* eat [ or [[ */ + if (eat_token(ctx, LBRACKET, 1, FLINE)) + return -1; + if (llb) { + assert(ctx->tok.tok == LBRACKET); + if (eat_token(ctx, LBRACKET, 1, FLINE)) + return -1; + } + + if (fill_tabpath(ctx)) + return -1; + + /* For [x.y.z] or [[x.y.z]], remove z from tpath. + */ + token_t z = ctx->tpath.tok[ctx->tpath.top - 1]; + xfree(ctx->tpath.key[ctx->tpath.top - 1]); + ctx->tpath.top--; + + /* set up ctx->curtab */ + if (walk_tabpath(ctx)) + return -1; + + if (!llb) { + /* [x.y.z] -> create z = {} in x.y */ + toml_table_t *curtab = create_keytable_in_table(ctx, ctx->curtab, z); + if (!curtab) + return -1; + ctx->curtab = curtab; + } else { + /* [[x.y.z]] -> create z = [] in x.y */ + toml_array_t *arr = 0; + { + char *zstr = normalize_key(ctx, z); + if (!zstr) + return -1; + arr = toml_array_in(ctx->curtab, zstr); + xfree(zstr); + } + if (!arr) { + arr = create_keyarray_in_table(ctx, ctx->curtab, z, 't'); + if (!arr) + return -1; + } + if (arr->kind != 't') + return e_syntax(ctx, z.lineno, "array mismatch"); + + /* add to z[] */ + toml_table_t *dest; + { + toml_table_t *t = create_table_in_array(ctx, arr); + if (!t) + return -1; + + if (0 == (t->key = STRDUP("__anon__"))) + return e_outofmemory(ctx, FLINE); + + dest = t; + } + + ctx->curtab = dest; + } + + if (ctx->tok.tok != RBRACKET) { + return e_syntax(ctx, ctx->tok.lineno, "expects ]"); + } + if (llb) { + if (!(ctx->tok.ptr + 1 < ctx->stop && ctx->tok.ptr[1] == ']')) { + return e_syntax(ctx, ctx->tok.lineno, "expects ]]"); + } + if (eat_token(ctx, RBRACKET, 1, FLINE)) + return -1; + } + + if (eat_token(ctx, RBRACKET, 1, FLINE)) + return -1; + + if (ctx->tok.tok != NEWLINE) + return e_syntax(ctx, ctx->tok.lineno, "extra chars after ] or ]]"); + + return 0; +} + +toml_table_t *toml_parse(char *conf, char *errbuf, int errbufsz) { + context_t ctx; + + // clear errbuf + if (errbufsz <= 0) + errbufsz = 0; + if (errbufsz > 0) + errbuf[0] = 0; + + // init context + memset(&ctx, 0, sizeof(ctx)); + ctx.start = conf; + ctx.stop = ctx.start + strlen(conf); + ctx.errbuf = errbuf; + ctx.errbufsz = errbufsz; + + // start with an artificial newline of length 0 + ctx.tok.tok = NEWLINE; + ctx.tok.lineno = 1; + ctx.tok.ptr = conf; + ctx.tok.len = 0; + + // make a root table + if (0 == (ctx.root = CALLOC(1, sizeof(*ctx.root)))) { + e_outofmemory(&ctx, FLINE); + // Do not goto fail, root table not set up yet + return 0; + } + + // set root as default table + ctx.curtab = ctx.root; + + /* Scan forward until EOF */ + for (token_t tok = ctx.tok; !tok.eof; tok = ctx.tok) { + switch (tok.tok) { + + case NEWLINE: + if (next_token(&ctx, 1)) + goto fail; + break; + + case STRING: + if (parse_keyval(&ctx, ctx.curtab)) + goto fail; + + if (ctx.tok.tok != NEWLINE) { + e_syntax(&ctx, ctx.tok.lineno, "extra chars after value"); + goto fail; + } + + if (eat_token(&ctx, NEWLINE, 1, FLINE)) + goto fail; + break; + + case LBRACKET: /* [ x.y.z ] or [[ x.y.z ]] */ + if (parse_select(&ctx)) + goto fail; + break; + + default: + e_syntax(&ctx, tok.lineno, "syntax error"); + goto fail; + } + } + + /* success */ + for (int i = 0; i < ctx.tpath.top; i++) + xfree(ctx.tpath.key[i]); + return ctx.root; + +fail: + // Something bad has happened. Free resources and return error. + for (int i = 0; i < ctx.tpath.top; i++) + xfree(ctx.tpath.key[i]); + toml_free(ctx.root); + return 0; +} + +toml_table_t *toml_parse_file(FILE *fp, char *errbuf, int errbufsz) { + int bufsz = 0; + char *buf = 0; + int off = 0; + + /* read from fp into buf */ + while (!feof(fp)) { + + if (off == bufsz) { + int xsz = bufsz + 1000; + char *x = expand(buf, bufsz, xsz); + if (!x) { + snprintf(errbuf, errbufsz, "out of memory"); + xfree(buf); + return 0; + } + buf = x; + bufsz = xsz; + } + + errno = 0; + int n = fread(buf + off, 1, bufsz - off, fp); + if (ferror(fp)) { + snprintf(errbuf, errbufsz, "%s", + errno ? strerror(errno) : "Error reading file"); + xfree(buf); + return 0; + } + off += n; + } + + /* tag on a NUL to cap the string */ + if (off == bufsz) { + int xsz = bufsz + 1; + char *x = expand(buf, bufsz, xsz); + if (!x) { + snprintf(errbuf, errbufsz, "out of memory"); + xfree(buf); + return 0; + } + buf = x; + bufsz = xsz; + } + buf[off] = 0; + + /* parse it, cleanup and finish */ + toml_table_t *ret = toml_parse(buf, errbuf, errbufsz); + xfree(buf); + return ret; +} + +static void xfree_kval(toml_keyval_t *p) { + if (!p) + return; + xfree(p->key); + xfree(p->val); + xfree(p); +} + +static void xfree_tab(toml_table_t *p); + +static void xfree_arr(toml_array_t *p) { + if (!p) + return; + + xfree(p->key); + const int n = p->nitem; + for (int i = 0; i < n; i++) { + toml_arritem_t *a = &p->item[i]; + if (a->val) + xfree(a->val); + else if (a->arr) + xfree_arr(a->arr); + else if (a->tab) + xfree_tab(a->tab); + } + xfree(p->item); + xfree(p); +} + +static void xfree_tab(toml_table_t *p) { + int i; + + if (!p) + return; + + xfree(p->key); + + for (i = 0; i < p->nkval; i++) + xfree_kval(p->kval[i]); + xfree(p->kval); + + for (i = 0; i < p->narr; i++) + xfree_arr(p->arr[i]); + xfree(p->arr); + + for (i = 0; i < p->ntab; i++) + xfree_tab(p->tab[i]); + xfree(p->tab); + + xfree(p); +} + +void toml_free(toml_table_t *tab) { xfree_tab(tab); } + +static void set_token(context_t *ctx, tokentype_t tok, int lineno, char *ptr, + int len) { + token_t t; + t.tok = tok; + t.lineno = lineno; + t.ptr = ptr; + t.len = len; + t.eof = 0; + ctx->tok = t; +} + +static void set_eof(context_t *ctx, int lineno) { + set_token(ctx, NEWLINE, lineno, ctx->stop, 0); + ctx->tok.eof = 1; +} + +/* Scan p for n digits compositing entirely of [0-9] */ +static int scan_digits(const char *p, int n) { + int ret = 0; + for (; n > 0 && isdigit(*p); n--, p++) { + ret = 10 * ret + (*p - '0'); + } + return n ? -1 : ret; +} + +static int scan_date(const char *p, int *YY, int *MM, int *DD) { + int year, month, day; + year = scan_digits(p, 4); + month = (year >= 0 && p[4] == '-') ? scan_digits(p + 5, 2) : -1; + day = (month >= 0 && p[7] == '-') ? scan_digits(p + 8, 2) : -1; + if (YY) + *YY = year; + if (MM) + *MM = month; + if (DD) + *DD = day; + return (year >= 0 && month >= 0 && day >= 0) ? 0 : -1; +} + +static int scan_time(const char *p, int *hh, int *mm, int *ss) { + int hour, minute, second; + hour = scan_digits(p, 2); + minute = (hour >= 0 && p[2] == ':') ? scan_digits(p + 3, 2) : -1; + second = (minute >= 0 && p[5] == ':') ? scan_digits(p + 6, 2) : -1; + if (hh) + *hh = hour; + if (mm) + *mm = minute; + if (ss) + *ss = second; + return (hour >= 0 && minute >= 0 && second >= 0) ? 0 : -1; +} + +static int scan_string(context_t *ctx, char *p, int lineno, int dotisspecial) { + char *orig = p; + if (0 == strncmp(p, "'''", 3)) { + char *q = p + 3; + + while (1) { + q = strstr(q, "'''"); + if (0 == q) { + return e_syntax(ctx, lineno, "unterminated triple-s-quote"); + } + while (q[3] == '\'') + q++; + break; + } + + set_token(ctx, STRING, lineno, orig, q + 3 - orig); + return 0; + } + + if (0 == strncmp(p, "\"\"\"", 3)) { + char *q = p + 3; + + while (1) { + q = strstr(q, "\"\"\""); + if (0 == q) { + return e_syntax(ctx, lineno, "unterminated triple-d-quote"); + } + if (q[-1] == '\\') { + q++; + continue; + } + while (q[3] == '\"') + q++; + break; + } + + // the string is [p+3, q-1] + + int hexreq = 0; /* #hex required */ + int escape = 0; + for (p += 3; p < q; p++) { + if (escape) { + escape = 0; + if (strchr("btnfr\"\\", *p)) + continue; + if (*p == 'u') { + hexreq = 4; + continue; + } + if (*p == 'U') { + hexreq = 8; + continue; + } + if (p[strspn(p, " \t\r")] == '\n') + continue; /* allow for line ending backslash */ + return e_syntax(ctx, lineno, "bad escape char"); + } + if (hexreq) { + hexreq--; + if (strchr("0123456789ABCDEF", *p)) + continue; + return e_syntax(ctx, lineno, "expect hex char"); + } + if (*p == '\\') { + escape = 1; + continue; + } + } + if (escape) + return e_syntax(ctx, lineno, "expect an escape char"); + if (hexreq) + return e_syntax(ctx, lineno, "expected more hex char"); + + set_token(ctx, STRING, lineno, orig, q + 3 - orig); + return 0; + } + + if ('\'' == *p) { + for (p++; *p && *p != '\n' && *p != '\''; p++) + ; + if (*p != '\'') { + return e_syntax(ctx, lineno, "unterminated s-quote"); + } + + set_token(ctx, STRING, lineno, orig, p + 1 - orig); + return 0; + } + + if ('\"' == *p) { + int hexreq = 0; /* #hex required */ + int escape = 0; + for (p++; *p; p++) { + if (escape) { + escape = 0; + if (strchr("btnfr\"\\", *p)) + continue; + if (*p == 'u') { + hexreq = 4; + continue; + } + if (*p == 'U') { + hexreq = 8; + continue; + } + return e_syntax(ctx, lineno, "bad escape char"); + } + if (hexreq) { + hexreq--; + if (strchr("0123456789ABCDEF", *p)) + continue; + return e_syntax(ctx, lineno, "expect hex char"); + } + if (*p == '\\') { + escape = 1; + continue; + } + if (*p == '\'') { + if (p[1] == '\'' && p[2] == '\'') { + return e_syntax(ctx, lineno, "triple-s-quote inside string lit"); + } + continue; + } + if (*p == '\n') + break; + if (*p == '"') + break; + } + if (*p != '"') { + return e_syntax(ctx, lineno, "unterminated quote"); + } + + set_token(ctx, STRING, lineno, orig, p + 1 - orig); + return 0; + } + + /* check for timestamp without quotes */ + if (0 == scan_date(p, 0, 0, 0) || 0 == scan_time(p, 0, 0, 0)) { + // forward thru the timestamp + p += strspn(p, "0123456789.:+-T Z"); + // squeeze out any spaces at end of string + for (; p[-1] == ' '; p--) + ; + // tokenize + set_token(ctx, STRING, lineno, orig, p - orig); + return 0; + } + + /* literals */ + for (; *p && *p != '\n'; p++) { + int ch = *p; + if (ch == '.' && dotisspecial) + break; + if ('A' <= ch && ch <= 'Z') + continue; + if ('a' <= ch && ch <= 'z') + continue; + if (strchr("0123456789+-_.", ch)) + continue; + break; + } + + set_token(ctx, STRING, lineno, orig, p - orig); + return 0; +} + +static int next_token(context_t *ctx, int dotisspecial) { + int lineno = ctx->tok.lineno; + char *p = ctx->tok.ptr; + int i; + + /* eat this tok */ + for (i = 0; i < ctx->tok.len; i++) { + if (*p++ == '\n') + lineno++; + } + + /* make next tok */ + while (p < ctx->stop) { + /* skip comment. stop just before the \n. */ + if (*p == '#') { + for (p++; p < ctx->stop && *p != '\n'; p++) + ; + continue; + } + + if (dotisspecial && *p == '.') { + set_token(ctx, DOT, lineno, p, 1); + return 0; + } + + switch (*p) { + case ',': + set_token(ctx, COMMA, lineno, p, 1); + return 0; + case '=': + set_token(ctx, EQUAL, lineno, p, 1); + return 0; + case '{': + set_token(ctx, LBRACE, lineno, p, 1); + return 0; + case '}': + set_token(ctx, RBRACE, lineno, p, 1); + return 0; + case '[': + set_token(ctx, LBRACKET, lineno, p, 1); + return 0; + case ']': + set_token(ctx, RBRACKET, lineno, p, 1); + return 0; + case '\n': + set_token(ctx, NEWLINE, lineno, p, 1); + return 0; + case '\r': + case ' ': + case '\t': + /* ignore white spaces */ + p++; + continue; + } + + return scan_string(ctx, p, lineno, dotisspecial); + } + + set_eof(ctx, lineno); + return 0; +} + +const char *toml_key_in(const toml_table_t *tab, int keyidx) { + if (keyidx < tab->nkval) + return tab->kval[keyidx]->key; + + keyidx -= tab->nkval; + if (keyidx < tab->narr) + return tab->arr[keyidx]->key; + + keyidx -= tab->narr; + if (keyidx < tab->ntab) + return tab->tab[keyidx]->key; + + return 0; +} + +int toml_key_exists(const toml_table_t *tab, const char *key) { + int i; + for (i = 0; i < tab->nkval; i++) { + if (0 == strcmp(key, tab->kval[i]->key)) + return 1; + } + for (i = 0; i < tab->narr; i++) { + if (0 == strcmp(key, tab->arr[i]->key)) + return 1; + } + for (i = 0; i < tab->ntab; i++) { + if (0 == strcmp(key, tab->tab[i]->key)) + return 1; + } + return 0; +} + +toml_raw_t toml_raw_in(const toml_table_t *tab, const char *key) { + int i; + for (i = 0; i < tab->nkval; i++) { + if (0 == strcmp(key, tab->kval[i]->key)) + return tab->kval[i]->val; + } + return 0; +} + +toml_array_t *toml_array_in(const toml_table_t *tab, const char *key) { + int i; + for (i = 0; i < tab->narr; i++) { + if (0 == strcmp(key, tab->arr[i]->key)) + return tab->arr[i]; + } + return 0; +} + +toml_table_t *toml_table_in(const toml_table_t *tab, const char *key) { + int i; + for (i = 0; i < tab->ntab; i++) { + if (0 == strcmp(key, tab->tab[i]->key)) + return tab->tab[i]; + } + return 0; +} + +toml_raw_t toml_raw_at(const toml_array_t *arr, int idx) { + return (0 <= idx && idx < arr->nitem) ? arr->item[idx].val : 0; +} + +char toml_array_kind(const toml_array_t *arr) { return arr->kind; } + +char toml_array_type(const toml_array_t *arr) { + if (arr->kind != 'v') + return 0; + + if (arr->nitem == 0) + return 0; + + return arr->type; +} + +int toml_array_nelem(const toml_array_t *arr) { return arr->nitem; } + +const char *toml_array_key(const toml_array_t *arr) { + return arr ? arr->key : (const char *)NULL; +} + +int toml_table_nkval(const toml_table_t *tab) { return tab->nkval; } + +int toml_table_narr(const toml_table_t *tab) { return tab->narr; } + +int toml_table_ntab(const toml_table_t *tab) { return tab->ntab; } + +const char *toml_table_key(const toml_table_t *tab) { + return tab ? tab->key : (const char *)NULL; +} + +toml_array_t *toml_array_at(const toml_array_t *arr, int idx) { + return (0 <= idx && idx < arr->nitem) ? arr->item[idx].arr : 0; +} + +toml_table_t *toml_table_at(const toml_array_t *arr, int idx) { + return (0 <= idx && idx < arr->nitem) ? arr->item[idx].tab : 0; +} + +static int parse_millisec(const char *p, const char **endp); + +int toml_rtots(toml_raw_t src_, toml_timestamp_t *ret) { + if (!src_) + return -1; + + const char *p = src_; + int must_parse_time = 0; + + memset(ret, 0, sizeof(*ret)); + + int *year = &ret->__buffer.year; + int *month = &ret->__buffer.month; + int *day = &ret->__buffer.day; + int *hour = &ret->__buffer.hour; + int *minute = &ret->__buffer.minute; + int *second = &ret->__buffer.second; + int *millisec = &ret->__buffer.millisec; + + /* parse date YYYY-MM-DD */ + if (0 == scan_date(p, year, month, day)) { + ret->year = year; + ret->month = month; + ret->day = day; + + p += 10; + if (*p) { + // parse the T or space separator + if (*p != 'T' && *p != ' ') + return -1; + must_parse_time = 1; + p++; + } + } + + /* parse time HH:MM:SS */ + if (0 == scan_time(p, hour, minute, second)) { + ret->hour = hour; + ret->minute = minute; + ret->second = second; + + /* optionally, parse millisec */ + p += 8; + if (*p == '.') { + p++; /* skip '.' */ + const char *qq; + *millisec = parse_millisec(p, &qq); + ret->millisec = millisec; + p = qq; + } + + if (*p) { + /* parse and copy Z */ + char *z = ret->__buffer.z; + ret->z = z; + if (*p == 'Z' || *p == 'z') { + *z++ = 'Z'; + p++; + *z = 0; + + } else if (*p == '+' || *p == '-') { + *z++ = *p++; + + if (!(isdigit(p[0]) && isdigit(p[1]))) + return -1; + *z++ = *p++; + *z++ = *p++; + + if (*p == ':') { + *z++ = *p++; + + if (!(isdigit(p[0]) && isdigit(p[1]))) + return -1; + *z++ = *p++; + *z++ = *p++; + } + + *z = 0; + } + } + } + if (*p != 0) + return -1; + + if (must_parse_time && !ret->hour) + return -1; + + return 0; +} + +/* Raw to boolean */ +int toml_rtob(toml_raw_t src, int *ret_) { + if (!src) + return -1; + int dummy; + int *ret = ret_ ? ret_ : &dummy; + + if (0 == strcmp(src, "true")) { + *ret = 1; + return 0; + } + if (0 == strcmp(src, "false")) { + *ret = 0; + return 0; + } + return -1; +} + +/* Raw to integer */ +int toml_rtoi(toml_raw_t src, int64_t *ret_) { + if (!src) + return -1; + + char buf[100]; + char *p = buf; + char *q = p + sizeof(buf); + const char *s = src; + int base = 0; + int64_t dummy; + int64_t *ret = ret_ ? ret_ : &dummy; + + /* allow +/- */ + if (s[0] == '+' || s[0] == '-') + *p++ = *s++; + + /* disallow +_100 */ + if (s[0] == '_') + return -1; + + /* if 0* ... */ + if ('0' == s[0]) { + switch (s[1]) { + case 'x': + base = 16; + s += 2; + break; + case 'o': + base = 8; + s += 2; + break; + case 'b': + base = 2; + s += 2; + break; + case '\0': + return *ret = 0, 0; + default: + /* ensure no other digits after it */ + if (s[1]) + return -1; + } + } + + /* just strip underscores and pass to strtoll */ + while (*s && p < q) { + int ch = *s++; + if (ch == '_') { + // disallow '__' + if (s[0] == '_') + return -1; + // numbers cannot end with '_' + if (s[0] == '\0') + return -1; + continue; /* skip _ */ + } + *p++ = ch; + } + + // if not at end-of-string or we ran out of buffer ... + if (*s || p == q) + return -1; + + /* cap with NUL */ + *p = 0; + + /* Run strtoll on buf to get the integer */ + char *endp; + errno = 0; + *ret = strtoll(buf, &endp, base); + return (errno || *endp) ? -1 : 0; +} + +int toml_rtod_ex(toml_raw_t src, double *ret_, char *buf, int buflen) { + if (!src) + return -1; + + char *p = buf; + char *q = p + buflen; + const char *s = src; + double dummy; + double *ret = ret_ ? ret_ : &dummy; + + /* allow +/- */ + if (s[0] == '+' || s[0] == '-') + *p++ = *s++; + + /* disallow +_1.00 */ + if (s[0] == '_') + return -1; + + /* decimal point, if used, must be surrounded by at least one digit on each + * side */ + { + char *dot = strchr(s, '.'); + if (dot) { + if (dot == s || !isdigit(dot[-1]) || !isdigit(dot[1])) + return -1; + } + } + + /* zero must be followed by . or 'e', or NUL */ + if (s[0] == '0' && s[1] && !strchr("eE.", s[1])) + return -1; + + /* just strip underscores and pass to strtod */ + while (*s && p < q) { + int ch = *s++; + if (ch == '_') { + // disallow '__' + if (s[0] == '_') + return -1; + // disallow last char '_' + if (s[0] == 0) + return -1; + continue; /* skip _ */ + } + *p++ = ch; + } + if (*s || p == q) + return -1; /* reached end of string or buffer is full? */ + + /* cap with NUL */ + *p = 0; + + /* Run strtod on buf to get the value */ + char *endp; + errno = 0; + *ret = strtod(buf, &endp); + return (errno || *endp) ? -1 : 0; +} + +int toml_rtod(toml_raw_t src, double *ret_) { + char buf[100]; + return toml_rtod_ex(src, ret_, buf, sizeof(buf)); +} + +int toml_rtos(toml_raw_t src, char **ret) { + int multiline = 0; + const char *sp; + const char *sq; + + *ret = 0; + if (!src) + return -1; + + int qchar = src[0]; + int srclen = strlen(src); + if (!(qchar == '\'' || qchar == '"')) { + return -1; + } + + // triple quotes? + if (qchar == src[1] && qchar == src[2]) { + multiline = 1; + sp = src + 3; + sq = src + srclen - 3; + /* last 3 chars in src must be qchar */ + if (!(sp <= sq && sq[0] == qchar && sq[1] == qchar && sq[2] == qchar)) + return -1; + + /* skip new line immediate after qchar */ + if (sp[0] == '\n') + sp++; + else if (sp[0] == '\r' && sp[1] == '\n') + sp += 2; + + } else { + sp = src + 1; + sq = src + srclen - 1; + /* last char in src must be qchar */ + if (!(sp <= sq && *sq == qchar)) + return -1; + } + + if (qchar == '\'') { + *ret = norm_lit_str(sp, sq - sp, multiline, 0, 0); + } else { + *ret = norm_basic_str(sp, sq - sp, multiline, 0, 0); + } + + return *ret ? 0 : -1; +} + +toml_datum_t toml_string_at(const toml_array_t *arr, int idx) { + toml_datum_t ret; + memset(&ret, 0, sizeof(ret)); + ret.ok = (0 == toml_rtos(toml_raw_at(arr, idx), &ret.u.s)); + return ret; +} + +toml_datum_t toml_bool_at(const toml_array_t *arr, int idx) { + toml_datum_t ret; + memset(&ret, 0, sizeof(ret)); + ret.ok = (0 == toml_rtob(toml_raw_at(arr, idx), &ret.u.b)); + return ret; +} + +toml_datum_t toml_int_at(const toml_array_t *arr, int idx) { + toml_datum_t ret; + memset(&ret, 0, sizeof(ret)); + ret.ok = (0 == toml_rtoi(toml_raw_at(arr, idx), &ret.u.i)); + return ret; +} + +toml_datum_t toml_double_at(const toml_array_t *arr, int idx) { + toml_datum_t ret; + memset(&ret, 0, sizeof(ret)); + ret.ok = (0 == toml_rtod(toml_raw_at(arr, idx), &ret.u.d)); + return ret; +} + +toml_datum_t toml_timestamp_at(const toml_array_t *arr, int idx) { + toml_timestamp_t ts; + toml_datum_t ret; + memset(&ret, 0, sizeof(ret)); + ret.ok = (0 == toml_rtots(toml_raw_at(arr, idx), &ts)); + if (ret.ok) { + ret.ok = !!(ret.u.ts = MALLOC(sizeof(*ret.u.ts))); + if (ret.ok) { + *ret.u.ts = ts; + if (ret.u.ts->year) + ret.u.ts->year = &ret.u.ts->__buffer.year; + if (ret.u.ts->month) + ret.u.ts->month = &ret.u.ts->__buffer.month; + if (ret.u.ts->day) + ret.u.ts->day = &ret.u.ts->__buffer.day; + if (ret.u.ts->hour) + ret.u.ts->hour = &ret.u.ts->__buffer.hour; + if (ret.u.ts->minute) + ret.u.ts->minute = &ret.u.ts->__buffer.minute; + if (ret.u.ts->second) + ret.u.ts->second = &ret.u.ts->__buffer.second; + if (ret.u.ts->millisec) + ret.u.ts->millisec = &ret.u.ts->__buffer.millisec; + if (ret.u.ts->z) + ret.u.ts->z = ret.u.ts->__buffer.z; + } + } + return ret; +} + +toml_datum_t toml_string_in(const toml_table_t *arr, const char *key) { + toml_datum_t ret; + memset(&ret, 0, sizeof(ret)); + toml_raw_t raw = toml_raw_in(arr, key); + if (raw) { + ret.ok = (0 == toml_rtos(raw, &ret.u.s)); + } + return ret; +} + +toml_datum_t toml_bool_in(const toml_table_t *arr, const char *key) { + toml_datum_t ret; + memset(&ret, 0, sizeof(ret)); + ret.ok = (0 == toml_rtob(toml_raw_in(arr, key), &ret.u.b)); + return ret; +} + +toml_datum_t toml_int_in(const toml_table_t *arr, const char *key) { + toml_datum_t ret; + memset(&ret, 0, sizeof(ret)); + ret.ok = (0 == toml_rtoi(toml_raw_in(arr, key), &ret.u.i)); + return ret; +} + +toml_datum_t toml_double_in(const toml_table_t *arr, const char *key) { + toml_datum_t ret; + memset(&ret, 0, sizeof(ret)); + ret.ok = (0 == toml_rtod(toml_raw_in(arr, key), &ret.u.d)); + return ret; +} + +toml_datum_t toml_timestamp_in(const toml_table_t *arr, const char *key) { + toml_timestamp_t ts; + toml_datum_t ret; + memset(&ret, 0, sizeof(ret)); + ret.ok = (0 == toml_rtots(toml_raw_in(arr, key), &ts)); + if (ret.ok) { + ret.ok = !!(ret.u.ts = MALLOC(sizeof(*ret.u.ts))); + if (ret.ok) { + *ret.u.ts = ts; + if (ret.u.ts->year) + ret.u.ts->year = &ret.u.ts->__buffer.year; + if (ret.u.ts->month) + ret.u.ts->month = &ret.u.ts->__buffer.month; + if (ret.u.ts->day) + ret.u.ts->day = &ret.u.ts->__buffer.day; + if (ret.u.ts->hour) + ret.u.ts->hour = &ret.u.ts->__buffer.hour; + if (ret.u.ts->minute) + ret.u.ts->minute = &ret.u.ts->__buffer.minute; + if (ret.u.ts->second) + ret.u.ts->second = &ret.u.ts->__buffer.second; + if (ret.u.ts->millisec) + ret.u.ts->millisec = &ret.u.ts->__buffer.millisec; + if (ret.u.ts->z) + ret.u.ts->z = ret.u.ts->__buffer.z; + } + } + return ret; +} + +static int parse_millisec(const char *p, const char **endp) { + int ret = 0; + int unit = 100; /* unit in millisec */ + for (; '0' <= *p && *p <= '9'; p++, unit /= 10) { + ret += (*p - '0') * unit; + } + *endp = p; + return ret; +} diff --git a/deps/toml/toml.h b/deps/toml/toml.h new file mode 100644 index 0000000..19dc3d2 --- /dev/null +++ b/deps/toml/toml.h @@ -0,0 +1,175 @@ +/* + MIT License + + Copyright (c) CK Tan + https://github.com/cktan/tomlc99 + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#ifndef TOML_H +#define TOML_H + +#ifdef _MSC_VER +#pragma warning(disable: 4996) +#endif + +#include +#include + +#ifdef __cplusplus +#define TOML_EXTERN extern "C" +#else +#define TOML_EXTERN extern +#endif + +typedef struct toml_timestamp_t toml_timestamp_t; +typedef struct toml_table_t toml_table_t; +typedef struct toml_array_t toml_array_t; +typedef struct toml_datum_t toml_datum_t; + +/* Parse a file. Return a table on success, or 0 otherwise. + * Caller must toml_free(the-return-value) after use. + */ +TOML_EXTERN toml_table_t *toml_parse_file(FILE *fp, char *errbuf, int errbufsz); + +/* Parse a string containing the full config. + * Return a table on success, or 0 otherwise. + * Caller must toml_free(the-return-value) after use. + */ +TOML_EXTERN toml_table_t *toml_parse(char *conf, /* NUL terminated, please. */ + char *errbuf, int errbufsz); + +/* Free the table returned by toml_parse() or toml_parse_file(). Once + * this function is called, any handles accessed through this tab + * directly or indirectly are no longer valid. + */ +TOML_EXTERN void toml_free(toml_table_t *tab); + +/* Timestamp types. The year, month, day, hour, minute, second, z + * fields may be NULL if they are not relevant. e.g. In a DATE + * type, the hour, minute, second and z fields will be NULLs. + */ +struct toml_timestamp_t { + struct { /* internal. do not use. */ + int year, month, day; + int hour, minute, second, millisec; + char z[10]; + } __buffer; + int *year, *month, *day; + int *hour, *minute, *second, *millisec; + char *z; +}; + +/*----------------------------------------------------------------- + * Enhanced access methods + */ +struct toml_datum_t { + int ok; + union { + toml_timestamp_t *ts; /* ts must be freed after use */ + char *s; /* string value. s must be freed after use */ + int b; /* bool value */ + int64_t i; /* int value */ + double d; /* double value */ + } u; +}; + +/* on arrays: */ +/* ... retrieve size of array. */ +TOML_EXTERN int toml_array_nelem(const toml_array_t *arr); +/* ... retrieve values using index. */ +TOML_EXTERN toml_datum_t toml_string_at(const toml_array_t *arr, int idx); +TOML_EXTERN toml_datum_t toml_bool_at(const toml_array_t *arr, int idx); +TOML_EXTERN toml_datum_t toml_int_at(const toml_array_t *arr, int idx); +TOML_EXTERN toml_datum_t toml_double_at(const toml_array_t *arr, int idx); +TOML_EXTERN toml_datum_t toml_timestamp_at(const toml_array_t *arr, int idx); +/* ... retrieve array or table using index. */ +TOML_EXTERN toml_array_t *toml_array_at(const toml_array_t *arr, int idx); +TOML_EXTERN toml_table_t *toml_table_at(const toml_array_t *arr, int idx); + +/* on tables: */ +/* ... retrieve the key in table at keyidx. Return 0 if out of range. */ +TOML_EXTERN const char *toml_key_in(const toml_table_t *tab, int keyidx); +/* ... returns 1 if key exists in tab, 0 otherwise */ +TOML_EXTERN int toml_key_exists(const toml_table_t *tab, const char *key); +/* ... retrieve values using key. */ +TOML_EXTERN toml_datum_t toml_string_in(const toml_table_t *arr, + const char *key); +TOML_EXTERN toml_datum_t toml_bool_in(const toml_table_t *arr, const char *key); +TOML_EXTERN toml_datum_t toml_int_in(const toml_table_t *arr, const char *key); +TOML_EXTERN toml_datum_t toml_double_in(const toml_table_t *arr, + const char *key); +TOML_EXTERN toml_datum_t toml_timestamp_in(const toml_table_t *arr, + const char *key); +/* .. retrieve array or table using key. */ +TOML_EXTERN toml_array_t *toml_array_in(const toml_table_t *tab, + const char *key); +TOML_EXTERN toml_table_t *toml_table_in(const toml_table_t *tab, + const char *key); + +/*----------------------------------------------------------------- + * lesser used + */ +/* Return the array kind: 't'able, 'a'rray, 'v'alue, 'm'ixed */ +TOML_EXTERN char toml_array_kind(const toml_array_t *arr); + +/* For array kind 'v'alue, return the type of values + i:int, d:double, b:bool, s:string, t:time, D:date, T:timestamp, 'm'ixed + 0 if unknown +*/ +TOML_EXTERN char toml_array_type(const toml_array_t *arr); + +/* Return the key of an array */ +TOML_EXTERN const char *toml_array_key(const toml_array_t *arr); + +/* Return the number of key-values in a table */ +TOML_EXTERN int toml_table_nkval(const toml_table_t *tab); + +/* Return the number of arrays in a table */ +TOML_EXTERN int toml_table_narr(const toml_table_t *tab); + +/* Return the number of sub-tables in a table */ +TOML_EXTERN int toml_table_ntab(const toml_table_t *tab); + +/* Return the key of a table*/ +TOML_EXTERN const char *toml_table_key(const toml_table_t *tab); + +/*-------------------------------------------------------------- + * misc + */ +TOML_EXTERN int toml_utf8_to_ucs(const char *orig, int len, int64_t *ret); +TOML_EXTERN int toml_ucs_to_utf8(int64_t code, char buf[6]); +TOML_EXTERN void toml_set_memutil(void *(*xxmalloc)(size_t), + void (*xxfree)(void *)); + +/*-------------------------------------------------------------- + * deprecated + */ +/* A raw value, must be processed by toml_rto* before using. */ +typedef const char *toml_raw_t; +TOML_EXTERN toml_raw_t toml_raw_in(const toml_table_t *tab, const char *key); +TOML_EXTERN toml_raw_t toml_raw_at(const toml_array_t *arr, int idx); +TOML_EXTERN int toml_rtos(toml_raw_t s, char **ret); +TOML_EXTERN int toml_rtob(toml_raw_t s, int *ret); +TOML_EXTERN int toml_rtoi(toml_raw_t s, int64_t *ret); +TOML_EXTERN int toml_rtod(toml_raw_t s, double *ret); +TOML_EXTERN int toml_rtod_ex(toml_raw_t s, double *ret, char *buf, int buflen); +TOML_EXTERN int toml_rtots(toml_raw_t s, toml_timestamp_t *ret); + +#endif /* TOML_H */ diff --git a/deps/uthash/utarray.h b/deps/uthash/utarray.h new file mode 100644 index 0000000..dc1cf8e --- /dev/null +++ b/deps/uthash/utarray.h @@ -0,0 +1,248 @@ +/* +Copyright (c) 2008-2022, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* a dynamic array implementation using macros + */ +#ifndef UTARRAY_H +#define UTARRAY_H + +#define UTARRAY_VERSION 2.3.0 + +#include /* size_t */ +#include /* memset, etc */ +#include /* exit */ + +#ifdef __GNUC__ +#define UTARRAY_UNUSED __attribute__((__unused__)) +#else +#define UTARRAY_UNUSED +#endif + +#ifdef oom +#error "The name of macro 'oom' has been changed to 'utarray_oom'. Please update your code." +#define utarray_oom() oom() +#endif + +#ifndef utarray_oom +#define utarray_oom() exit(-1) +#endif + +typedef void (ctor_f)(void *dst, const void *src); +typedef void (dtor_f)(void *elt); +typedef void (init_f)(void *elt); +typedef struct { + size_t sz; + init_f *init; + ctor_f *copy; + dtor_f *dtor; +} UT_icd; + +typedef struct { + unsigned i,n;/* i: index of next available slot, n: num slots */ + UT_icd icd; /* initializer, copy and destructor functions */ + char *d; /* n slots of size icd->sz*/ +} UT_array; + +#define utarray_init(a,_icd) do { \ + memset(a,0,sizeof(UT_array)); \ + (a)->icd = *(_icd); \ +} while(0) + +#define utarray_done(a) do { \ + if ((a)->n) { \ + if ((a)->icd.dtor) { \ + unsigned _ut_i; \ + for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr(a,_ut_i)); \ + } \ + } \ + free((a)->d); \ + } \ + (a)->n=0; \ +} while(0) + +#define utarray_new(a,_icd) do { \ + (a) = (UT_array*)malloc(sizeof(UT_array)); \ + if ((a) == NULL) { \ + utarray_oom(); \ + } \ + utarray_init(a,_icd); \ +} while(0) + +#define utarray_free(a) do { \ + utarray_done(a); \ + free(a); \ +} while(0) + +#define utarray_reserve(a,by) do { \ + if (((a)->i+(by)) > (a)->n) { \ + char *utarray_tmp; \ + while (((a)->i+(by)) > (a)->n) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \ + utarray_tmp=(char*)realloc((a)->d, (a)->n*(a)->icd.sz); \ + if (utarray_tmp == NULL) { \ + utarray_oom(); \ + } \ + (a)->d=utarray_tmp; \ + } \ +} while(0) + +#define utarray_push_back(a,p) do { \ + utarray_reserve(a,1); \ + if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,(a)->i++), p); } \ + else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd.sz); }; \ +} while(0) + +#define utarray_pop_back(a) do { \ + if ((a)->icd.dtor) { (a)->icd.dtor( _utarray_eltptr(a,--((a)->i))); } \ + else { (a)->i--; } \ +} while(0) + +#define utarray_extend_back(a) do { \ + utarray_reserve(a,1); \ + if ((a)->icd.init) { (a)->icd.init(_utarray_eltptr(a,(a)->i)); } \ + else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd.sz); } \ + (a)->i++; \ +} while(0) + +#define utarray_len(a) ((a)->i) + +#define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL) +#define _utarray_eltptr(a,j) ((void*)((a)->d + ((a)->icd.sz * (j)))) + +#define utarray_insert(a,p,j) do { \ + if ((j) > (a)->i) utarray_resize(a,j); \ + utarray_reserve(a,1); \ + if ((j) < (a)->i) { \ + memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j), \ + ((a)->i - (j))*((a)->icd.sz)); \ + } \ + if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,j), p); } \ + else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); }; \ + (a)->i++; \ +} while(0) + +#define utarray_inserta(a,w,j) do { \ + if (utarray_len(w) == 0) break; \ + if ((j) > (a)->i) utarray_resize(a,j); \ + utarray_reserve(a,utarray_len(w)); \ + if ((j) < (a)->i) { \ + memmove(_utarray_eltptr(a,(j)+utarray_len(w)), \ + _utarray_eltptr(a,j), \ + ((a)->i - (j))*((a)->icd.sz)); \ + } \ + if ((a)->icd.copy) { \ + unsigned _ut_i; \ + for(_ut_i=0;_ut_i<(w)->i;_ut_i++) { \ + (a)->icd.copy(_utarray_eltptr(a, (j) + _ut_i), _utarray_eltptr(w, _ut_i)); \ + } \ + } else { \ + memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0), \ + utarray_len(w)*((a)->icd.sz)); \ + } \ + (a)->i += utarray_len(w); \ +} while(0) + +#define utarray_resize(dst,num) do { \ + unsigned _ut_i; \ + if ((dst)->i > (unsigned)(num)) { \ + if ((dst)->icd.dtor) { \ + for (_ut_i = (num); _ut_i < (dst)->i; ++_ut_i) { \ + (dst)->icd.dtor(_utarray_eltptr(dst, _ut_i)); \ + } \ + } \ + } else if ((dst)->i < (unsigned)(num)) { \ + utarray_reserve(dst, (num) - (dst)->i); \ + if ((dst)->icd.init) { \ + for (_ut_i = (dst)->i; _ut_i < (unsigned)(num); ++_ut_i) { \ + (dst)->icd.init(_utarray_eltptr(dst, _ut_i)); \ + } \ + } else { \ + memset(_utarray_eltptr(dst, (dst)->i), 0, (dst)->icd.sz*((num) - (dst)->i)); \ + } \ + } \ + (dst)->i = (num); \ +} while(0) + +#define utarray_concat(dst,src) do { \ + utarray_inserta(dst, src, utarray_len(dst)); \ +} while(0) + +#define utarray_erase(a,pos,len) do { \ + if ((a)->icd.dtor) { \ + unsigned _ut_i; \ + for (_ut_i = 0; _ut_i < (len); _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr(a, (pos) + _ut_i)); \ + } \ + } \ + if ((a)->i > ((pos) + (len))) { \ + memmove(_utarray_eltptr(a, pos), _utarray_eltptr(a, (pos) + (len)), \ + ((a)->i - ((pos) + (len))) * (a)->icd.sz); \ + } \ + (a)->i -= (len); \ +} while(0) + +#define utarray_renew(a,u) do { \ + if (a) utarray_clear(a); \ + else utarray_new(a, u); \ +} while(0) + +#define utarray_clear(a) do { \ + if ((a)->i > 0) { \ + if ((a)->icd.dtor) { \ + unsigned _ut_i; \ + for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ + (a)->icd.dtor(_utarray_eltptr(a, _ut_i)); \ + } \ + } \ + (a)->i = 0; \ + } \ +} while(0) + +#define utarray_sort(a,cmp) do { \ + qsort((a)->d, (a)->i, (a)->icd.sz, cmp); \ +} while(0) + +#define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd.sz,cmp) + +#define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL) +#define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : (((a)->i != utarray_eltidx(a,e)+1) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL)) +#define utarray_prev(a,e) (((e)==NULL) ? utarray_back(a) : ((utarray_eltidx(a,e) != 0) ? _utarray_eltptr(a,utarray_eltidx(a,e)-1) : NULL)) +#define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL) +#define utarray_eltidx(a,e) (((char*)(e) - (a)->d) / (a)->icd.sz) + +/* last we pre-define a few icd for common utarrays of ints and strings */ +static void utarray_str_cpy(void *dst, const void *src) { + char *const *srcc = (char *const *)src; + char **dstc = (char**)dst; + *dstc = (*srcc == NULL) ? NULL : strdup(*srcc); +} +static void utarray_str_dtor(void *elt) { + char **eltc = (char**)elt; + if (*eltc != NULL) free(*eltc); +} +static const UT_icd ut_str_icd UTARRAY_UNUSED = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor}; +static const UT_icd ut_int_icd UTARRAY_UNUSED = {sizeof(int),NULL,NULL,NULL}; +static const UT_icd ut_ptr_icd UTARRAY_UNUSED = {sizeof(void*),NULL,NULL,NULL}; + + +#endif /* UTARRAY_H */ diff --git a/deps/uthash/uthash.h b/deps/uthash/uthash.h new file mode 100644 index 0000000..49c69df --- /dev/null +++ b/deps/uthash/uthash.h @@ -0,0 +1,1138 @@ +/* +Copyright (c) 2003-2022, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#define UTHASH_VERSION 2.3.0 + +#include /* memcmp, memset, strlen */ +#include /* ptrdiff_t */ +#include /* exit */ + +#if defined(HASH_DEFINE_OWN_STDINT) && HASH_DEFINE_OWN_STDINT +/* This codepath is provided for backward compatibility, but I plan to remove it. */ +#warning "HASH_DEFINE_OWN_STDINT is deprecated; please use HASH_NO_STDINT instead" +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +#elif defined(HASH_NO_STDINT) && HASH_NO_STDINT +#else +#include /* uint8_t, uint32_t */ +#endif + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if !defined(DECLTYPE) && !defined(NO_DECLTYPE) +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#endif +#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE(x) +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while (0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while (0) +#endif + +#ifndef uthash_malloc +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free +#define uthash_free(ptr,sz) free(ptr) /* free fcn */ +#endif +#ifndef uthash_bzero +#define uthash_bzero(a,n) memset(a,'\0',n) +#endif +#ifndef uthash_strlen +#define uthash_strlen(s) strlen(s) +#endif + +#ifndef HASH_FUNCTION +#define HASH_FUNCTION(keyptr,keylen,hashv) HASH_JEN(keyptr, keylen, hashv) +#endif + +#ifndef HASH_KEYCMP +#define HASH_KEYCMP(a,b,n) memcmp(a,b,n) +#endif + +#ifndef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif + +#ifndef HASH_NONFATAL_OOM +#define HASH_NONFATAL_OOM 0 +#endif + +#if HASH_NONFATAL_OOM +/* malloc failures can be recovered from */ + +#ifndef uthash_nonfatal_oom +#define uthash_nonfatal_oom(obj) do {} while (0) /* non-fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0) +#define IF_HASH_NONFATAL_OOM(x) x + +#else +/* malloc failures result in lost memory, hash tables are unusable */ + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory") +#define IF_HASH_NONFATAL_OOM(x) + +#endif + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhp */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) +/* calculate the hash handle from element address elp */ +#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle*)(void*)(((char*)(elp)) + ((tbl)->hho))) + +#define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \ +do { \ + struct UT_hash_handle *_hd_hh_item = (itemptrhh); \ + unsigned _hd_bkt; \ + HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + (head)->hh.tbl->buckets[_hd_bkt].count++; \ + _hd_hh_item->hh_next = NULL; \ + _hd_hh_item->hh_prev = NULL; \ +} while (0) + +#define HASH_VALUE(keyptr,keylen,hashv) \ +do { \ + HASH_FUNCTION(keyptr, keylen, hashv); \ +} while (0) + +#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_bkt; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ + } \ + } \ +} while (0) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_hashv; \ + HASH_VALUE(keyptr, keylen, _hf_hashv); \ + HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ + } \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) +#define HASH_BLOOM_MAKE(tbl,oomed) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!(tbl)->bloom_bv) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ + } \ +} while (0) + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ +} while (0) + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) +#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U))) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#else +#define HASH_BLOOM_MAKE(tbl,oomed) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) (1) +#define HASH_BLOOM_BYTELEN 0U +#endif + +#define HASH_MAKE_TABLE(hh,head,oomed) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table)); \ + if (!(head)->hh.tbl) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ + if (!(head)->hh.tbl->buckets) { \ + HASH_RECORD_OOM(oomed); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } else { \ + uthash_bzero((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + uthash_free((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } \ + ) \ + } \ + } \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ +} while (0) + +#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ +} while (0) + +#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ +} while (0) + +#define HASH_APPEND_LIST(hh, head, add) \ +do { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail->next = (add); \ + (head)->hh.tbl->tail = &((add)->hh); \ +} while (0) + +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + do { \ + if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) { \ + break; \ + } \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) + +#ifdef NO_DECLTYPE +#undef HASH_AKBI_INNER_LOOP +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + char *_hs_saved_head = (char*)(head); \ + do { \ + DECLTYPE_ASSIGN(head, _hs_iter); \ + if (cmpfcn(head, add) > 0) { \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + break; \ + } \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) +#endif + +#if HASH_NONFATAL_OOM + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + if (!(oomed)) { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + if (oomed) { \ + HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \ + HASH_DELETE_HH(hh, head, &(add)->hh); \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } else { \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ + } \ + } else { \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } \ +} while (0) + +#else + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ +} while (0) + +#endif + + +#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (char*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + void *_hs_iter = (head); \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \ + if (_hs_iter) { \ + (add)->hh.next = _hs_iter; \ + if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \ + HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \ + } else { \ + (head) = (add); \ + } \ + HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \ + } else { \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \ +} while (0) + +#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ +do { \ + unsigned _hs_hashv; \ + HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) + +#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ + HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) + +#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (const void*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \ +} while (0) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_hashv; \ + HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) + +#define HASH_TO_BKT(hashv,num_bkts,bkt) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1U)); \ +} while (0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ + HASH_DELETE_HH(hh, head, &(delptr)->hh) + +#define HASH_DELETE_HH(hh,head,delptrhh) \ +do { \ + struct UT_hash_handle *_hd_hh_del = (delptrhh); \ + if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } else { \ + unsigned _hd_bkt; \ + if (_hd_hh_del == (head)->hh.tbl->tail) { \ + (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \ + } \ + if (_hd_hh_del->prev != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \ + } else { \ + DECLTYPE_ASSIGN(head, _hd_hh_del->next); \ + } \ + if (_hd_hh_del->next != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \ + } \ + HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh, head, "HASH_DELETE_HH"); \ +} while (0) + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ +do { \ + unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr); \ + HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out); \ +} while (0) +#define HASH_ADD_STR(head,strfield,add) \ +do { \ + unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield); \ + HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add); \ +} while (0) +#define HASH_REPLACE_STR(head,strfield,add,replaced) \ +do { \ + unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield); \ + HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced); \ +} while (0) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_REPLACE_INT(head,intfield,add,replaced) \ + HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ + HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#include /* fprintf, stderr */ +#define HASH_OOPS(...) do { fprintf(stderr, __VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head,where) \ +do { \ + struct UT_hash_handle *_thh; \ + if (head) { \ + unsigned _bkt_i; \ + unsigned _count = 0; \ + char *_prev; \ + for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) { \ + unsigned _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \ + (where), (void*)_thh->hh_prev, (void*)_prev); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \ + (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev != (char*)_thh->prev) { \ + HASH_OOPS("%s: invalid prev %p, actual %p\n", \ + (where), (void*)_thh->prev, (void*)_prev); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid app item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head,where) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ +#define HASH_BER(key,keylen,hashv) \ +do { \ + unsigned _hb_keylen = (unsigned)keylen; \ + const unsigned char *_hb_key = (const unsigned char*)(key); \ + (hashv) = 0; \ + while (_hb_keylen-- != 0U) { \ + (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ + } \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx + * (archive link: https://archive.is/Ivcan ) + */ +#define HASH_SAX(key,keylen,hashv) \ +do { \ + unsigned _sx_i; \ + const unsigned char *_hs_key = (const unsigned char*)(key); \ + hashv = 0; \ + for (_sx_i=0; _sx_i < keylen; _sx_i++) { \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + } \ +} while (0) +/* FNV-1a variation */ +#define HASH_FNV(key,keylen,hashv) \ +do { \ + unsigned _fn_i; \ + const unsigned char *_hf_key = (const unsigned char*)(key); \ + (hashv) = 2166136261U; \ + for (_fn_i=0; _fn_i < keylen; _fn_i++) { \ + hashv = hashv ^ _hf_key[_fn_i]; \ + hashv = hashv * 16777619U; \ + } \ +} while (0) + +#define HASH_OAT(key,keylen,hashv) \ +do { \ + unsigned _ho_i; \ + const unsigned char *_ho_key=(const unsigned char*)(key); \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ +} while (0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,hashv) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + unsigned const char *_hj_key=(unsigned const char*)(key); \ + hashv = 0xfeedbeefu; \ + _hj_i = _hj_j = 0x9e3779b9u; \ + _hj_k = (unsigned)(keylen); \ + while (_hj_k >= 12U) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12U; \ + } \ + hashv += (unsigned)(keylen); \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ + case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ + case 1: _hj_i += _hj_key[0]; /* FALLTHROUGH */ \ + default: ; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ +} while (0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,hashv) \ +do { \ + unsigned const char *_sfh_key=(unsigned const char*)(key); \ + uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ + \ + unsigned _sfh_rem = _sfh_len & 3U; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabeu; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0U; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2U*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + break; \ + default: ; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ +} while (0) + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ +do { \ + if ((head).hh_head != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ + } else { \ + (out) = NULL; \ + } \ + while ((out) != NULL) { \ + if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ + if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) { \ + break; \ + } \ + } \ + if ((out)->hh.hh_next != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ + } else { \ + (out) = NULL; \ + } \ + } \ +} while (0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,hh,addhh,oomed) \ +do { \ + UT_hash_bucket *_ha_head = &(head); \ + _ha_head->count++; \ + (addhh)->hh_next = _ha_head->hh_head; \ + (addhh)->hh_prev = NULL; \ + if (_ha_head->hh_head != NULL) { \ + _ha_head->hh_head->hh_prev = (addhh); \ + } \ + _ha_head->hh_head = (addhh); \ + if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \ + && !(addhh)->tbl->noexpand) { \ + HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + HASH_DEL_IN_BKT(head,addhh); \ + } \ + ) \ + } \ +} while (0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(head,delhh) \ +do { \ + UT_hash_bucket *_hd_head = &(head); \ + _hd_head->count--; \ + if (_hd_head->hh_head == (delhh)) { \ + _hd_head->hh_head = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_prev) { \ + (delhh)->hh_prev->hh_next = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_next) { \ + (delhh)->hh_next->hh_prev = (delhh)->hh_prev; \ + } \ +} while (0) + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(hh,tbl,oomed) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \ + if (!_he_new_buckets) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero(_he_new_buckets, \ + sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \ + (tbl)->ideal_chain_maxlen = \ + ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) + \ + ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ + (tbl)->nonideal_items = 0; \ + for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) { \ + _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh != NULL) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[_he_bkt]); \ + if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) { \ + (tbl)->nonideal_items++; \ + if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \ + _he_newbkt->expand_mult++; \ + } \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head != NULL) { \ + _he_newbkt->hh_head->hh_prev = _he_thh; \ + } \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ + (tbl)->num_buckets *= 2U; \ + (tbl)->log2_num_buckets++; \ + (tbl)->buckets = _he_new_buckets; \ + (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ? \ + ((tbl)->ineff_expands+1U) : 0U; \ + if ((tbl)->ineff_expands > 1U) { \ + (tbl)->noexpand = 1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ + } \ +} while (0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head != NULL) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping != 0U) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p != NULL) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) { \ + _hs_psize++; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + if (_hs_q == NULL) { \ + break; \ + } \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) { \ + if (_hs_psize == 0U) { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else if ((cmpfcn( \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)) \ + )) <= 0) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail != NULL ) { \ + _hs_tail->next = ((_hs_e != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + if (_hs_e != NULL) { \ + _hs_e->prev = ((_hs_tail != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \ + } \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + if (_hs_tail != NULL) { \ + _hs_tail->next = NULL; \ + } \ + if (_hs_nmerges <= 1U) { \ + _hs_looping = 0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2U; \ + } \ + HASH_FSCK(hh, head, "HASH_SRT"); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt = NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if ((src) != NULL) { \ + for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh != NULL; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; ) \ + _dst_hh = (UT_hash_handle*)(void*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh != NULL) { \ + _last_elt_hh->next = _elt; \ + } \ + if ((dst) == NULL) { \ + DECLTYPE_ASSIGN(dst, _elt); \ + HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + uthash_nonfatal_oom(_elt); \ + (dst) = NULL; \ + continue; \ + } \ + ) \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \ + (dst)->hh_dst.tbl->num_items++; \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \ + HASH_DELETE_HH(hh_dst, dst, _dst_hh); \ + _dst_hh->tbl = NULL; \ + uthash_nonfatal_oom(_elt); \ + continue; \ + } \ + ) \ + HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if ((head) != NULL) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } \ +} while (0) + +#define HASH_OVERHEAD(hh,head) \ + (((head) != NULL) ? ( \ + (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ + ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ + sizeof(UT_hash_table) + \ + (HASH_BLOOM_BYTELEN))) : 0U) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1u +#define HASH_BLOOM_SIGNATURE 0xb12220f2u + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + uint8_t bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + const void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ diff --git a/deps/uthash/utlist.h b/deps/uthash/utlist.h new file mode 100644 index 0000000..492908c --- /dev/null +++ b/deps/uthash/utlist.h @@ -0,0 +1,1073 @@ +/* +Copyright (c) 2007-2022, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTLIST_H +#define UTLIST_H + +#define UTLIST_VERSION 2.3.0 + +#include + +/* + * This file contains macros to manipulate singly and doubly-linked lists. + * + * 1. LL_ macros: singly-linked lists. + * 2. DL_ macros: doubly-linked lists. + * 3. CDL_ macros: circular doubly-linked lists. + * + * To use singly-linked lists, your structure must have a "next" pointer. + * To use doubly-linked lists, your structure must "prev" and "next" pointers. + * Either way, the pointer to the head of the list must be initialized to NULL. + * + * ----------------.EXAMPLE ------------------------- + * struct item { + * int id; + * struct item *prev, *next; + * } + * + * struct item *list = NULL: + * + * int main() { + * struct item *item; + * ... allocate and populate item ... + * DL_APPEND(list, item); + * } + * -------------------------------------------------- + * + * For doubly-linked lists, the append and delete macros are O(1) + * For singly-linked lists, append and delete are O(n) but prepend is O(1) + * The sort macro is O(n log(n)) for all types of single/double/circular lists. + */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if !defined(LDECLTYPE) && !defined(NO_DECLTYPE) +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define LDECLTYPE(x) decltype(x) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#endif +#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#else /* GNU, Sun and other compilers */ +#define LDECLTYPE(x) __typeof(x) +#endif +#endif + +/* for VS2008 we use some workarounds to get around the lack of decltype, + * namely, we always reassign our tmp variable to the list head if we need + * to dereference its prev/next pointers, and save/restore the real head.*/ +#ifdef NO_DECLTYPE +#define IF_NO_DECLTYPE(x) x +#define LDECLTYPE(x) char* +#define UTLIST_SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } +#define UTLIST_NEXT(elt,list,next) ((char*)((list)->next)) +#define UTLIST_NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } +/* #define UTLIST_PREV(elt,list,prev) ((char*)((list)->prev)) */ +#define UTLIST_PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } +#define UTLIST_RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } +#define UTLIST_CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } +#else +#define IF_NO_DECLTYPE(x) +#define UTLIST_SV(elt,list) +#define UTLIST_NEXT(elt,list,next) ((elt)->next) +#define UTLIST_NEXTASGN(elt,list,to,next) ((elt)->next)=(to) +/* #define UTLIST_PREV(elt,list,prev) ((elt)->prev) */ +#define UTLIST_PREVASGN(elt,list,to,prev) ((elt)->prev)=(to) +#define UTLIST_RS(list) +#define UTLIST_CASTASGN(a,b) (a)=(b) +#endif + +/****************************************************************************** + * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * + * Unwieldy variable names used here to avoid shadowing passed-in variables. * + *****************************************************************************/ +#define LL_SORT(list, cmp) \ + LL_SORT2(list, cmp, next) + +#define LL_SORT2(list, cmp, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ + } \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + + +#define DL_SORT(list, cmp) \ + DL_SORT2(list, cmp, prev, next) + +#define DL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while ((_ls_psize > 0) || ((_ls_qsize > 0) && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } else if ((_ls_qsize == 0) || (!_ls_q)) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + UTLIST_CASTASGN((list)->prev, _ls_tail); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +#define CDL_SORT(list, cmp) \ + CDL_SORT2(list, cmp, prev, next) + +#define CDL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + UTLIST_CASTASGN(_ls_oldhead,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); \ + if (UTLIST_NEXT(_ls_q,list,next) == _ls_oldhead) { \ + _ls_q = NULL; \ + } else { \ + _ls_q = UTLIST_NEXT(_ls_q,list,next); \ + } \ + UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + UTLIST_CASTASGN((list)->prev,_ls_tail); \ + UTLIST_CASTASGN(_tmp,list); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_tmp,next); UTLIST_RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +/****************************************************************************** + * singly linked list macros (non-circular) * + *****************************************************************************/ +#define LL_PREPEND(head,add) \ + LL_PREPEND2(head,add,next) + +#define LL_PREPEND2(head,add,next) \ +do { \ + (add)->next = (head); \ + (head) = (add); \ +} while (0) + +#define LL_CONCAT(head1,head2) \ + LL_CONCAT2(head1,head2,next) + +#define LL_CONCAT2(head1,head2,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head1) { \ + _tmp = (head1); \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(head2); \ + } else { \ + (head1)=(head2); \ + } \ +} while (0) + +#define LL_APPEND(head,add) \ + LL_APPEND2(head,add,next) + +#define LL_APPEND2(head,add,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + (add)->next=NULL; \ + if (head) { \ + _tmp = (head); \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(add); \ + } else { \ + (head)=(add); \ + } \ +} while (0) + +#define LL_INSERT_INORDER(head,add,cmp) \ + LL_INSERT_INORDER2(head,add,cmp,next) + +#define LL_INSERT_INORDER2(head,add,cmp,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + LL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + LL_APPEND_ELEM2(head, _tmp, add, next); \ + } else { \ + (head) = (add); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define LL_LOWER_BOUND(head,elt,like,cmp) \ + LL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define LL_LOWER_BOUND2(head,elt,like,cmp,next) \ + do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ + if (cmp((elt)->next, like) >= 0) { \ + break; \ + } \ + } \ + } \ + } while (0) + +#define LL_DELETE(head,del) \ + LL_DELETE2(head,del,next) + +#define LL_DELETE2(head,del,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (del))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (del)->next; \ + } \ + } \ +} while (0) + +#define LL_COUNT(head,el,counter) \ + LL_COUNT2(head,el,counter,next) \ + +#define LL_COUNT2(head,el,counter,next) \ +do { \ + (counter) = 0; \ + LL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define LL_FOREACH(head,el) \ + LL_FOREACH2(head,el,next) + +#define LL_FOREACH2(head,el,next) \ + for ((el) = (head); el; (el) = (el)->next) + +#define LL_FOREACH_SAFE(head,el,tmp) \ + LL_FOREACH_SAFE2(head,el,tmp,next) + +#define LL_FOREACH_SAFE2(head,el,tmp,next) \ + for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) + +#define LL_SEARCH_SCALAR(head,out,field,val) \ + LL_SEARCH_SCALAR2(head,out,field,val,next) + +#define LL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while (0) + +#define LL_SEARCH(head,out,elt,cmp) \ + LL_SEARCH2(head,out,elt,cmp,next) + +#define LL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while (0) + +#define LL_REPLACE_ELEM2(head, el, add, next) \ +do { \ + LDECLTYPE(head) _tmp; \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ +} while (0) + +#define LL_REPLACE_ELEM(head, el, add) \ + LL_REPLACE_ELEM2(head, el, add, next) + +#define LL_PREPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + LDECLTYPE(head) _tmp; \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ + } else { \ + LL_APPEND2(head, add, next); \ + } \ +} while (0) \ + +#define LL_PREPEND_ELEM(head, el, add) \ + LL_PREPEND_ELEM2(head, el, add, next) + +#define LL_APPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (el)->next = (add); \ + } else { \ + LL_PREPEND2(head, add, next); \ + } \ +} while (0) \ + +#define LL_APPEND_ELEM(head, el, add) \ + LL_APPEND_ELEM2(head, el, add, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef LL_CONCAT2 +#define LL_CONCAT2(head1,head2,next) \ +do { \ + char *_tmp; \ + if (head1) { \ + _tmp = (char*)(head1); \ + while ((head1)->next) { (head1) = (head1)->next; } \ + (head1)->next = (head2); \ + UTLIST_RS(head1); \ + } else { \ + (head1)=(head2); \ + } \ +} while (0) + +#undef LL_APPEND2 +#define LL_APPEND2(head,add,next) \ +do { \ + if (head) { \ + (add)->next = head; /* use add->next as a temp variable */ \ + while ((add)->next->next) { (add)->next = (add)->next->next; } \ + (add)->next->next=(add); \ + } else { \ + (head)=(add); \ + } \ + (add)->next=NULL; \ +} while (0) + +#undef LL_INSERT_INORDER2 +#define LL_INSERT_INORDER2(head,add,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, add)) >= 0) { \ + (add)->next = (head); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next != NULL && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->next = (head)->next; \ + (head)->next = (add); \ + UTLIST_RS(head); \ + } \ +} while (0) + +#undef LL_DELETE2 +#define LL_DELETE2(head,del,next) \ +do { \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && ((head)->next != (del))) { \ + (head) = (head)->next; \ + } \ + if ((head)->next) { \ + (head)->next = ((del)->next); \ + } \ + UTLIST_RS(head); \ + } \ +} while (0) + +#undef LL_REPLACE_ELEM2 +#define LL_REPLACE_ELEM2(head, el, add, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->next = head; \ + while ((add)->next->next && ((add)->next->next != (el))) { \ + (add)->next = (add)->next->next; \ + } \ + if ((add)->next->next) { \ + (add)->next->next = (add); \ + } \ + } \ + (add)->next = (el)->next; \ +} while (0) + +#undef LL_PREPEND_ELEM2 +#define LL_PREPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->next = (head); \ + while ((add)->next->next && ((add)->next->next != (el))) { \ + (add)->next = (add)->next->next; \ + } \ + if ((add)->next->next) { \ + (add)->next->next = (add); \ + } \ + } \ + (add)->next = (el); \ + } else { \ + LL_APPEND2(head, add, next); \ + } \ +} while (0) \ + +#endif /* NO_DECLTYPE */ + +/****************************************************************************** + * doubly linked list macros (non-circular) * + *****************************************************************************/ +#define DL_PREPEND(head,add) \ + DL_PREPEND2(head,add,prev,next) + +#define DL_PREPEND2(head,add,prev,next) \ +do { \ + (add)->next = (head); \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev = (add); \ + } else { \ + (add)->prev = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define DL_APPEND(head,add) \ + DL_APPEND2(head,add,prev,next) + +#define DL_APPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev->next = (add); \ + (head)->prev = (add); \ + (add)->next = NULL; \ + } else { \ + (head)=(add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_INSERT_INORDER(head,add,cmp) \ + DL_INSERT_INORDER2(head,add,cmp,prev,next) + +#define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + DL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + DL_APPEND_ELEM2(head, _tmp, add, prev, next); \ + } else { \ + (head) = (add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_LOWER_BOUND(head,elt,like,cmp) \ + DL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define DL_LOWER_BOUND2(head,elt,like,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ + if ((cmp((elt)->next, like)) >= 0) { \ + break; \ + } \ + } \ + } \ +} while (0) + +#define DL_CONCAT(head1,head2) \ + DL_CONCAT2(head1,head2,prev,next) + +#define DL_CONCAT2(head1,head2,prev,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head2) { \ + if (head1) { \ + UTLIST_CASTASGN(_tmp, (head2)->prev); \ + (head2)->prev = (head1)->prev; \ + (head1)->prev->next = (head2); \ + UTLIST_CASTASGN((head1)->prev, _tmp); \ + } else { \ + (head1)=(head2); \ + } \ + } \ +} while (0) + +#define DL_DELETE(head,del) \ + DL_DELETE2(head,del,prev,next) + +#define DL_DELETE2(head,del,prev,next) \ +do { \ + assert((head) != NULL); \ + assert((del)->prev != NULL); \ + if ((del)->prev == (del)) { \ + (head)=NULL; \ + } else if ((del)==(head)) { \ + (del)->next->prev = (del)->prev; \ + (head) = (del)->next; \ + } else { \ + (del)->prev->next = (del)->next; \ + if ((del)->next) { \ + (del)->next->prev = (del)->prev; \ + } else { \ + (head)->prev = (del)->prev; \ + } \ + } \ +} while (0) + +#define DL_COUNT(head,el,counter) \ + DL_COUNT2(head,el,counter,next) \ + +#define DL_COUNT2(head,el,counter,next) \ +do { \ + (counter) = 0; \ + DL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define DL_FOREACH(head,el) \ + DL_FOREACH2(head,el,next) + +#define DL_FOREACH2(head,el,next) \ + for ((el) = (head); el; (el) = (el)->next) + +/* this version is safe for deleting the elements during iteration */ +#define DL_FOREACH_SAFE(head,el,tmp) \ + DL_FOREACH_SAFE2(head,el,tmp,next) + +#define DL_FOREACH_SAFE2(head,el,tmp,next) \ + for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) + +/* these are identical to their singly-linked list counterparts */ +#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR +#define DL_SEARCH LL_SEARCH +#define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2 +#define DL_SEARCH2 LL_SEARCH2 + +#define DL_REPLACE_ELEM2(head, el, add, prev, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + (add)->next = (el)->next; \ + if ((el)->next == NULL) { \ + (add)->prev = (add); \ + } else { \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + } \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->prev->next = (add); \ + if ((el)->next == NULL) { \ + (head)->prev = (add); \ + } else { \ + (add)->next->prev = (add); \ + } \ + } \ +} while (0) + +#define DL_REPLACE_ELEM(head, el, add) \ + DL_REPLACE_ELEM2(head, el, add, prev, next) + +#define DL_PREPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->prev->next = (add); \ + } \ + } else { \ + DL_APPEND2(head, add, prev, next); \ + } \ +} while (0) \ + +#define DL_PREPEND_ELEM(head, el, add) \ + DL_PREPEND_ELEM2(head, el, add, prev, next) + +#define DL_APPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (add)->prev = (el); \ + (el)->next = (add); \ + if ((add)->next) { \ + (add)->next->prev = (add); \ + } else { \ + (head)->prev = (add); \ + } \ + } else { \ + DL_PREPEND2(head, add, prev, next); \ + } \ +} while (0) \ + +#define DL_APPEND_ELEM(head, el, add) \ + DL_APPEND_ELEM2(head, el, add, prev, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef DL_INSERT_INORDER2 +#define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + if ((head) == NULL) { \ + (add)->prev = (add); \ + (add)->next = NULL; \ + (head) = (add); \ + } else if ((cmp(head, add)) >= 0) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->prev = (head); \ + (add)->next = (head)->next; \ + (head)->next = (add); \ + UTLIST_RS(head); \ + if ((add)->next) { \ + (add)->next->prev = (add); \ + } else { \ + (head)->prev = (add); \ + } \ + } \ +} while (0) +#endif /* NO_DECLTYPE */ + +/****************************************************************************** + * circular doubly linked list macros * + *****************************************************************************/ +#define CDL_APPEND(head,add) \ + CDL_APPEND2(head,add,prev,next) + +#define CDL_APPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + (head) = (add); \ + } \ +} while (0) + +#define CDL_PREPEND(head,add) \ + CDL_PREPEND2(head,add,prev,next) + +#define CDL_PREPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define CDL_INSERT_INORDER(head,add,cmp) \ + CDL_INSERT_INORDER2(head,add,cmp,prev,next) + +#define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + CDL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + CDL_APPEND_ELEM2(head, _tmp, add, prev, next); \ + } else { \ + (head) = (add); \ + (head)->next = (head); \ + (head)->prev = (head); \ + } \ +} while (0) + +#define CDL_LOWER_BOUND(head,elt,like,cmp) \ + CDL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define CDL_LOWER_BOUND2(head,elt,like,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != (head); (elt) = (elt)->next) { \ + if ((cmp((elt)->next, like)) >= 0) { \ + break; \ + } \ + } \ + } \ +} while (0) + +#define CDL_DELETE(head,del) \ + CDL_DELETE2(head,del,prev,next) + +#define CDL_DELETE2(head,del,prev,next) \ +do { \ + if (((head)==(del)) && ((head)->next == (head))) { \ + (head) = NULL; \ + } else { \ + (del)->next->prev = (del)->prev; \ + (del)->prev->next = (del)->next; \ + if ((del) == (head)) (head)=(del)->next; \ + } \ +} while (0) + +#define CDL_COUNT(head,el,counter) \ + CDL_COUNT2(head,el,counter,next) \ + +#define CDL_COUNT2(head, el, counter,next) \ +do { \ + (counter) = 0; \ + CDL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define CDL_FOREACH(head,el) \ + CDL_FOREACH2(head,el,next) + +#define CDL_FOREACH2(head,el,next) \ + for ((el)=(head);el;(el)=(((el)->next==(head)) ? NULL : (el)->next)) + +#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ + CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) + +#define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \ + for ((el) = (head), (tmp1) = (head) ? (head)->prev : NULL; \ + (el) && ((tmp2) = (el)->next, 1); \ + (el) = ((el) == (tmp1) ? NULL : (tmp2))) + +#define CDL_SEARCH_SCALAR(head,out,field,val) \ + CDL_SEARCH_SCALAR2(head,out,field,val,next) + +#define CDL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while (0) + +#define CDL_SEARCH(head,out,elt,cmp) \ + CDL_SEARCH2(head,out,elt,cmp,next) + +#define CDL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while (0) + +#define CDL_REPLACE_ELEM2(head, el, add, prev, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((el)->next == (el)) { \ + (add)->next = (add); \ + (add)->prev = (add); \ + (head) = (add); \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ + } \ +} while (0) + +#define CDL_REPLACE_ELEM(head, el, add) \ + CDL_REPLACE_ELEM2(head, el, add, prev, next) + +#define CDL_PREPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ + } else { \ + CDL_APPEND2(head, add, prev, next); \ + } \ +} while (0) + +#define CDL_PREPEND_ELEM(head, el, add) \ + CDL_PREPEND_ELEM2(head, el, add, prev, next) + +#define CDL_APPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (add)->prev = (el); \ + (el)->next = (add); \ + (add)->next->prev = (add); \ + } else { \ + CDL_PREPEND2(head, add, prev, next); \ + } \ +} while (0) + +#define CDL_APPEND_ELEM(head, el, add) \ + CDL_APPEND_ELEM2(head, el, add, prev, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef CDL_INSERT_INORDER2 +#define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + if ((head) == NULL) { \ + (add)->prev = (add); \ + (add)->next = (add); \ + (head) = (add); \ + } else if ((cmp(head, add)) >= 0) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (add)->prev->next = (add); \ + (head)->prev = (add); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((char*)(head)->next != _tmp && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->prev = (head); \ + (add)->next = (head)->next; \ + (add)->next->prev = (add); \ + (head)->next = (add); \ + UTLIST_RS(head); \ + } \ +} while (0) +#endif /* NO_DECLTYPE */ + +#endif /* UTLIST_H */ diff --git a/deps/uthash/utringbuffer.h b/deps/uthash/utringbuffer.h new file mode 100644 index 0000000..6034117 --- /dev/null +++ b/deps/uthash/utringbuffer.h @@ -0,0 +1,108 @@ +/* +Copyright (c) 2015-2022, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* a ring-buffer implementation using macros + */ +#ifndef UTRINGBUFFER_H +#define UTRINGBUFFER_H + +#define UTRINGBUFFER_VERSION 2.3.0 + +#include +#include +#include "utarray.h" // for "UT_icd" + +typedef struct { + unsigned i; /* index of next available slot; wraps at n */ + unsigned n; /* capacity */ + unsigned char f; /* full */ + UT_icd icd; /* initializer, copy and destructor functions */ + char *d; /* n slots of size icd->sz */ +} UT_ringbuffer; + +#define utringbuffer_init(a, _n, _icd) do { \ + memset(a, 0, sizeof(UT_ringbuffer)); \ + (a)->icd = *(_icd); \ + (a)->n = (_n); \ + if ((a)->n) { (a)->d = (char*)malloc((a)->n * (_icd)->sz); } \ +} while(0) + +#define utringbuffer_clear(a) do { \ + if ((a)->icd.dtor) { \ + if ((a)->f) { \ + unsigned _ut_i; \ + for (_ut_i = 0; _ut_i < (a)->n; ++_ut_i) { \ + (a)->icd.dtor(utringbuffer_eltptr(a, _ut_i)); \ + } \ + } else { \ + unsigned _ut_i; \ + for (_ut_i = 0; _ut_i < (a)->i; ++_ut_i) { \ + (a)->icd.dtor(utringbuffer_eltptr(a, _ut_i)); \ + } \ + } \ + } \ + (a)->i = 0; \ + (a)->f = 0; \ +} while(0) + +#define utringbuffer_done(a) do { \ + utringbuffer_clear(a); \ + free((a)->d); (a)->d = NULL; \ + (a)->n = 0; \ +} while(0) + +#define utringbuffer_new(a,n,_icd) do { \ + a = (UT_ringbuffer*)malloc(sizeof(UT_ringbuffer)); \ + utringbuffer_init(a, n, _icd); \ +} while(0) + +#define utringbuffer_free(a) do { \ + utringbuffer_done(a); \ + free(a); \ +} while(0) + +#define utringbuffer_push_back(a,p) do { \ + if ((a)->icd.dtor && (a)->f) { (a)->icd.dtor(_utringbuffer_internalptr(a,(a)->i)); } \ + if ((a)->icd.copy) { (a)->icd.copy( _utringbuffer_internalptr(a,(a)->i), p); } \ + else { memcpy(_utringbuffer_internalptr(a,(a)->i), p, (a)->icd.sz); }; \ + if (++(a)->i == (a)->n) { (a)->i = 0; (a)->f = 1; } \ +} while(0) + +#define utringbuffer_len(a) ((a)->f ? (a)->n : (a)->i) +#define utringbuffer_empty(a) ((a)->i == 0 && !(a)->f) +#define utringbuffer_full(a) ((a)->f != 0) + +#define _utringbuffer_real_idx(a,j) ((a)->f ? ((j) + (a)->i) % (a)->n : (j)) +#define _utringbuffer_internalptr(a,j) ((void*)((a)->d + ((a)->icd.sz * (j)))) +#define utringbuffer_eltptr(a,j) ((0 <= (j) && (j) < utringbuffer_len(a)) ? _utringbuffer_internalptr(a,_utringbuffer_real_idx(a,j)) : NULL) + +#define _utringbuffer_fake_idx(a,j) ((a)->f ? ((j) + (a)->n - (a)->i) % (a)->n : (j)) +#define _utringbuffer_internalidx(a,e) (((char*)(e) >= (a)->d) ? (((char*)(e) - (a)->d)/(a)->icd.sz) : -1) +#define utringbuffer_eltidx(a,e) _utringbuffer_fake_idx(a, _utringbuffer_internalidx(a,e)) + +#define utringbuffer_front(a) utringbuffer_eltptr(a,0) +#define utringbuffer_next(a,e) ((e)==NULL ? utringbuffer_front(a) : utringbuffer_eltptr(a, utringbuffer_eltidx(a,e)+1)) +#define utringbuffer_prev(a,e) ((e)==NULL ? utringbuffer_back(a) : utringbuffer_eltptr(a, utringbuffer_eltidx(a,e)-1)) +#define utringbuffer_back(a) (utringbuffer_empty(a) ? NULL : utringbuffer_eltptr(a, utringbuffer_len(a) - 1)) + +#endif /* UTRINGBUFFER_H */ diff --git a/deps/uthash/utstack.h b/deps/uthash/utstack.h new file mode 100644 index 0000000..94b8c51 --- /dev/null +++ b/deps/uthash/utstack.h @@ -0,0 +1,88 @@ +/* +Copyright (c) 2018-2022, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTSTACK_H +#define UTSTACK_H + +#define UTSTACK_VERSION 2.3.0 + +/* + * This file contains macros to manipulate a singly-linked list as a stack. + * + * To use utstack, your structure must have a "next" pointer. + * + * ----------------.EXAMPLE ------------------------- + * struct item { + * int id; + * struct item *next; + * }; + * + * struct item *stack = NULL; + * + * int main() { + * int count; + * struct item *tmp; + * struct item *item = malloc(sizeof *item); + * item->id = 42; + * STACK_COUNT(stack, tmp, count); assert(count == 0); + * STACK_PUSH(stack, item); + * STACK_COUNT(stack, tmp, count); assert(count == 1); + * STACK_POP(stack, item); + * free(item); + * STACK_COUNT(stack, tmp, count); assert(count == 0); + * } + * -------------------------------------------------- + */ + +#define STACK_TOP(head) (head) + +#define STACK_EMPTY(head) (!(head)) + +#define STACK_PUSH(head,add) \ + STACK_PUSH2(head,add,next) + +#define STACK_PUSH2(head,add,next) \ +do { \ + (add)->next = (head); \ + (head) = (add); \ +} while (0) + +#define STACK_POP(head,result) \ + STACK_POP2(head,result,next) + +#define STACK_POP2(head,result,next) \ +do { \ + (result) = (head); \ + (head) = (head)->next; \ +} while (0) + +#define STACK_COUNT(head,el,counter) \ + STACK_COUNT2(head,el,counter,next) \ + +#define STACK_COUNT2(head,el,counter,next) \ +do { \ + (counter) = 0; \ + for ((el) = (head); el; (el) = (el)->next) { ++(counter); } \ +} while (0) + +#endif /* UTSTACK_H */ diff --git a/deps/uthash/utstring.h b/deps/uthash/utstring.h new file mode 100644 index 0000000..f0270fb --- /dev/null +++ b/deps/uthash/utstring.h @@ -0,0 +1,407 @@ +/* +Copyright (c) 2008-2022, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* a dynamic string implementation using macros + */ +#ifndef UTSTRING_H +#define UTSTRING_H + +#define UTSTRING_VERSION 2.3.0 + +#include +#include +#include +#include + +#ifdef __GNUC__ +#define UTSTRING_UNUSED __attribute__((__unused__)) +#else +#define UTSTRING_UNUSED +#endif + +#ifdef oom +#error "The name of macro 'oom' has been changed to 'utstring_oom'. Please update your code." +#define utstring_oom() oom() +#endif + +#ifndef utstring_oom +#define utstring_oom() exit(-1) +#endif + +typedef struct { + char *d; /* pointer to allocated buffer */ + size_t n; /* allocated capacity */ + size_t i; /* index of first unused byte */ +} UT_string; + +#define utstring_reserve(s,amt) \ +do { \ + if (((s)->n - (s)->i) < (size_t)(amt)) { \ + char *utstring_tmp = (char*)realloc( \ + (s)->d, (s)->n + (amt)); \ + if (!utstring_tmp) { \ + utstring_oom(); \ + } \ + (s)->d = utstring_tmp; \ + (s)->n += (amt); \ + } \ +} while(0) + +#define utstring_init(s) \ +do { \ + (s)->n = 0; (s)->i = 0; (s)->d = NULL; \ + utstring_reserve(s,100); \ + (s)->d[0] = '\0'; \ +} while(0) + +#define utstring_done(s) \ +do { \ + if ((s)->d != NULL) free((s)->d); \ + (s)->n = 0; \ +} while(0) + +#define utstring_free(s) \ +do { \ + utstring_done(s); \ + free(s); \ +} while(0) + +#define utstring_new(s) \ +do { \ + (s) = (UT_string*)malloc(sizeof(UT_string)); \ + if (!(s)) { \ + utstring_oom(); \ + } \ + utstring_init(s); \ +} while(0) + +#define utstring_renew(s) \ +do { \ + if (s) { \ + utstring_clear(s); \ + } else { \ + utstring_new(s); \ + } \ +} while(0) + +#define utstring_clear(s) \ +do { \ + (s)->i = 0; \ + (s)->d[0] = '\0'; \ +} while(0) + +#define utstring_bincpy(s,b,l) \ +do { \ + utstring_reserve((s),(l)+1); \ + if (l) memcpy(&(s)->d[(s)->i], b, l); \ + (s)->i += (l); \ + (s)->d[(s)->i]='\0'; \ +} while(0) + +#define utstring_concat(dst,src) \ +do { \ + utstring_reserve((dst),((src)->i)+1); \ + if ((src)->i) memcpy(&(dst)->d[(dst)->i], (src)->d, (src)->i); \ + (dst)->i += (src)->i; \ + (dst)->d[(dst)->i]='\0'; \ +} while(0) + +#define utstring_len(s) ((s)->i) + +#define utstring_body(s) ((s)->d) + +UTSTRING_UNUSED static void utstring_printf_va(UT_string *s, const char *fmt, va_list ap) { + int n; + va_list cp; + for (;;) { +#ifdef _WIN32 + cp = ap; +#else + va_copy(cp, ap); +#endif + n = vsnprintf (&s->d[s->i], s->n-s->i, fmt, cp); + va_end(cp); + + if ((n > -1) && ((size_t) n < (s->n-s->i))) { + s->i += n; + return; + } + + /* Else try again with more space. */ + if (n > -1) utstring_reserve(s,n+1); /* exact */ + else utstring_reserve(s,(s->n)*2); /* 2x */ + } +} +#ifdef __GNUC__ +/* support printf format checking (2=the format string, 3=start of varargs) */ +static void utstring_printf(UT_string *s, const char *fmt, ...) + __attribute__ (( format( printf, 2, 3) )); +#endif +UTSTRING_UNUSED static void utstring_printf(UT_string *s, const char *fmt, ...) { + va_list ap; + va_start(ap,fmt); + utstring_printf_va(s,fmt,ap); + va_end(ap); +} + +/******************************************************************************* + * begin substring search functions * + ******************************************************************************/ +/* Build KMP table from left to right. */ +UTSTRING_UNUSED static void _utstring_BuildTable( + const char *P_Needle, + size_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + + i = 0; + j = i - 1; + P_KMP_Table[i] = j; + while (i < (long) P_NeedleLen) + { + while ( (j > -1) && (P_Needle[i] != P_Needle[j]) ) + { + j = P_KMP_Table[j]; + } + i++; + j++; + if (i < (long) P_NeedleLen) + { + if (P_Needle[i] == P_Needle[j]) + { + P_KMP_Table[i] = P_KMP_Table[j]; + } + else + { + P_KMP_Table[i] = j; + } + } + else + { + P_KMP_Table[i] = j; + } + } + + return; +} + + +/* Build KMP table from right to left. */ +UTSTRING_UNUSED static void _utstring_BuildTableR( + const char *P_Needle, + size_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + + i = P_NeedleLen - 1; + j = i + 1; + P_KMP_Table[i + 1] = j; + while (i >= 0) + { + while ( (j < (long) P_NeedleLen) && (P_Needle[i] != P_Needle[j]) ) + { + j = P_KMP_Table[j + 1]; + } + i--; + j--; + if (i >= 0) + { + if (P_Needle[i] == P_Needle[j]) + { + P_KMP_Table[i + 1] = P_KMP_Table[j + 1]; + } + else + { + P_KMP_Table[i + 1] = j; + } + } + else + { + P_KMP_Table[i + 1] = j; + } + } + + return; +} + + +/* Search data from left to right. ( Multiple search mode. ) */ +UTSTRING_UNUSED static long _utstring_find( + const char *P_Haystack, + size_t P_HaystackLen, + const char *P_Needle, + size_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + long V_FindPosition = -1; + + /* Search from left to right. */ + i = j = 0; + while ( (j < (int)P_HaystackLen) && (((P_HaystackLen - j) + i) >= P_NeedleLen) ) + { + while ( (i > -1) && (P_Needle[i] != P_Haystack[j]) ) + { + i = P_KMP_Table[i]; + } + i++; + j++; + if (i >= (int)P_NeedleLen) + { + /* Found. */ + V_FindPosition = j - i; + break; + } + } + + return V_FindPosition; +} + + +/* Search data from right to left. ( Multiple search mode. ) */ +UTSTRING_UNUSED static long _utstring_findR( + const char *P_Haystack, + size_t P_HaystackLen, + const char *P_Needle, + size_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + long V_FindPosition = -1; + + /* Search from right to left. */ + j = (P_HaystackLen - 1); + i = (P_NeedleLen - 1); + while ( (j >= 0) && (j >= i) ) + { + while ( (i < (int)P_NeedleLen) && (P_Needle[i] != P_Haystack[j]) ) + { + i = P_KMP_Table[i + 1]; + } + i--; + j--; + if (i < 0) + { + /* Found. */ + V_FindPosition = j + 1; + break; + } + } + + return V_FindPosition; +} + + +/* Search data from left to right. ( One time search mode. ) */ +UTSTRING_UNUSED static long utstring_find( + UT_string *s, + long P_StartPosition, /* Start from 0. -1 means last position. */ + const char *P_Needle, + size_t P_NeedleLen) +{ + long V_StartPosition; + long V_HaystackLen; + long *V_KMP_Table; + long V_FindPosition = -1; + + if (P_StartPosition < 0) + { + V_StartPosition = s->i + P_StartPosition; + } + else + { + V_StartPosition = P_StartPosition; + } + V_HaystackLen = s->i - V_StartPosition; + if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) ) + { + V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1)); + if (V_KMP_Table != NULL) + { + _utstring_BuildTable(P_Needle, P_NeedleLen, V_KMP_Table); + + V_FindPosition = _utstring_find(s->d + V_StartPosition, + V_HaystackLen, + P_Needle, + P_NeedleLen, + V_KMP_Table); + if (V_FindPosition >= 0) + { + V_FindPosition += V_StartPosition; + } + + free(V_KMP_Table); + } + } + + return V_FindPosition; +} + + +/* Search data from right to left. ( One time search mode. ) */ +UTSTRING_UNUSED static long utstring_findR( + UT_string *s, + long P_StartPosition, /* Start from 0. -1 means last position. */ + const char *P_Needle, + size_t P_NeedleLen) +{ + long V_StartPosition; + long V_HaystackLen; + long *V_KMP_Table; + long V_FindPosition = -1; + + if (P_StartPosition < 0) + { + V_StartPosition = s->i + P_StartPosition; + } + else + { + V_StartPosition = P_StartPosition; + } + V_HaystackLen = V_StartPosition + 1; + if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) ) + { + V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1)); + if (V_KMP_Table != NULL) + { + _utstring_BuildTableR(P_Needle, P_NeedleLen, V_KMP_Table); + + V_FindPosition = _utstring_findR(s->d, + V_HaystackLen, + P_Needle, + P_NeedleLen, + V_KMP_Table); + + free(V_KMP_Table); + } + } + + return V_FindPosition; +} +/******************************************************************************* + * end substring search functions * + ******************************************************************************/ + +#endif /* UTSTRING_H */ diff --git a/include/http_decoder.h b/include/http_decoder.h new file mode 100644 index 0000000..fe7d1c2 --- /dev/null +++ b/include/http_decoder.h @@ -0,0 +1,118 @@ +/* +********************************************************************************************** +* File: http_decoder.h +* Description: +* Authors: Liu WenTan +* Date: 2024-01-10 +* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved. +*********************************************************************************************** +*/ + +#ifndef _HTTP_DECODER_H_ +#define _HTTP_DECODER_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +enum http_message_type { + HTTP_MESSAGE_REQ_LINE, + HTTP_MESSAGE_REQ_HEADER, + HTTP_MESSAGE_REQ_BODY, + HTTP_MESSAGE_RES_LINE, + HTTP_MESSAGE_RES_HEADER, + HTTP_MESSAGE_RES_BODY, + HTTP_MESSAGE_MAX +}; + +//http string +struct hstring { + char *str; + size_t str_len; +}; + +struct http_header { + struct hstring key; + struct hstring val; +}; + +struct http_request_line { + struct hstring method; + struct hstring uri; + struct hstring version; + + int major_version; + int minor_version; +}; + +struct http_response_line { + struct hstring version; + struct hstring status; + + int major_version; + int minor_version; + int status_code; +}; + +struct http_message; + +enum http_message_type http_message_type(struct http_message *msg); + +/** + * @retval succeed(0) failed(-1) +*/ +int http_message_get_request_line(struct http_message *msg, + struct http_request_line *line); + +int http_message_get_response_line(struct http_message *msg, + struct http_response_line *line); + +/* same key may has multiple kv */ +int http_message_get_request_header(struct http_message *msg, struct hstring *key, + struct http_header *hdr_array, size_t array_size); + +int http_message_get_response_header(struct http_message *msg, struct hstring *key, + struct http_header *hdr_array, size_t array_size); + +/** + * @brief loop reading all headers + * + * @retval succeed(1) failed(<= 0) +*/ +int http_message_request_header_next(struct http_message *msg, + struct http_header *header); + +int http_message_response_header_next(struct http_message *msg, + struct http_header *header); + +/** + * @retval succeed(0) failed(-1) +*/ +int http_message_get_request_raw_body(struct http_message *msg, + struct hstring *body); + +int http_message_get_response_raw_body(struct http_message *msg, + struct hstring *body); + +/** + * @brief If the body hasn't been compressed, return raw body + * + * @retval succeed(0) failed(-1) +*/ +int http_message_get_request_decompress_body(struct http_message *msg, + struct hstring *body); + +int http_message_get_response_decompress_body(struct http_message *msg, + struct hstring *body); + +int http_message_get_url(struct http_message *msg, struct hstring *url); + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/readme.md b/readme.md index ea786ff..9e9113d 100644 --- a/readme.md +++ b/readme.md @@ -1 +1,2 @@ -readme \ No newline at end of file +# Http decoder +### This is a HTTP protocol parsing library based on llhttp. diff --git a/src/.gitkeep b/src/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..2312d62 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,18 @@ +add_definitions(-fPIC) + +include_directories(/opt/MESA/include/) +include_directories(${PROJECT_SOURCE_DIR}/deps/) + +aux_source_directory(${PROJECT_SOURCE_DIR}/deps/mempool DEPS_SRC) +aux_source_directory(${PROJECT_SOURCE_DIR}/deps/toml DEPS_SRC) + +set(HTTP_SRC ${DEPS_SRC} http_decoder.c http_decoder_utils.c http_decoder_half.c + http_decoder_table.c http_decoder_string.c http_content_decompress.c + http_decoder_result_queue.c) + +add_library(http_decoder SHARED ${HTTP_SRC}) +set_target_properties(http_decoder PROPERTIES LINK_FLAGS "-Wl,--version-script=${PROJECT_SOURCE_DIR}/src/version.map") +target_link_libraries(http_decoder z brotlidec llhttp-static fieldstat4) +set_target_properties(http_decoder PROPERTIES PREFIX "") + +install(TARGETS http_decoder LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/stellar_plugin/${lib_name} COMPONENT LIBRARIES) \ No newline at end of file diff --git a/src/http_content_decompress.c b/src/http_content_decompress.c new file mode 100644 index 0000000..5fe08a8 --- /dev/null +++ b/src/http_content_decompress.c @@ -0,0 +1,268 @@ +/* +********************************************************************************************** +* File: http_content_decompress.c +* Description: +* Authors: LuWenPeng +* Date: 2022-10-31 +* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved. +*********************************************************************************************** +*/ + + +#include +#include +#include +#include + +#include "stellar/utils.h" +#include "http_decoder_utils.h" +#include "http_content_decompress.h" + +#define BUFFER_SIZE (16 * 1024) + +struct http_content_decompress { + enum http_content_encoding encoding; + + z_stream *z_stream_ptr; + BrotliDecoderState *br_state; + + char *buffer; + size_t buffer_size; +}; + +enum http_content_encoding +http_content_encoding_str2int(const char *content_encoding) +{ + if (strcasestr(content_encoding, "gzip") != NULL) { + return HTTP_CONTENT_ENCODING_GZIP; + } + + if (strcasestr(content_encoding, "deflate") != NULL) { + return HTTP_CONTENT_ENCODING_DEFLATE; + } + + if (strcasestr(content_encoding, "br") != NULL) { + return HTTP_CONTENT_ENCODING_BR; + } + + return HTTP_CONTENT_ENCODING_NONE; +} + +const char * +http_content_encoding_int2str(enum http_content_encoding content_encoding) +{ + if (content_encoding == HTTP_CONTENT_ENCODING_GZIP) { + return "gzip"; + } + + if (content_encoding == HTTP_CONTENT_ENCODING_DEFLATE) { + return "deflate"; + } + + if (content_encoding == HTTP_CONTENT_ENCODING_BR) { + return "br"; + } + + return "unknown"; +} + +struct http_content_decompress * +http_content_decompress_create(enum http_content_encoding encoding) +{ + struct http_content_decompress *decompress = + CALLOC(struct http_content_decompress, 1); + assert(decompress); + + decompress->encoding = encoding; + decompress->z_stream_ptr = NULL; + decompress->br_state = NULL; + + decompress->buffer = CALLOC(char, BUFFER_SIZE); + assert(decompress->buffer); + decompress->buffer_size = BUFFER_SIZE; + + if (encoding == HTTP_CONTENT_ENCODING_GZIP + || encoding == HTTP_CONTENT_ENCODING_DEFLATE) { + decompress->z_stream_ptr = CALLOC(z_stream, 1); + assert(decompress->z_stream_ptr); + + decompress->z_stream_ptr->zalloc = NULL; + decompress->z_stream_ptr->zfree = NULL; + decompress->z_stream_ptr->opaque = NULL; + decompress->z_stream_ptr->avail_in = 0; + decompress->z_stream_ptr->next_in = Z_NULL; + + if (encoding == HTTP_CONTENT_ENCODING_GZIP) { + if (inflateInit2(decompress->z_stream_ptr, MAX_WBITS + 16) + != Z_OK) { + goto error; + } + } + + if (encoding == HTTP_CONTENT_ENCODING_DEFLATE) { + if (inflateInit2(decompress->z_stream_ptr, -MAX_WBITS) != Z_OK) { + goto error; + } + } + } + + if (encoding == HTTP_CONTENT_ENCODING_BR) { + decompress->br_state = BrotliDecoderCreateInstance(NULL, NULL, NULL); + if (decompress->br_state == NULL) { + goto error; + } + } + + return decompress; + +error: + http_content_decompress_destroy(decompress); + return NULL; +} + +void http_content_decompress_destroy(struct http_content_decompress *decompress) +{ + if (NULL == decompress) { + return; + } + + if (decompress->z_stream_ptr != NULL) { + inflateEnd(decompress->z_stream_ptr); + FREE(decompress->z_stream_ptr); + } + + if (decompress->br_state) { + BrotliDecoderDestroyInstance(decompress->br_state); + decompress->br_state = NULL; + } + + FREE(decompress->buffer); + FREE(decompress); +} + +static int +http_content_decompress_write_zlib(struct http_content_decompress *decompress, + const char *indata, size_t indata_len, + char **outdata, size_t *outdata_len) +{ + z_stream *z_stream_ptr = decompress->z_stream_ptr; + z_stream_ptr->avail_in = (unsigned int)indata_len; + z_stream_ptr->next_in = (unsigned char *)indata; + z_stream_ptr->avail_out = (unsigned int)decompress->buffer_size; + z_stream_ptr->next_out = (unsigned char *)decompress->buffer; + + *outdata = NULL; + *outdata_len = 0; + + int ret = 0; + do { + ret = inflate(z_stream_ptr, Z_NO_FLUSH); + if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT + || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) { + (void)inflateEnd(z_stream_ptr); + return -1; + } + + size_t have = decompress->buffer_size - z_stream_ptr->avail_out; + if (have > 0) { + if (0 == z_stream_ptr->avail_out) { + decompress->buffer_size += have; + decompress->buffer = REALLOC(char, decompress->buffer, + decompress->buffer_size); + *outdata = decompress->buffer; + *outdata_len = *outdata_len + have; + http_decoder_log(DEBUG, "%s realloc outbuffer %zu bytes", + http_content_encoding_int2str(decompress->encoding), + decompress->buffer_size); + z_stream_ptr->avail_out = have; + z_stream_ptr->next_out = (unsigned char *)decompress->buffer + + (decompress->buffer_size - have); + } else { + *outdata = decompress->buffer; + *outdata_len = have; + } + } + } while (z_stream_ptr->avail_in != 0); + + return 0; +} + +static int +http_content_decompress_write_br(struct http_content_decompress *decompress, + const char *indata, size_t indata_len, + char **outdata, size_t *outdata_len) +{ + size_t available_in = indata_len; + const unsigned char *next_in = (const unsigned char *)indata; + size_t available_out = decompress->buffer_size; + unsigned char *next_out = (unsigned char *)decompress->buffer; + + *outdata = NULL; + *outdata_len = 0; + + int ret; + for (;;) { + ret = BrotliDecoderDecompressStream(decompress->br_state, &available_in, + &next_in, &available_out, &next_out, 0); + size_t have = decompress->buffer_size - available_out; + if (have > 0) { + if (0 == available_out) { + decompress->buffer_size += have; + decompress->buffer = REALLOC(char, decompress->buffer, + decompress->buffer_size); + *outdata = decompress->buffer; + *outdata_len = *outdata_len + have; + available_out = have; + next_out = (unsigned char *)decompress->buffer + + (decompress->buffer_size - have); + } else { + *outdata = decompress->buffer; + *outdata_len = have; + } + } + + if (ret == BROTLI_DECODER_RESULT_SUCCESS || + ret == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) { + return 0; + } + + if (ret == BROTLI_DECODER_RESULT_ERROR) { + BrotliDecoderErrorCode errcode = + BrotliDecoderGetErrorCode(decompress->br_state); + http_decoder_log(ERROR, + "BrotliDecoderDecompressStream() failed: errno = %d, %s", + errcode, BrotliDecoderErrorString(errcode)); + return -1; + } + + assert(ret == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT); + } +} + +int http_content_decompress_write(struct http_content_decompress *decompress, + const char *indata, size_t indata_len, + char **outdata, size_t *outdata_len) +{ + assert(decompress); + assert(indata); + assert(indata_len > 0); + assert(outdata); + assert(outdata_len); + + *outdata = NULL; + *outdata_len = 0; + + if (decompress->encoding == HTTP_CONTENT_ENCODING_GZIP || + decompress->encoding == HTTP_CONTENT_ENCODING_DEFLATE) { + return http_content_decompress_write_zlib(decompress, indata, indata_len, + outdata, outdata_len); + } + + if (decompress->encoding == HTTP_CONTENT_ENCODING_BR) { + return http_content_decompress_write_br(decompress, indata, indata_len, + outdata, outdata_len); + } + + assert(0); + return -1; +} \ No newline at end of file diff --git a/src/http_content_decompress.h b/src/http_content_decompress.h new file mode 100644 index 0000000..3c2ba48 --- /dev/null +++ b/src/http_content_decompress.h @@ -0,0 +1,52 @@ +/* +********************************************************************************************** +* File: http_content_decompress.h +* Description: +* Authors: LuWenPeng +* Date: 2022-10-31 +* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved. +*********************************************************************************************** +*/ + + +#ifndef _HTTP_CONTENT_DECOMPRESS_H_ +#define _HTTP_CONTENT_DECOMPRESS_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +enum http_content_encoding { + HTTP_CONTENT_ENCODING_NONE = 0, + HTTP_CONTENT_ENCODING_GZIP = 1 << 1, + HTTP_CONTENT_ENCODING_DEFLATE = 1 << 2, + HTTP_CONTENT_ENCODING_BR = 1 << 3, +}; + +struct http_content_decompress; + +enum http_content_encoding +http_content_encoding_str2int(const char *content_encoding); + +const char * +http_content_encoding_int2str(enum http_content_encoding content_encoding); + +struct http_content_decompress * +http_content_decompress_create(enum http_content_encoding encoding); + +void http_content_decompress_destroy(struct http_content_decompress *decompress); + +// return 0 : success +// return -1 : error +int http_content_decompress_write(struct http_content_decompress *decompress, + const char *indata, size_t indata_len, + char **outdata, size_t *outdata_len); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/http_decoder.c b/src/http_decoder.c new file mode 100644 index 0000000..68f7c58 --- /dev/null +++ b/src/http_decoder.c @@ -0,0 +1,858 @@ +/* +********************************************************************************************** +* File: http_decoder.c +* Description: +* Authors: Liu WenTan +* Date: 2024-01-10 +* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved. +*********************************************************************************************** +*/ + +#include +#include +#include + +#include "toml/toml.h" +#include "stellar/utils.h" +#include "stellar/session.h" +#include "stellar/session_mq.h" +#include "stellar/session_exdata.h" +#include "http_decoder.h" +#include "http_decoder_half.h" +#include "http_decoder_table.h" +#include "http_decoder_result_queue.h" +#include "llhttp.h" +#include "http_decoder_inc.h" +#include "fieldstat/fieldstat_easy.h" + +#define HTTP_IDENTIFY_LEN 16 +#define HD_RESULT_QUEUE_LEN 16 + +#define DEFAULT_STAT_OUTPUT_INTERVAL 1 +#define DEFAULT_STAT_INTERVAL_PKTS 1000 +#define DEFAULT_MEMPOOL_SIZE (32 * 1024) + +const char *g_hd_cfg_path = "./etc/http/http_decoder.toml"; +const char *http_decoder_topic = "HTTP_DECODER_MESSAGE"; +const char *fs_file_name = "http_decoder.fs"; + +struct http_decoder_config { + int decompress_switch; + int stat_interval_pkts; //call fieldstat_incrby every stat_interval_pkts + int stat_output_interval; + size_t result_queue_len; // per session result queue length + size_t mempool_size; // per session mempool size +}; + +/** + * NOTE: http_message don't have the ownership of data + */ +struct http_message { + enum http_message_type type; + struct http_decoder_result_queue *ref_queue; + size_t queue_index; +}; + +struct http_decoder { + struct http_decoder_half *c2s_half; + struct http_decoder_half *s2c_half; +}; + +struct http_decoder_exdata { + struct http_decoder_result_queue *queue; + struct http_decoder *decoder; + nmx_pool_t *mempool; +}; + +struct http_decoder_stat { + long long incoming_bytes; + long long incoming_pkts; + long long incoming_trans; + long long err_pkts; + int counter; +}; + +struct http_decoder_context { + int plugin_id; + int topic_id; + int ex_data_idx; + int fs_incoming_bytes_id; + int fs_incoming_pkts_id; + int fs_incoming_trans_id; + int fs_err_pkts_id; + struct stellar *st; + struct fieldstat_easy *fse; + struct http_decoder_config hd_cfg; +}; + +__thread struct http_decoder_stat _th_stat; + +struct http_message * +http_message_new(enum http_message_type type, + struct http_decoder_result_queue *queue, + int queue_index) +{ + struct http_message *msg = CALLOC(struct http_message, 1); + + msg->type = type; + msg->ref_queue = queue; + msg->queue_index = queue_index; + + return msg; +} + +static void http_message_free(void *http_msg, void *cb_arg) +{ + if (NULL == http_msg) { + return; + } + + FREE(http_msg); +} + +static void http_event_handler(enum http_event event, + struct http_decoder_half_data **data, + struct http_event_context *ev_ctx) +{ + assert(ev_ctx); + + size_t queue_idx = 0; + nmx_pool_t *mempool = ev_ctx->ref_mempool; + struct http_decoder_result_queue *queue = ev_ctx->ref_queue; + struct http_message *msg = NULL; + struct http_decoder_half_data *half_data = NULL; + int ret = 0; + + switch (event) { + case HTTP_EVENT_REQ_INIT: + half_data = http_decoder_result_queue_peek_req(queue); + if (half_data != NULL) { + http_decoder_result_queue_inc_req_index(queue); + } + + half_data = http_decoder_result_queue_peek_req(queue); + if (half_data != NULL) { + half_data = http_decoder_result_queue_pop_req(queue); + http_decoder_half_data_free(mempool, half_data); + half_data = NULL; + } + + half_data = http_decoder_half_data_new(mempool); + ret = http_decoder_result_queue_push_req(queue, half_data); + if (ret < 0) { + fprintf(stderr, "http_decoder_result_queue_push req failed."); + http_decoder_half_data_free(mempool, half_data); + half_data = NULL; + } + *data = half_data; + break; + case HTTP_EVENT_REQ_LINE: + queue_idx = http_decoder_result_queue_req_index(queue); + msg = http_message_new(HTTP_MESSAGE_REQ_LINE, queue, queue_idx); + session_mq_publish_message(ev_ctx->ref_session, ev_ctx->topic_id, msg); + break; + case HTTP_EVENT_REQ_HDR_END: + { + int build_url_final = http_decoder_join_url_finally(ev_ctx, http_decoder_result_queue_peek_req(queue),mempool); + ret = http_decoder_half_data_has_parsed_header(*data); + if (0 == ret && 0 == build_url_final) { + break; + } + queue_idx = http_decoder_result_queue_req_index(queue); + msg = http_message_new(HTTP_MESSAGE_REQ_HEADER, queue, queue_idx); + session_mq_publish_message(ev_ctx->ref_session, ev_ctx->topic_id, msg); + } + break; + case HTTP_EVENT_REQ_BODY_BEGIN: + break; + case HTTP_EVENT_REQ_BODY_DATA: + queue_idx = http_decoder_result_queue_req_index(queue); + msg = http_message_new(HTTP_MESSAGE_REQ_BODY, queue, queue_idx); + session_mq_publish_message(ev_ctx->ref_session, ev_ctx->topic_id, msg); + break; + case HTTP_EVENT_REQ_BODY_END: + break; + case HTTP_EVENT_REQ_END: + http_decoder_result_queue_inc_req_index(queue); + half_data = http_decoder_result_queue_pop_req(queue); + if (half_data != NULL) { + http_decoder_half_data_free(mempool, half_data); + half_data = NULL; + } + break; + case HTTP_EVENT_RES_INIT: + half_data = http_decoder_result_queue_peek_res(queue); + if (half_data != NULL) { + http_decoder_result_queue_inc_res_index(queue); + } + + half_data = http_decoder_result_queue_peek_res(queue); + if (half_data != NULL) { + half_data = http_decoder_result_queue_pop_res(queue); + http_decoder_half_data_free(mempool, half_data); + half_data = NULL; + } + + half_data = http_decoder_half_data_new(mempool); + ret = http_decoder_result_queue_push_res(queue, half_data); + if (ret < 0) { + fprintf(stderr, "http_decoder_result_queue_push res failed."); + http_decoder_half_data_free(mempool, half_data); + half_data = NULL; + } + *data = half_data; + break; + case HTTP_EVENT_RES_LINE: + queue_idx = http_decoder_result_queue_res_index(queue); + msg = http_message_new(HTTP_MESSAGE_RES_LINE, queue, queue_idx); + session_mq_publish_message(ev_ctx->ref_session, ev_ctx->topic_id, msg); + break; + case HTTP_EVENT_RES_HDR: + break; + case HTTP_EVENT_RES_HDR_END: + ret = http_decoder_half_data_has_parsed_header(*data); + if (0 == ret) { + break; + } + + queue_idx = http_decoder_result_queue_res_index(queue); + msg = http_message_new(HTTP_MESSAGE_RES_HEADER, queue, queue_idx); + session_mq_publish_message(ev_ctx->ref_session, ev_ctx->topic_id, msg); + break; + case HTTP_EVENT_RES_BODY_BEGIN: + break; + case HTTP_EVENT_RES_BODY_DATA: + queue_idx = http_decoder_result_queue_res_index(queue); + msg = http_message_new(HTTP_MESSAGE_RES_BODY, queue, queue_idx); + session_mq_publish_message(ev_ctx->ref_session, ev_ctx->topic_id, msg); + break; + case HTTP_EVENT_RES_BODY_END: + break; + case HTTP_EVENT_RES_END: + http_decoder_result_queue_inc_res_index(queue); + half_data = http_decoder_result_queue_pop_res(queue); + if (half_data != NULL) { + http_decoder_half_data_free(mempool, half_data); + half_data = NULL; + } + break; + default: + assert(0); + break; + } +} + +static struct http_decoder * +http_decoder_new(nmx_pool_t *mempool, http_event_cb *ev_cb, + int decompress_switch) +{ + struct http_decoder *decoder = MEMPOOL_CALLOC(mempool, struct http_decoder, 1); + assert(decoder); + + decoder->c2s_half = http_decoder_half_new(mempool, ev_cb, HTTP_REQUEST, + decompress_switch); + decoder->s2c_half = http_decoder_half_new(mempool, ev_cb, HTTP_RESPONSE, + decompress_switch); + + return decoder; +} + +static void +http_decoder_free(nmx_pool_t *mempool, struct http_decoder *decoder) +{ + if (NULL == decoder) { + return; + } + + if (decoder->c2s_half != NULL) { + http_decoder_half_free(mempool, decoder->c2s_half); + decoder->c2s_half = NULL; + } + + if (decoder->s2c_half != NULL) { + http_decoder_half_free(mempool, decoder->s2c_half); + decoder->s2c_half = NULL; + } + + MEMPOOL_FREE(mempool, decoder); +} + +static struct http_decoder_exdata * +http_decoder_exdata_new(size_t mempool_size, size_t queue_size, + int decompress_switch) +{ + struct http_decoder_exdata *ex_data = CALLOC(struct http_decoder_exdata, 1); + + ex_data->mempool = nmx_create_pool(mempool_size); + ex_data->decoder = http_decoder_new(ex_data->mempool, http_event_handler, + decompress_switch); + ex_data->queue = http_decoder_result_queue_new(ex_data->mempool, queue_size); + + return ex_data; +} + +static void http_decoder_exdata_free(struct http_decoder_exdata *ex_data) +{ + if (NULL == ex_data) { + return; + } + + if (ex_data->decoder != NULL) { + http_decoder_free(ex_data->mempool, ex_data->decoder); + ex_data->decoder = NULL; + } + + if (ex_data->queue != NULL) { + http_decoder_result_queue_free(ex_data->mempool, ex_data->queue); + ex_data->queue = NULL; + } + + nmx_destroy_pool(ex_data->mempool); + + FREE(ex_data); +} + +static int http_protocol_identify(const char *data, size_t data_len) +{ + llhttp_t parser; + llhttp_settings_t settings; + enum llhttp_errno error; + + llhttp_settings_init(&settings); + llhttp_init(&parser, HTTP_BOTH, &settings); + + error = llhttp_execute(&parser, data, data_len); + if (error != HPE_OK) { + return -1; + } + + return 0; +} + +static int +http_decoder_stat_init(struct http_decoder_context *ctx, int thread_num) +{ + ctx->fse = + fieldstat_easy_new(thread_num, "http_decoder_statistics", NULL, 0); + if (NULL == ctx->fse) { + fprintf(stderr, "fieldstat_easy_new failed."); + return -1; + } + + ctx->fs_incoming_bytes_id = + fieldstat_easy_register_counter(ctx->fse, "incoming_bytes"); + if (ctx->fs_incoming_bytes_id < 0) { + fprintf(stderr, "fieldstat_easy_register_counter incoming_bytes failed."); + return -1; + } + + ctx->fs_incoming_trans_id = + fieldstat_easy_register_counter(ctx->fse, "incoming_trans"); + if (ctx->fs_incoming_trans_id < 0) { + fprintf(stderr, "fieldstat_easy_register_counter incoming_trans failed."); + return -1; + } + + ctx->fs_incoming_pkts_id = + fieldstat_easy_register_counter(ctx->fse, "incoming_pkts"); + if (ctx->fs_incoming_pkts_id < 0) { + fprintf(stderr, "fieldstat_easy_register_counter incoming_pkts failed."); + return -1; + } + + ctx->fs_err_pkts_id = fieldstat_easy_register_counter(ctx->fse, "err_pkts"); + if (ctx->fs_err_pkts_id < 0) { + fprintf(stderr, "fieldstat_easy_register_counter err_pkts failed."); + return -1; + } + + int stat_output_interval = DEFAULT_STAT_OUTPUT_INTERVAL; + if (ctx->hd_cfg.stat_output_interval > 0) { + stat_output_interval = ctx->hd_cfg.stat_output_interval; + } + + int ret = fieldstat_easy_enable_auto_output(ctx->fse, fs_file_name, + stat_output_interval); + if (ret < 0) { + fprintf(stderr, "fieldstat_easy_enable_auto_output failed."); + return -1; + } + + sleep(1); + + return 0; +} + +static void +http_decoder_stat_output(struct http_decoder_context *ctx, int thread_id) +{ + if (NULL == ctx || thread_id < 0) { + return; + } + + int stat_interval_pkts = DEFAULT_STAT_INTERVAL_PKTS; + if (ctx->hd_cfg.stat_interval_pkts > 0) { + stat_interval_pkts = ctx->hd_cfg.stat_interval_pkts; + } + + if (_th_stat.counter >= stat_interval_pkts) { + fieldstat_easy_counter_incrby(ctx->fse, thread_id, + ctx->fs_incoming_bytes_id, NULL, 0, + _th_stat.incoming_bytes); + + fieldstat_easy_counter_incrby(ctx->fse, thread_id, + ctx->fs_incoming_pkts_id, NULL, 0, + _th_stat.incoming_pkts); + + fieldstat_easy_counter_incrby(ctx->fse, thread_id, + ctx->fs_incoming_trans_id, NULL, 0, + _th_stat.incoming_trans); + + fieldstat_easy_counter_incrby(ctx->fse, thread_id, + ctx->fs_err_pkts_id, NULL, 0, + _th_stat.err_pkts); + + _th_stat.counter = 0; + _th_stat.err_pkts = 0; + _th_stat.incoming_bytes = 0; + _th_stat.incoming_pkts = 0; + _th_stat.incoming_trans = 0; + } +} + +int http_decoder_entry(struct session *sess, int events, + const struct packet *pkt, void *cb_arg) +{ + struct http_decoder_context *ctx = (struct http_decoder_context *)cb_arg; + size_t payload_len = 0; + uint64_t inner_flag = 0; + + int ret = session_is_inner_most(sess, &inner_flag); + if (0 == ret) { + return 0; + } + + struct http_decoder_exdata *ex_data = + session_get_ex_data(sess, ctx->ex_data_idx); + + if (events & SESS_EV_CLOSING) { + if (ex_data != NULL) { + http_decoder_exdata_free(ex_data); + session_set_ex_data(sess, ctx->ex_data_idx, NULL); + } + + return 0; + } + + const char *payload = session_get0_current_payload(sess, &payload_len); + + if (events & SESS_EV_OPENING) { + assert(ex_data == NULL); + + //If not http, ignore this session + if (payload_len > 0) { + size_t http_identify_len = payload_len > HTTP_IDENTIFY_LEN + ? HTTP_IDENTIFY_LEN + : payload_len; + + ret = http_protocol_identify(payload, http_identify_len); + if (ret < 0) { + // ignore this session's event + struct session_event *s_event = + session_get_intrinsic_event(sess, ctx->plugin_id); + + session_event_assign(s_event, ctx->st, sess, 0, + http_decoder_entry, ctx); + return 0; + } + } + + ex_data = http_decoder_exdata_new(ctx->hd_cfg.mempool_size, + ctx->hd_cfg.result_queue_len, + ctx->hd_cfg.decompress_switch); + session_set_ex_data(sess, ctx->ex_data_idx, ex_data); + } + + if (0 == payload_len || NULL == ex_data) { + return 0; + } + + int dir = packet_get_direction(pkt); + if (dir < 0) { + return -1; + } + + int thread_id = session_get_current_thread_id(sess); + struct http_decoder_half *cur_half = NULL; + + if (dir == PACKET_DIRECTION_C2S) { + cur_half = ex_data->decoder->c2s_half; + } else { + cur_half = ex_data->decoder->s2c_half; + } + + http_decoder_half_reinit(cur_half, ctx->topic_id, ex_data->queue, + ex_data->mempool, sess); + ret = http_decoder_half_parse(cur_half, payload, payload_len); + if (ret < 0) { + _th_stat.err_pkts += 1; + } + + _th_stat.incoming_bytes += payload_len; + _th_stat.incoming_pkts += 1; + _th_stat.incoming_trans += http_decoder_half_trans_count(cur_half); + _th_stat.counter++; + + http_decoder_stat_output(ctx, thread_id); + + return 0; +} + +static void _http_decoder_context_free(struct http_decoder_context *ctx) +{ + if (NULL == ctx) { + return; + } + + if (ctx->fse != NULL) { + fieldstat_easy_free(ctx->fse); + ctx->fse = NULL; + } + + if (ctx->topic_id >= 0) { + session_mq_destroy_topic(ctx->st, ctx->topic_id); + ctx->topic_id = -1; + } + + FREE(ctx); +} + +static void http_decoder_ex_data_free(struct session *s, int idx, + void *ex_data, void *arg) +{ + if (NULL == ex_data) { + return; + } + + struct http_decoder_exdata *exdata = (struct http_decoder_exdata *)ex_data; + http_decoder_exdata_free(exdata); +} + +static int load_http_decoder_config(const char *cfg_path, + struct http_decoder_config *hd_cfg) +{ + FILE *fp = fopen(cfg_path, "r"); + if (NULL == fp) { + fprintf(stderr, "[%s:%d]Can't open config file:%s", + __FUNCTION__, __LINE__, cfg_path); + return -1; + } + + int ret = 0; + char errbuf[256] = {0}; + + toml_table_t *root = toml_parse_file(fp, errbuf, sizeof(errbuf)); + fclose(fp); + + toml_table_t *basic_sec_tbl = toml_table_in(root, "basic"); + if (NULL == basic_sec_tbl) { + fprintf(stderr, "[%s:%d]config file:%s has no key: [basic]", + __FUNCTION__, __LINE__, cfg_path); + ret = -1; + goto next; + } + + toml_datum_t int_val = toml_int_in(basic_sec_tbl, "decompress"); + if (int_val.ok != 0) { + hd_cfg->decompress_switch = int_val.u.b; + } + + int_val = toml_int_in(basic_sec_tbl, "mempool_size"); + if (int_val.ok != 0) { + hd_cfg->mempool_size = int_val.u.i; + } else { + hd_cfg->mempool_size = DEFAULT_MEMPOOL_SIZE; + } + + int_val = toml_int_in(basic_sec_tbl, "result_queue_len"); + if (int_val.ok != 0) { + hd_cfg->result_queue_len = int_val.u.i; + } else { + hd_cfg->result_queue_len = HD_RESULT_QUEUE_LEN; + } + + int_val = toml_int_in(basic_sec_tbl, "stat_interval_pkts"); + if (int_val.ok != 0) { + hd_cfg->stat_interval_pkts = int_val.u.i; + } else { + hd_cfg->stat_interval_pkts = DEFAULT_STAT_INTERVAL_PKTS; + } + + int_val = toml_int_in(basic_sec_tbl, "stat_output_interval"); + if (int_val.ok != 0) { + hd_cfg->stat_output_interval = int_val.u.i; + } else { + hd_cfg->stat_output_interval = DEFAULT_STAT_OUTPUT_INTERVAL; + } + +next: + toml_free(root); + return ret; +} + +void *http_decoder_init(struct stellar *st) +{ + int plugin_id = -1; + int topic_id = -1; + int thread_num = 0; + + struct http_decoder_context *ctx = CALLOC(struct http_decoder_context, 1); + + int ret = load_http_decoder_config(g_hd_cfg_path, &ctx->hd_cfg); + if (ret < 0) { + goto failed; + } + + ctx->st = st; + ctx->ex_data_idx = stellar_session_get_ex_new_index(st, "HTTP_DECODER", + http_decoder_ex_data_free, + NULL); + + plugin_id = stellar_plugin_register(st, SESS_EV_TCP|SESS_EV_CLOSING, + http_decoder_entry, ctx); + if (plugin_id < 0) { + goto failed; + } + + ctx->plugin_id = plugin_id; + + topic_id = session_mq_get_topic_id(st, http_decoder_topic); + if (topic_id < 0) { + topic_id = session_mq_create_topic(st, http_decoder_topic, + http_message_free, NULL); + } + + ctx->topic_id = topic_id; + + thread_num = stellar_get_worker_thread_num(st); + + if (http_decoder_stat_init(ctx, thread_num) < 0) { + goto failed; + } + + printf("http_decoder_init: ex_data_idx:%d, plugin_id:%d, topic_id:%d\n", + ctx->ex_data_idx, ctx->plugin_id, ctx->topic_id); + + return ctx; + +failed: + _http_decoder_context_free(ctx); + return NULL; +} + +void http_decoder_exit(void *decoder_ctx) +{ + if (NULL == decoder_ctx) { + return; + } + + struct http_decoder_context *ctx = + (struct http_decoder_context *)decoder_ctx; + + _http_decoder_context_free(ctx); +} + +enum http_message_type http_message_type(struct http_message *msg) +{ + if (NULL == msg) { + return HTTP_MESSAGE_MAX; + } + + return msg->type; +} + +int http_message_get_request_line(struct http_message *msg, + struct http_request_line *line) +{ + if (NULL == msg || msg->type != HTTP_MESSAGE_REQ_LINE || + NULL == line) { + return -1; + } + + assert(msg->ref_queue); + assert(msg->queue_index < HD_RESULT_QUEUE_LEN); + + struct http_decoder_half_data *req_data = + msg->ref_queue->array[msg->queue_index].req_data; + + return http_decoder_half_data_get_request_line(req_data, line); +} + +int http_message_get_response_line(struct http_message *msg, + struct http_response_line *line) +{ + if (NULL == msg || msg->type != HTTP_MESSAGE_RES_LINE || + NULL == line) { + return -1; + } + + assert(msg->ref_queue); + assert(msg->queue_index < HD_RESULT_QUEUE_LEN); + + struct http_decoder_half_data *res_data = + msg->ref_queue->array[msg->queue_index].res_data; + + return http_decoder_half_data_get_response_line(res_data, line); +} + +int http_message_get_request_header(struct http_message *msg, struct hstring *key, + struct http_header *hdr_array, size_t array_size) +{ + if (NULL == msg || msg->type != HTTP_MESSAGE_REQ_HEADER || + NULL == key || NULL == hdr_array || 0 == array_size) { + return -1; + } + + assert(msg->ref_queue); + assert(msg->queue_index < HD_RESULT_QUEUE_LEN); + + struct http_decoder_half_data *req_data = + msg->ref_queue->array[msg->queue_index].req_data; + + return http_decoder_half_data_get_header(req_data, key, hdr_array, array_size); +} + +int http_message_get_response_header(struct http_message *msg, struct hstring *key, + struct http_header *hdr_array, size_t array_size) +{ + if (NULL == msg || msg->type != HTTP_MESSAGE_RES_HEADER || NULL == key || + NULL == hdr_array || 0 == array_size) { + return -1; + } + + assert(msg->ref_queue); + assert(msg->queue_index < HD_RESULT_QUEUE_LEN); + + struct http_decoder_half_data *res_data = + msg->ref_queue->array[msg->queue_index].res_data; + + return http_decoder_half_data_get_header(res_data, key, hdr_array, array_size); +} + +int http_message_request_header_next(struct http_message *msg, + struct http_header *hdr) +{ + if (NULL == msg || msg->type != HTTP_MESSAGE_REQ_HEADER + || NULL == hdr) { + return -1; + } + + assert(msg->ref_queue); + assert(msg->queue_index < HD_RESULT_QUEUE_LEN); + + struct http_decoder_half_data *req_data = + msg->ref_queue->array[msg->queue_index].req_data; + + return http_decoder_half_data_iter_header(req_data, hdr); +} + +int http_message_response_header_next(struct http_message *msg, + struct http_header *hdr) +{ + if (NULL == msg || msg->type != HTTP_MESSAGE_RES_HEADER || + NULL == hdr) { + return -1; + } + + assert(msg->ref_queue); + assert(msg->queue_index < HD_RESULT_QUEUE_LEN); + + struct http_decoder_half_data *res_data = + msg->ref_queue->array[msg->queue_index].res_data; + + return http_decoder_half_data_iter_header(res_data, hdr); +} + +int http_message_get_request_raw_body(struct http_message *msg, + struct hstring *body) +{ + if (NULL == msg || (msg->type != HTTP_MESSAGE_REQ_BODY) || + NULL == body) { + return -1; + } + + assert(msg->ref_queue); + assert(msg->queue_index < HD_RESULT_QUEUE_LEN); + + struct http_decoder_half_data *req_data = + msg->ref_queue->array[msg->queue_index].req_data; + + return http_decoder_half_data_get_raw_body(req_data, body); +} + +int http_message_get_response_raw_body(struct http_message *msg, + struct hstring *body) +{ + if (NULL == msg || (msg->type != HTTP_MESSAGE_RES_BODY) || + NULL == body) { + return -1; + } + + assert(msg->ref_queue); + assert(msg->queue_index < HD_RESULT_QUEUE_LEN); + + struct http_decoder_half_data *res_data = + msg->ref_queue->array[msg->queue_index].res_data; + + return http_decoder_half_data_get_raw_body(res_data, body); +} + +int http_message_get_request_decompress_body(struct http_message *msg, + struct hstring *body) +{ + if (NULL == msg || (msg->type != HTTP_MESSAGE_REQ_BODY) || + NULL == body) { + return -1; + } + + assert(msg->ref_queue); + assert(msg->queue_index < HD_RESULT_QUEUE_LEN); + + struct http_decoder_half_data *req_data = + msg->ref_queue->array[msg->queue_index].req_data; + + return http_decoder_half_data_get_decompress_body(req_data, body); +} + +int http_message_get_response_decompress_body(struct http_message *msg, + struct hstring *body) +{ + if (NULL == msg || (msg->type != HTTP_MESSAGE_RES_BODY) || + NULL == body) { + return -1; + } + + assert(msg->ref_queue); + assert(msg->queue_index < HD_RESULT_QUEUE_LEN); + + struct http_decoder_half_data *res_data = + msg->ref_queue->array[msg->queue_index].res_data; + + return http_decoder_half_data_get_decompress_body(res_data, body); +} + +int http_message_get_url(struct http_message *msg, struct hstring *url) +{ + if (NULL == msg || NULL == url) + { + return -1; + } + + assert(msg->ref_queue); + assert(msg->queue_index < HD_RESULT_QUEUE_LEN); + + struct http_decoder_half_data *req_data = + msg->ref_queue->array[msg->queue_index].req_data; + + return http_half_data_get_url(req_data, url); +} \ No newline at end of file diff --git a/src/http_decoder_half.c b/src/http_decoder_half.c new file mode 100644 index 0000000..56a7838 --- /dev/null +++ b/src/http_decoder_half.c @@ -0,0 +1,1034 @@ +/* +********************************************************************************************** +* File: http_decoder_half.c +* Description: +* Authors: Liu WenTan +* Date: 2024-01-10 +* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved. +*********************************************************************************************** +*/ + +#include +#include +#include +#include + +#include "stellar/utils.h" +#include "stellar/session_mq.h" +#include "llhttp.h" +#include "http_decoder.h" +#include "http_decoder_inc.h" +#include "http_decoder_utils.h" +#include "http_decoder_half.h" +#include "http_decoder_table.h" +#include "http_content_decompress.h" + +struct http_decoder_half_data { + struct http_decoder_table *table; + + int major_version; + int minor_version; + int status_code; + + enum http_content_encoding content_encoding; + struct http_content_decompress *decompress; + char *ref_decompress_body; + size_t decompress_body_len; + + int joint_url_complete; + struct hstring joint_url; // http://[:]/? +}; + +struct http_decoder_half { + llhttp_t parser; + llhttp_settings_t settings; + enum llhttp_errno error; + + int decompress_switch; + + enum http_event event; + http_event_cb *http_ev_cb; + struct http_event_context *http_ev_ctx; + + struct http_decoder_half_data *ref_data; + + long long trans_counter; + long long err_counter; +}; + +// #define HTTP_DECODER_DEBUG +#ifdef HTTP_DECODER_DEBUG +static void printf_debug_info(const char *desc, const char *at, size_t length) +{ + if (at) + { + char *temp = safe_dup(at, length); + printf("HTTP PARSER STAGE: %s: %s\n", desc, temp); + FREE(temp); + } + else + { + printf("HTTP PARSER STAGE: %s\n", desc); + } +} +#else +#define printf_debug_info(desc, at, length) +#endif + +static void +http_decoder_half_data_decompress(struct http_decoder_half_data *data) +{ + assert(data); + + if (data->content_encoding == HTTP_CONTENT_ENCODING_NONE) { + return; + } + + struct hstring raw_body = {0}; + http_decoder_table_get_body(data->table, &raw_body); + if (raw_body.str == NULL || raw_body.str_len == 0) { + return; + } + + if (NULL == data->decompress) { + data->decompress = http_content_decompress_create(data->content_encoding); + } + + assert(data->decompress); + if (http_content_decompress_write(data->decompress, raw_body.str, + raw_body.str_len, + &data->ref_decompress_body, + &data->decompress_body_len) == -1) { + // log error + http_content_decompress_destroy(data->decompress); + data->decompress = NULL; + } +} + +/* Possible return values 0, -1, `HPE_PAUSED` */ +static int on_message_begin(llhttp_t *http) +{ + printf_debug_info("on_message_begin", NULL, 0); + + struct http_decoder_half *half = + container_of(http, struct http_decoder_half, parser); + assert(half); + + if (half->parser.type == HTTP_REQUEST) { + half->event = HTTP_EVENT_REQ_INIT; + } else { + half->event = HTTP_EVENT_RES_INIT; + } + + half->ref_data = NULL; + + assert(half->http_ev_cb != NULL); + half->http_ev_cb(half->event, &half->ref_data, half->http_ev_ctx); + + half->trans_counter++; + + return 0; +} + +static int on_message_complete(llhttp_t *http) +{ + printf_debug_info("on_message_complete", NULL, 0); + + struct http_decoder_half *half = + container_of(http, struct http_decoder_half, parser); + assert(half); + + if (half->parser.type == HTTP_REQUEST) { + if (half->event == HTTP_EVENT_REQ_BODY_DATA) { + half->event = HTTP_EVENT_REQ_BODY_END; + if (half->http_ev_cb != NULL) { + half->http_ev_cb(half->event, &half->ref_data, + half->http_ev_ctx); + } + } + } else { + if (half->event == HTTP_EVENT_RES_BODY_DATA) { + half->event = HTTP_EVENT_RES_BODY_END; + if (half->http_ev_cb != NULL) { + half->http_ev_cb(half->event, &half->ref_data, + half->http_ev_ctx); + } + } + } + + //trigger req_end/res_end + if (half->parser.type == HTTP_REQUEST) { + half->event = HTTP_EVENT_REQ_END; + if (half->http_ev_cb != NULL) { + half->http_ev_cb(half->event, &half->ref_data, half->http_ev_ctx); + } + } else { + half->event = HTTP_EVENT_RES_END; + if (half->http_ev_cb != NULL) { + half->http_ev_cb(half->event, &half->ref_data, half->http_ev_ctx); + } + } + + return 0; +} + +static int on_reset(llhttp_t *http) +{ + printf_debug_info("on_reset", NULL, 0); + + return 0; +} + +static int on_method(llhttp_t *http, const char *at, size_t length) +{ + printf_debug_info("on_method", at, length); + + struct http_decoder_half *half = + container_of(http, struct http_decoder_half, parser); + assert(half); + + http_decoder_table_refer(half->ref_data->table, HTTP_ITEM_METHOD, + at, length); + return 0; +} + +/* Information-only callbacks, return value is ignored */ +static int on_method_complete(llhttp_t *http) +{ + printf_debug_info("on_method_complete", NULL, 0); + + struct http_decoder_half *half = + container_of(http, struct http_decoder_half, parser); + assert(half); + + if (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_METHOD) == + STRING_STATE_REFER) { + http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_METHOD); + } + + http_decoder_table_commit(half->ref_data->table, HTTP_ITEM_METHOD); + + return 0; +} + +/* Possible return values 0, -1, HPE_USER */ +static int on_uri(llhttp_t *http, const char *at, size_t length) +{ + printf_debug_info("on_uri", at, length); + + struct http_decoder_half *half = + container_of(http, struct http_decoder_half, parser); + assert(half); + + http_decoder_table_refer(half->ref_data->table, HTTP_ITEM_URI, + at, length); + return 0; +} + +static void http_decoder_cached_portion_url(struct http_decoder_half *half, const struct hstring *uri_result) +{ + struct http_decoder_half_data *ref_data = half->ref_data; + + ref_data->joint_url.str_len = uri_result->str_len; + ref_data->joint_url.str = MEMPOOL_CALLOC(half->http_ev_ctx->ref_mempool, char, uri_result->str_len); + // ref_data->joint_url.str = (char *)malloc(uri_result->str_len); + memcpy(ref_data->joint_url.str, uri_result->str, uri_result->str_len); +} + +/* Information-only callbacks, return value is ignored */ +static int on_uri_complete(llhttp_t *http) +{ + printf_debug_info("on_uri_complete", NULL, 0); + + struct http_decoder_half *half = + container_of(http, struct http_decoder_half, parser); + assert(half); + + if (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_URI) == + STRING_STATE_REFER) { + http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_URI); + } + + http_decoder_table_commit(half->ref_data->table, HTTP_ITEM_URI); + + struct hstring uri_result = {}; + http_decoder_table_get_uri(half->ref_data->table, &uri_result); + assert(uri_result.str); + http_decoder_cached_portion_url(half, &uri_result); + + return 0; +} + +/* Possible return values 0, -1, HPE_USER */ +static int on_version(llhttp_t *http, const char *at, size_t length) +{ + printf_debug_info("on_version", at, length); + + struct http_decoder_half *half = + container_of(http, struct http_decoder_half, parser); + assert(half); + + http_decoder_table_refer(half->ref_data->table, HTTP_ITEM_VERSION, + at, length); + return 0; +} + +/* Information-only callbacks, return value is ignored */ +static int on_version_complete(llhttp_t *http) +{ + printf_debug_info("on_version_complete", NULL, 0); + + struct http_decoder_half *half = + container_of(http, struct http_decoder_half, parser); + assert(half); + + if (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_VERSION) == + STRING_STATE_REFER) { + http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_VERSION); + } + + http_decoder_table_commit(half->ref_data->table, HTTP_ITEM_VERSION); + + half->ref_data->major_version = llhttp_get_http_major(&half->parser); + half->ref_data->minor_version = llhttp_get_http_minor(&half->parser); + + if (half->parser.type == HTTP_REQUEST) { + half->event = HTTP_EVENT_REQ_LINE; + if (half->http_ev_cb) { + half->http_ev_cb(half->event, &half->ref_data, half->http_ev_ctx); + } + } + + return 0; +} + +/* Possible return values 0, -1, HPE_USER */ +static int on_status(llhttp_t *http, const char *at, size_t length) +{ + printf_debug_info("on_status", at, length); + + struct http_decoder_half *half = + container_of(http, struct http_decoder_half, parser); + assert(half); + + http_decoder_table_refer(half->ref_data->table, HTTP_ITEM_STATUS, + at, length); + return 0; +} + +/* Information-only callbacks, return value is ignored */ +static int on_status_complete(llhttp_t *http) +{ + printf_debug_info("on_status_complete", NULL, 0); + + struct http_decoder_half *half = + container_of(http, struct http_decoder_half, parser); + assert(half); + + if (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_STATUS) == + STRING_STATE_REFER) { + http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_STATUS); + } + + http_decoder_table_commit(half->ref_data->table, HTTP_ITEM_STATUS); + half->ref_data->status_code = llhttp_get_status_code(&half->parser); + + if (half->parser.type == HTTP_RESPONSE) { + half->event = HTTP_EVENT_RES_LINE; + if (half->http_ev_cb != NULL) { + half->http_ev_cb(half->event, &half->ref_data, half->http_ev_ctx); + } + } + + return 0; +} + +/* Possible return values 0, -1, HPE_USER */ +static int on_header_field(llhttp_t *http, const char *at, size_t length) +{ + printf_debug_info("on_header_field", at, length); + + struct http_decoder_half *half = + container_of(http, struct http_decoder_half, parser); + assert(half); + + http_decoder_table_refer(half->ref_data->table, HTTP_ITEM_HDRKEY, + at, length); + return 0; +} + +/* Information-only callbacks, return value is ignored */ +static int on_header_field_complete(llhttp_t *http) +{ + printf_debug_info("on_header_field_complete", NULL, 0); + + struct http_decoder_half *half = + container_of(http, struct http_decoder_half, parser); + assert(half); + + http_decoder_table_commit(half->ref_data->table, HTTP_ITEM_HDRKEY); + + return 0; +} + +/* Possible return values 0, -1, HPE_USER */ +static int on_header_value(llhttp_t *http, const char *at, size_t length) +{ + printf_debug_info("on_header_value", at, length); + + struct http_decoder_half *half = + container_of(http, struct http_decoder_half, parser); + assert(half); + + http_decoder_table_refer(half->ref_data->table, HTTP_ITEM_HDRVAL, + at, length); + return 0; +} + +#define MAX_ENCODING_STR_LEN 8 +/* Information-only callbacks, return value is ignored */ +static int on_header_value_complete(llhttp_t *http) +{ + printf_debug_info("on_header_value_complete", NULL, 0); + + struct http_decoder_half *half = + container_of(http, struct http_decoder_half, parser); + assert(half); + + if (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_HDRKEY) == + STRING_STATE_CACHE) { + http_decoder_table_commit(half->ref_data->table, HTTP_ITEM_HDRKEY); + } + + http_decoder_table_commit(half->ref_data->table, HTTP_ITEM_HDRVAL); + + if (half->ref_data->content_encoding == HTTP_CONTENT_ENCODING_NONE) { + struct http_header http_hdr = {0}; + struct hstring key = {.str = (char *)"Content-Encoding", .str_len = 16}; + + if (http_decoder_table_get_header(half->ref_data->table, &key, + &http_hdr, 1) == 1) { + char encoding_str[MAX_ENCODING_STR_LEN + 1] = {0}; + size_t str_len = http_hdr.val.str_len; + if (str_len > MAX_ENCODING_STR_LEN) { + str_len = MAX_ENCODING_STR_LEN; + } + memcpy(encoding_str, http_hdr.val.str, str_len); + half->ref_data->content_encoding = http_content_encoding_str2int(encoding_str); + } + } + + http_decoder_get_host_feed_url(half); + + return 0; +} + +/* When on_chunk_header is called, the current chunk length is stored + * in parser->content_length. + * Possible return values 0, -1, `HPE_PAUSED` + */ +static int on_chunk_header(llhttp_t *http) +{ + printf_debug_info("on_chunk_header", NULL, 0); + + return 0; +} + +/* When on_chunk_header is called, the current chunk length is stored + * in parser->content_length. + * Possible return values 0, -1, `HPE_PAUSED` + */ +static int on_chunk_header_complete(llhttp_t *http) +{ + printf_debug_info("on_chunk_header_complete", NULL, 0); + + return 0; +} + +/* Possible return values: + * 0 - Proceed normally + * 1 - Assume that request/response has no body, and proceed to parsing the next message + * 2 - Assume absence of body (as above) and make `llhttp_execute()` return `HPE_PAUSED_UPGRADE` + * -1 - Error `HPE_PAUSED` + */ +static int on_headers_complete(llhttp_t *http) +{ + printf_debug_info("on_headers_complete", NULL, 0); + + struct http_decoder_half *half = + container_of(http, struct http_decoder_half, parser); + assert(half); + assert(half->ref_data); + + http_decoder_table_set_header_complete(half->ref_data->table); + + if (half->parser.type == HTTP_REQUEST) { + half->event = HTTP_EVENT_REQ_HDR_END; + if (half->http_ev_cb) { + half->http_ev_cb(half->event, &half->ref_data, half->http_ev_ctx); + } + } + + if (half->parser.type == HTTP_RESPONSE) { + half->event = HTTP_EVENT_RES_HDR_END; + if (half->http_ev_cb) { + half->http_ev_cb(half->event, &half->ref_data, half->http_ev_ctx); + } + } + + return 0; +} + +/* Possible return values 0, -1, HPE_USER */ +static int on_body(llhttp_t *http, const char *at, size_t length) +{ + printf_debug_info("on_body", at, length); + + struct http_decoder_half *half = + container_of(http, struct http_decoder_half, parser); + assert(half); + + // trigger body_begin event + if (half->parser.type == HTTP_REQUEST) { + if (half->event == HTTP_EVENT_REQ_HDR_END) { + half->event = HTTP_EVENT_REQ_BODY_BEGIN; + if (half->http_ev_cb) { + half->http_ev_cb(half->event, &half->ref_data, + half->http_ev_ctx); + } + } + } else { + if (half->event == HTTP_EVENT_RES_HDR_END) { + half->event = HTTP_EVENT_RES_BODY_BEGIN; + if (half->http_ev_cb) { + half->http_ev_cb(half->event, &half->ref_data, + half->http_ev_ctx); + } + } + } + + if (half->ref_data != NULL) { + if (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_BODY) == + STRING_STATE_COMMIT) { + http_decoder_table_reset(half->ref_data->table, HTTP_ITEM_BODY); + } + + http_decoder_table_refer(half->ref_data->table, HTTP_ITEM_BODY, + at, length); + http_decoder_table_commit(half->ref_data->table, HTTP_ITEM_BODY); + } + + if (1 == half->decompress_switch && + half->ref_data->content_encoding != HTTP_CONTENT_ENCODING_NONE) { + http_decoder_half_data_decompress(half->ref_data); + } + + if (half->parser.type == HTTP_REQUEST) { + half->event = HTTP_EVENT_REQ_BODY_DATA; + if (half->http_ev_cb) { + half->http_ev_cb(half->event, &half->ref_data, half->http_ev_ctx); + } + } else { + half->event = HTTP_EVENT_RES_BODY_DATA; + if (half->http_ev_cb) { + half->http_ev_cb(half->event, &half->ref_data, half->http_ev_ctx); + } + } + + return 0; +} + +static void +http_decoder_half_init(struct http_decoder_half *half, + http_event_cb *http_ev_cb, int type) +{ + if (NULL == half) { + return; + } + + llhttp_settings_init(&half->settings); + llhttp_init(&half->parser, type, &half->settings); + + half->settings.on_message_begin = on_message_begin; + half->settings.on_message_complete = on_message_complete; + half->settings.on_reset = on_reset; + + half->settings.on_url = on_uri; + half->settings.on_url_complete = on_uri_complete; + + half->settings.on_status = on_status; + half->settings.on_status_complete = on_status_complete; + + half->settings.on_method = on_method; + half->settings.on_method_complete = on_method_complete; + + half->settings.on_version = on_version; + half->settings.on_version_complete = on_version_complete; + + half->settings.on_header_field = on_header_field; + half->settings.on_header_field_complete = on_header_field_complete; + + half->settings.on_header_value = on_header_value; + half->settings.on_header_value_complete = on_header_value_complete; + + half->settings.on_chunk_header = on_chunk_header; + half->settings.on_chunk_complete = on_chunk_header_complete; + + half->settings.on_headers_complete = on_headers_complete; + half->settings.on_body = on_body; + + half->error = HPE_OK; + + half->http_ev_cb = http_ev_cb; + + half->ref_data = NULL; +} + +struct http_decoder_half * +http_decoder_half_new(nmx_pool_t *mempool, http_event_cb *ev_cb, int http_type, + int decompress_switch) +{ + struct http_decoder_half *half = MEMPOOL_CALLOC(mempool, struct http_decoder_half, 1); + assert(half); + + half->decompress_switch = decompress_switch; + half->http_ev_ctx = MEMPOOL_CALLOC(mempool, struct http_event_context, 1); + http_decoder_half_init(half, ev_cb, http_type); + + return half; +} + +void http_decoder_half_free(nmx_pool_t *mempool, struct http_decoder_half *half) +{ + if (NULL == half) { + return; + } + + if (half->http_ev_ctx != NULL) { + MEMPOOL_FREE(mempool, half->http_ev_ctx); + half->http_ev_ctx = NULL; + } + + MEMPOOL_FREE(mempool, half); +} + +void http_decoder_half_reinit(struct http_decoder_half *half, int topic_id, + struct http_decoder_result_queue *queue, + nmx_pool_t *mempool, struct session *sess) +{ + assert(half != NULL); + + if (half->ref_data != NULL) { + http_decoder_table_reinit(half->ref_data->table); + } + + half->http_ev_ctx->topic_id = topic_id; + half->http_ev_ctx->ref_mempool = mempool; + half->http_ev_ctx->ref_session = sess; + half->http_ev_ctx->ref_queue = queue; +} + +static void publish_message_for_parsed_header(struct http_decoder_half *half) +{ + if (0 == http_decoder_table_has_parsed_header(half->ref_data->table)) { + return; + } + + // publish complete kv-header message + struct http_message *msg = NULL; + size_t queue_idx = 0; + struct http_decoder_result_queue *queue = half->http_ev_ctx->ref_queue; + + if (half->parser.type == HTTP_REQUEST) { + queue_idx = http_decoder_result_queue_req_index(queue); + + msg = http_message_new(HTTP_MESSAGE_REQ_HEADER, queue, queue_idx); + + session_mq_publish_message(half->http_ev_ctx->ref_session, + half->http_ev_ctx->topic_id, msg); + } else { + // http response + queue_idx = http_decoder_result_queue_res_index(queue); + + msg = http_message_new(HTTP_MESSAGE_RES_HEADER, queue, queue_idx); + + session_mq_publish_message(half->http_ev_ctx->ref_session, + half->http_ev_ctx->topic_id, msg); + } +} + +int http_decoder_half_parse(struct http_decoder_half *half, const char *data, + size_t data_len) +{ + if (NULL == half || NULL == data || 0 == data_len) { + return -1; + } + + half->error = llhttp_execute(&half->parser, data, data_len); + + int ret = 0; + uint8_t type = 0; + struct http_decoder_half_data *half_data = NULL; + + switch (half->error) { + case HPE_OK: + break; + case HPE_PAUSED: + llhttp_resume(&half->parser); + break; + case HPE_PAUSED_UPGRADE: + llhttp_resume_after_upgrade(&half->parser); + ret = 0; + break; + default: + type = half->parser.type; + llhttp_init(&half->parser, type, &half->settings); + ret = -1; + break; + } + + if (ret < 0) { + // fprintf(stdout, + // "llhttp_execute parse error: %s err_reason:%s\n", + // llhttp_errno_name(half->error), half->parser.reason); + return half->error; + } + + if (half->ref_data != NULL) { + if (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_URI) + == STRING_STATE_REFER) { + http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_URI); + } + + if (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_STATUS) + == STRING_STATE_REFER) { + http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_STATUS); + } + + if (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_METHOD) + == STRING_STATE_REFER) { + http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_METHOD); + } + + if (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_VERSION) + == STRING_STATE_REFER) { + http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_VERSION); + } + + if (http_decoder_table_header_complete(half->ref_data->table)) { + http_decoder_table_reset_header_complete(half->ref_data->table); + } else { + publish_message_for_parsed_header(half); + } + + enum string_state hdr_key_state = + http_decoder_table_state(half->ref_data->table, HTTP_ITEM_HDRKEY); + enum string_state hdr_val_state = + http_decoder_table_state(half->ref_data->table, HTTP_ITEM_HDRVAL); + + /* Truncated in http header key + For example http header k-v => User-Agent: Chrome + case1: + packet1: User- hdr_key_state == STRING_STATE_REFER + packet2: Agent: Chrome + + case2: + packet1: User-Agent: hdr_key_state == STRING_STATE_COMMIT + hdr_val_state == STRING_STATE_INIT + packet2: Chrome + */ + if (hdr_key_state == STRING_STATE_REFER || + (hdr_key_state == STRING_STATE_COMMIT && hdr_val_state == STRING_STATE_INIT)) { + http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_HDRKEY); + } + + /* Truncated in http header value + For example http header k-v => User-Agent: Chrome + packet1: User-Agent: Ch hdr_key_state == STRING_STATE_COMMIT + hdr_val_state == STRING_STATE_REFER + + packet2: rome + */ + if (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_HDRVAL) + == STRING_STATE_REFER) { + /* Header key should have been committed + If it's not cached, cache it for next packet to use + */ + http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_HDRKEY); + http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_HDRVAL); + } + + if (http_decoder_table_state(half->ref_data->table, HTTP_ITEM_BODY) + == STRING_STATE_REFER) { + http_decoder_table_cache(half->ref_data->table, HTTP_ITEM_BODY); + } + } + + return 0; +} + +long long http_decoder_half_trans_count(struct http_decoder_half *half) +{ + if (NULL == half) { + return 0; + } + + long long trans_cnt = half->trans_counter; + half->trans_counter = 0; + + return trans_cnt; +} + +struct http_decoder_half_data * +http_decoder_half_data_new(nmx_pool_t *mempool) +{ + struct http_decoder_half_data *data = + MEMPOOL_CALLOC(mempool, struct http_decoder_half_data, 1); + assert(data); + + data->table = http_decoder_table_new(mempool); + assert(data->table); + + data->major_version = -1; + data->minor_version = -1; + data->status_code = -1; + + data->content_encoding = HTTP_CONTENT_ENCODING_NONE; + data->ref_decompress_body = NULL; + data->decompress_body_len = 0; + + return data; +} + +void http_decoder_half_data_free(nmx_pool_t *mempool, + struct http_decoder_half_data *data) +{ + if (NULL == data) { + return; + } + + if (data->table != NULL) { + http_decoder_table_free(data->table); + data->table = NULL; + } + + if (data->decompress != NULL) { + http_content_decompress_destroy(data->decompress); + data->decompress = NULL; + } + + if(data->joint_url.str) + { + MEMPOOL_FREE(mempool, data->joint_url.str); + data->joint_url.str = NULL; + data->joint_url_complete = 0; + } + + MEMPOOL_FREE(mempool, data); +} + +int http_decoder_half_data_get_request_line(struct http_decoder_half_data *data, + struct http_request_line *line) +{ + if (NULL == data || NULL == line) { + return -1; + } + + http_decoder_table_get_method(data->table, &line->method); + http_decoder_table_get_uri(data->table, &line->uri); + http_decoder_table_get_version(data->table, &line->version); + + line->major_version = data->major_version; + line->minor_version = data->minor_version; + + return 0; +} + +int http_decoder_half_data_get_response_line(struct http_decoder_half_data *data, + struct http_response_line *line) +{ + if (NULL == data || NULL == line) { + return -1; + } + + http_decoder_table_get_version(data->table, &line->version); + http_decoder_table_get_status(data->table, &line->status); + + line->major_version = data->major_version; + line->minor_version = data->minor_version; + line->status_code = data->status_code; + + return 0; +} + +int http_decoder_half_data_get_header(struct http_decoder_half_data *data, + struct hstring *key, + struct http_header *hdr_array, + size_t array_size) +{ + if (NULL == data || NULL == key || + NULL == hdr_array || 0 == array_size) { + return -1; + } + + return http_decoder_table_get_header(data->table, key, hdr_array, array_size); +} + +int http_decoder_half_data_iter_header(struct http_decoder_half_data *data, + struct http_header *header) +{ + if (NULL == data || NULL == header) { + return -1; + } + + return http_decoder_table_iter_header(data->table, header); +} + +int http_decoder_half_data_has_parsed_header(struct http_decoder_half_data *data) +{ + if (NULL == data) { + return 0; + } + + return http_decoder_table_has_parsed_header(data->table); +} + +int http_decoder_half_data_get_raw_body(struct http_decoder_half_data *data, + struct hstring *body) +{ + if (NULL == data || NULL == body) { + return -1; + } + + return http_decoder_table_get_body(data->table, body); +} + +int http_decoder_half_data_get_decompress_body(struct http_decoder_half_data *data, + struct hstring *body) +{ + if (NULL == data || NULL == body) { + return -1; + } + + if (HTTP_CONTENT_ENCODING_NONE == data->content_encoding) { + return http_decoder_table_get_body(data->table, body); + } + + + body->str = data->ref_decompress_body; + body->str_len = data->decompress_body_len; + return 0; +} + +void http_decoder_half_data_dump(struct http_decoder_half *half) +{ + if (NULL == half || NULL == half->ref_data) { + return; + } + + http_decoder_table_dump(half->ref_data->table); +} + +static void using_session_addr_as_host(struct session *ref_session, + struct http_header *host_result, nmx_pool_t *mempool) +{ + const struct session_addr *ssaddr; + enum session_addr_type ssaddr_type; + ssaddr = session_get0_addr(ref_session, &ssaddr_type); + if (!ssaddr) + { + assert(0); + } + + char ip_string_buf[INET6_ADDRSTRLEN]; + if (SESSION_ADDR_TYPE_IPV4_TCP == ssaddr_type || SESSION_ADDR_TYPE_IPV4_UDP == ssaddr_type) + { + host_result->val.str = MEMPOOL_CALLOC(mempool, char, (INET_ADDRSTRLEN + 7) /* "ip:port" max length */); + inet_ntop(AF_INET, &ssaddr->ipv4.daddr, ip_string_buf, INET_ADDRSTRLEN); + sprintf(host_result->val.str, "%s:%u", ip_string_buf, ntohs(ssaddr->ipv4.dport)); + host_result->val.str_len = strlen(host_result->val.str); + } + else if (SESSION_ADDR_TYPE_IPV6_TCP == ssaddr_type || SESSION_ADDR_TYPE_IPV6_UDP == ssaddr_type) + { + host_result->val.str = MEMPOOL_CALLOC(mempool, char, (INET6_ADDRSTRLEN + 7) /* "ip:port" max length */); + inet_ntop(AF_INET6, &ssaddr->ipv6.daddr, ip_string_buf, INET6_ADDRSTRLEN); + sprintf(host_result->val.str, "%s:%u", ip_string_buf, ntohs(ssaddr->ipv6.dport)); + host_result->val.str_len = strlen(host_result->val.str); + } + else + { + assert(0); + } +} + +void http_decoder_join_url(struct http_decoder_half_data *hfdata, nmx_pool_t *mempool, const struct http_header *host_hdr) +{ + //int url_cache_str_len = strlen("http://") + host_hdr->val.str_len + hfdata->joint_url.str_len; + int url_cache_str_len = host_hdr->val.str_len + hfdata->joint_url.str_len; + char *url_cache_str = MEMPOOL_CALLOC(mempool, char, url_cache_str_len); + + char *ptr = url_cache_str; + //memcpy(ptr, "http://", strlen("http://")); + //ptr += strlen("http://"); + memcpy(ptr, host_hdr->val.str, host_hdr->val.str_len); + ptr += host_hdr->val.str_len; + memcpy(ptr, hfdata->joint_url.str, hfdata->joint_url.str_len); + + MEMPOOL_FREE(mempool, hfdata->joint_url.str); // free the cached uri buffer + hfdata->joint_url.str = url_cache_str; + hfdata->joint_url.str_len = url_cache_str_len; + + hfdata->joint_url_complete = 1; +} + +int http_decoder_join_url_finally(struct http_event_context *ev_ctx, + struct http_decoder_half_data *hfdata, + nmx_pool_t *mempool) +{ + struct http_header addr_as_host = {}; + + if (hfdata->joint_url_complete) + { + return 0; + } + + using_session_addr_as_host(ev_ctx->ref_session, &addr_as_host, mempool); + http_decoder_join_url(hfdata, mempool, &addr_as_host); + MEMPOOL_FREE(mempool, addr_as_host.val.str); // free session addr to host buffer + return 1; +} + +void http_decoder_get_host_feed_url(struct http_decoder_half *half) +{ + struct http_header host_result = {}; + struct hstring host_key = {"Host", 4}; + const char *host_refer_str = NULL; + int host_refer_len = 0; + + if (half->ref_data->joint_url_complete) + { + return; + } + + int host_header_cnt = http_decoder_half_data_get_header(half->ref_data, &host_key, + &host_result, 1); + if (host_header_cnt <= 0) + { + return; + } + + http_decoder_join_url(half->ref_data, half->http_ev_ctx->ref_mempool, &host_result); +} + +int http_half_data_get_url(struct http_decoder_half_data *res_data, struct hstring *url) +{ + if (0 == res_data->joint_url_complete) + { + return -1; + } + + url->str = res_data->joint_url.str; + url->str_len = res_data->joint_url.str_len; + + return 0; +} \ No newline at end of file diff --git a/src/http_decoder_half.h b/src/http_decoder_half.h new file mode 100644 index 0000000..0fcd0b6 --- /dev/null +++ b/src/http_decoder_half.h @@ -0,0 +1,119 @@ +/* +********************************************************************************************** +* File: http_decoder_half.h +* Description: +* Authors: Liu WenTan +* Date: 2024-01-10 +* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved. +*********************************************************************************************** +*/ + + +#ifndef _HTTP_DECODER_HALF_H_ +#define _HTTP_DECODER_HALF_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +#include "stellar/session.h" +#include "http_decoder.h" +#include "http_content_decompress.h" +#include "http_decoder_result_queue.h" + +// only one http event is fired at a time +enum http_event { + HTTP_EVENT_REQ_INIT = 1 << 1, + HTTP_EVENT_REQ_LINE = 1 << 2, + HTTP_EVENT_REQ_HDR = 1 << 3, + HTTP_EVENT_REQ_HDR_END = 1 << 4, + HTTP_EVENT_REQ_BODY_BEGIN = 1 << 5, + HTTP_EVENT_REQ_BODY_DATA = 1 << 6, + HTTP_EVENT_REQ_BODY_END = 1 << 7, + HTTP_EVENT_REQ_END = 1 << 8, + + HTTP_EVENT_RES_INIT = 1 << 9, + HTTP_EVENT_RES_LINE = 1 << 10, + HTTP_EVENT_RES_HDR = 1 << 11, + HTTP_EVENT_RES_HDR_END = 1 << 12, + HTTP_EVENT_RES_BODY_BEGIN = 1 << 13, + HTTP_EVENT_RES_BODY_DATA = 1 << 14, + HTTP_EVENT_RES_BODY_END = 1 << 15, + HTTP_EVENT_RES_END = 1 << 16, +}; + +struct http_event_context { + int topic_id; + nmx_pool_t *ref_mempool; + struct session *ref_session; + struct http_decoder_result_queue *ref_queue; +}; + +struct http_decoder_half; +struct http_decoder_half_data; + +typedef void http_event_cb(enum http_event event, struct http_decoder_half_data **data, + struct http_event_context *ev_ctx); + +struct http_decoder_half * +http_decoder_half_new(nmx_pool_t *mempool, http_event_cb *event_cb, int http_type, + int decompress_switch); + +void http_decoder_half_free(nmx_pool_t *mempool, struct http_decoder_half *half); + +void http_decoder_half_reinit(struct http_decoder_half *half, int topic_id, + struct http_decoder_result_queue *queue, + nmx_pool_t *mempool, struct session *sess); + +int http_decoder_half_parse(struct http_decoder_half *half, const char *data, + size_t data_len); + +long long http_decoder_half_trans_count(struct http_decoder_half *half); + +//http decoder half data API +struct http_decoder_half_data * +http_decoder_half_data_new(nmx_pool_t *mempool); + +void http_decoder_half_data_free(nmx_pool_t *mempool, + struct http_decoder_half_data *data); + +int http_decoder_half_data_get_request_line(struct http_decoder_half_data *data, + struct http_request_line *line); + +int http_decoder_half_data_get_response_line(struct http_decoder_half_data *data, + struct http_response_line *line); + +int http_decoder_half_data_get_header(struct http_decoder_half_data *data, + struct hstring *key, struct http_header *hdr_array, + size_t array_size); + +int http_decoder_half_data_iter_header(struct http_decoder_half_data *data, + struct http_header *header); + +int http_decoder_half_data_has_parsed_header(struct http_decoder_half_data *data); + +int http_decoder_half_data_get_raw_body(struct http_decoder_half_data *data, + struct hstring *body); + +int http_decoder_half_data_get_decompress_body(struct http_decoder_half_data *data, + struct hstring *body); + +void http_decoder_half_data_dump(struct http_decoder_half *half); + +void http_decoder_get_host_feed_url(struct http_decoder_half *half); +void http_decoder_join_url(struct http_decoder_half_data *hfdata, + nmx_pool_t *mempool, + const struct http_header *host_hdr); +int http_decoder_join_url_finally(struct http_event_context *ev_ctx, + struct http_decoder_half_data *hfdata, + nmx_pool_t *mempool); +int http_half_data_get_url(struct http_decoder_half_data *res_data, struct hstring *url); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/http_decoder_inc.h b/src/http_decoder_inc.h new file mode 100644 index 0000000..2d6a5c0 --- /dev/null +++ b/src/http_decoder_inc.h @@ -0,0 +1,53 @@ +/* +********************************************************************************************** +* File: http_decoder_inc.h +* Description: +* Authors: Liu WenTan +* Date: 2024-01-10 +* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved. +*********************************************************************************************** +*/ + +#ifndef _HTTP_DECODER_INC_H_ +#define _HTTP_DECODER_INC_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "mempool/nmx_palloc.h" +#include "stellar/utils.h" +#include "http_decoder.h" +#include "http_decoder_result_queue.h" + + +#define MEMPOOL_CALLOC(pool, type, number) ((type *)nmx_pcalloc(pool, sizeof(type) * number)) +#define MEMPOOL_REALLOC(pool) +#define MEMPOOL_FREE(pool, p) nmx_pfree(pool, p) + +#ifdef ENABLE_MEMPOOL + +#define HD_CALLOC(pool, type, number) MEMPOOL_CALLOC(pool, number, type) +#define HD_FREE(pool, p) MEMPOOL_FREE(pool, p) + +#else + +#define HD_CALLOC(pool, type, number) CALLOC(type, number) +#define HD_FREE(pool, p) FREE(p) + +#endif + + +struct http_message; + +struct http_message * +http_message_new(enum http_message_type type, + struct http_decoder_result_queue *queue, + int queue_index); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/http_decoder_result_queue.c b/src/http_decoder_result_queue.c new file mode 100644 index 0000000..9a60d15 --- /dev/null +++ b/src/http_decoder_result_queue.c @@ -0,0 +1,172 @@ +/* +********************************************************************************************** +* File: http_decoder_result_queue.c +* Description: +* Authors: Liuwentan +* Date: 2024-01-15 +* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved. +*********************************************************************************************** +*/ + +#include + +#include "stellar/utils.h" +#include "http_decoder_half.h" +#include "http_decoder_inc.h" +#include "http_decoder_result_queue.h" + +struct http_decoder_result_queue * +http_decoder_result_queue_new(nmx_pool_t *mempool, size_t queue_size) +{ + struct http_decoder_result_queue *queue = + MEMPOOL_CALLOC(mempool, struct http_decoder_result_queue, 1); + assert(queue); + + queue->req_index = 0; + queue->res_index = 0; + queue->queue_size = queue_size; + + queue->array = MEMPOOL_CALLOC(mempool, struct http_decoder_result, + queue->queue_size); + assert(queue->array); + + return queue; +} + +void http_decoder_result_queue_free(nmx_pool_t *mempool, + struct http_decoder_result_queue *queue) +{ + if (NULL == queue) { + return; + } + + if (queue->array != NULL) { + for (size_t i = 0; i < queue->queue_size; i++) { + if (queue->array[i].req_data != NULL) { + http_decoder_half_data_free(mempool, queue->array[i].req_data); + queue->array[i].req_data = NULL; + } + + if (queue->array[i].res_data != NULL) { + http_decoder_half_data_free(mempool, queue->array[i].res_data); + queue->array[i].res_data = NULL; + } + } + + MEMPOOL_FREE(mempool, queue->array); + } + + MEMPOOL_FREE(mempool, queue); +} + +void http_decoder_result_queue_inc_req_index(struct http_decoder_result_queue *queue) +{ + assert(queue); + + queue->req_index++; + queue->req_index = queue->req_index % queue->queue_size; +} + +void http_decoder_result_queue_inc_res_index(struct http_decoder_result_queue *queue) +{ + assert(queue); + + queue->res_index++; + queue->res_index = queue->res_index % queue->queue_size; +} + +size_t http_decoder_result_queue_req_index(struct http_decoder_result_queue *queue) +{ + assert(queue); + + return queue->req_index; +} + +size_t http_decoder_result_queue_res_index(struct http_decoder_result_queue *queue) +{ + assert(queue); + + return queue->res_index; +} + +int http_decoder_result_queue_push_req(struct http_decoder_result_queue *queue, + struct http_decoder_half_data *req_data) +{ + if (NULL == queue || NULL == req_data) { + return -1; + } + + assert(queue->array[queue->req_index].req_data == NULL); + if (queue->array[queue->req_index].req_data != NULL) { + return -1; + } + + queue->array[queue->req_index].req_data = req_data; + return 0; +} + +int http_decoder_result_queue_push_res(struct http_decoder_result_queue *queue, + struct http_decoder_half_data *res_data) +{ + if (NULL == queue || NULL == res_data) { + return -1; + } + + assert(queue->array[queue->res_index].res_data == NULL); + if (queue->array[queue->res_index].res_data != NULL) { + return -1; + } + + queue->array[queue->res_index].res_data = res_data; + return 0; +} + +struct http_decoder_half_data * +http_decoder_result_queue_pop_req(struct http_decoder_result_queue *queue) +{ + if (NULL == queue) { + return NULL; + } + + struct http_decoder_half_data *req_data = + queue->array[queue->req_index].req_data; + queue->array[queue->req_index].req_data = NULL; + + return req_data; +} + +struct http_decoder_half_data * +http_decoder_result_queue_pop_res(struct http_decoder_result_queue *queue) +{ + if (NULL == queue) { + return NULL; + } + + struct http_decoder_half_data *res_data = + queue->array[queue->res_index].res_data; + queue->array[queue->res_index].res_data = NULL; + + return res_data; +} + +struct http_decoder_half_data * +http_decoder_result_queue_peek_req(struct http_decoder_result_queue *queue) +{ + if (NULL == queue) { + return NULL; + } + + assert(queue->req_index < queue->queue_size); + return queue->array[queue->req_index].req_data; +} + +struct http_decoder_half_data * +http_decoder_result_queue_peek_res(struct http_decoder_result_queue *queue) +{ + if (NULL == queue) { + return NULL; + } + + assert(queue->res_index < queue->queue_size); + return queue->array[queue->res_index].res_data; +} \ No newline at end of file diff --git a/src/http_decoder_result_queue.h b/src/http_decoder_result_queue.h new file mode 100644 index 0000000..cd2163a --- /dev/null +++ b/src/http_decoder_result_queue.h @@ -0,0 +1,73 @@ +/* +********************************************************************************************** +* File: http_decoder_result_queue.h +* Description: +* Authors: Liuwentan +* Date: 2024-01-15 +* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved. +*********************************************************************************************** +*/ + + +#ifndef _HTTP_DECODER_RESULT_QUEUE_H_ +#define _HTTP_DECODER_RESULT_QUEUE_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +#include "mempool/nmx_palloc.h" +#include "http_decoder_half.h" + +struct http_decoder_result { + struct http_decoder_half_data *req_data; + struct http_decoder_half_data *res_data; +}; + +struct http_decoder_result_queue { + size_t req_index; + size_t res_index; + size_t queue_size; + struct http_decoder_result *array; +}; + +struct http_decoder_result_queue * +http_decoder_result_queue_new(nmx_pool_t *mempool, size_t queue_size); + +void http_decoder_result_queue_free(nmx_pool_t *mempool, + struct http_decoder_result_queue *queue); + +void http_decoder_result_queue_inc_req_index(struct http_decoder_result_queue *queue); + +void http_decoder_result_queue_inc_res_index(struct http_decoder_result_queue *queue); + +size_t http_decoder_result_queue_req_index(struct http_decoder_result_queue *queue); + +size_t http_decoder_result_queue_res_index(struct http_decoder_result_queue *queue); + +struct http_decoder_half_data * +http_decoder_result_queue_pop_req(struct http_decoder_result_queue *queue); + +struct http_decoder_half_data * +http_decoder_result_queue_pop_res(struct http_decoder_result_queue *queue); + +int http_decoder_result_queue_push_req(struct http_decoder_result_queue *queue, + struct http_decoder_half_data *req_data); + +int http_decoder_result_queue_push_res(struct http_decoder_result_queue *queue, + struct http_decoder_half_data *res_data); + +struct http_decoder_half_data * +http_decoder_result_queue_peek_req(struct http_decoder_result_queue *queue); + +struct http_decoder_half_data * +http_decoder_result_queue_peek_res(struct http_decoder_result_queue *queue); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/http_decoder_string.c b/src/http_decoder_string.c new file mode 100644 index 0000000..5414c5b --- /dev/null +++ b/src/http_decoder_string.c @@ -0,0 +1,277 @@ +/* +********************************************************************************************** +* File: http_decoder_string.h +* Description: +* Authors: LuWenPeng +* Date: 2022-10-31 +* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved. +*********************************************************************************************** +*/ + +#include +#include +#include +#include + +#include "stellar/utils.h" +#include "http_decoder_utils.h" +#include "http_decoder_string.h" + +static const char *string_state_to_desc(enum string_state state) +{ + switch (state) { + case STRING_STATE_INIT: + return "init"; + break; + case STRING_STATE_REFER: + return "refer"; + break; + case STRING_STATE_CACHE: + return "cache"; + break; + case STRING_STATE_COMMIT: + return "commit"; + break; + default: + return "unknown"; + break; + } +} + +void http_decoder_string_refer(struct http_decoder_string *rstr, + const char *at, size_t length) +{ + if (NULL == rstr) { + return; + } + + switch (rstr->state) { + case STRING_STATE_INIT: + case STRING_STATE_CACHE: + rstr->refer.str = (char *)at; + rstr->refer.str_len = length; + break; + default: + abort(); + break; + } + + rstr->state = STRING_STATE_REFER; +} + +static void string_refer2cache(struct http_decoder_string *rstr) +{ + if (0 == rstr->refer.str_len) { + return; + } + + if (rstr->cache.str_len >= rstr->max_cache_size) { + return; + } + + size_t length = rstr->cache.str_len + rstr->refer.str_len; + if (length > rstr->max_cache_size) { + length = rstr->max_cache_size; + } + + if (NULL == rstr->cache.str) { + rstr->cache.str = CALLOC(char, length + 1); + memcpy(rstr->cache.str, rstr->refer.str, length); + } else { + rstr->cache.str = REALLOC(char, rstr->cache.str, length + 1); + memcpy(rstr->cache.str + rstr->cache.str_len, rstr->refer.str, + (length - rstr->cache.str_len)); + } + + rstr->cache.str_len = length; + + rstr->refer.str = NULL; + rstr->refer.str_len = 0; +} + +static void string_commit2cache(struct http_decoder_string *rstr) +{ + if (rstr->cache.str_len == rstr->commit.str_len && + rstr->cache.str == rstr->commit.str) { + + rstr->commit.str = NULL; + rstr->commit.str_len = 0; + return; + } + + //Only http header key need to backward to cache + size_t length = 0; + if (rstr->commit.str_len > rstr->max_cache_size) { + length = rstr->max_cache_size; + } else { + length = rstr->commit.str_len; + } + + if (length > 0) { + if (NULL == rstr->cache.str) { + rstr->cache.str = CALLOC(char, length + 1); + } else { + abort(); + } + memcpy(rstr->cache.str, rstr->commit.str, length); + rstr->cache.str_len = length; + + rstr->commit.str = NULL; + rstr->commit.str_len = 0; + } +} + +void http_decoder_string_cache(struct http_decoder_string *rstr) +{ + if (NULL == rstr) { + return; + } + + switch (rstr->state) { + case STRING_STATE_REFER: + string_refer2cache(rstr); + break; + case STRING_STATE_CACHE: + break; + case STRING_STATE_COMMIT: + //commit backward to cache + string_commit2cache(rstr); + break; + default: + abort(); + break; + } + + rstr->state = STRING_STATE_CACHE; +} + +void http_decoder_string_commit(struct http_decoder_string *rstr) +{ + if (NULL == rstr) { + return; + } + + switch (rstr->state) { + case STRING_STATE_REFER: + if (rstr->cache.str_len) { + http_decoder_string_cache(rstr); + + rstr->commit.str = rstr->cache.str; + rstr->commit.str_len = rstr->cache.str_len; + // not overwrite rstr->cache.str + } else { + rstr->commit.str = rstr->refer.str; + rstr->commit.str_len = rstr->refer.str_len; + + rstr->refer.str = NULL; + rstr->refer.str_len = 0; + } + break; + case STRING_STATE_CACHE: + rstr->commit.str = rstr->cache.str; + rstr->commit.str_len = rstr->cache.str_len; + // not overwrite rstr->cache.str + break; + default: + //abort(); + break; + } + + rstr->state = STRING_STATE_COMMIT; +} + +void http_decoder_string_reset(struct http_decoder_string *rstr) +{ + assert(rstr); + + switch (rstr->state) { + case STRING_STATE_INIT: + case STRING_STATE_REFER: + case STRING_STATE_CACHE: + case STRING_STATE_COMMIT: + FREE(rstr->cache.str); + memset(rstr, 0, sizeof(struct http_decoder_string)); + break; + default: + abort(); + break; + } + + rstr->state = STRING_STATE_INIT; +} + + +void http_decoder_string_init(struct http_decoder_string *rstr, + size_t max_cache_size) +{ + rstr->max_cache_size = max_cache_size; +} + +void http_decoder_string_reinit(struct http_decoder_string *rstr) +{ + if (rstr->state == STRING_STATE_CACHE) { + return; + } + + if (rstr->state == STRING_STATE_COMMIT && + rstr->cache.str == rstr->commit.str && + rstr->cache.str_len == rstr->commit.str_len) { + return; + } + + if (rstr->cache.str != NULL) { + FREE(rstr->cache.str); + rstr->cache.str_len = 0; + } + + rstr->refer.str = NULL; + rstr->refer.str_len = 0; + + rstr->commit.str = NULL; + rstr->commit.str_len = 0; + + rstr->state = STRING_STATE_INIT; +} + +enum string_state http_decoder_string_state(struct http_decoder_string *rstr) +{ + return rstr->state; +} + +int http_decoder_string_get(struct http_decoder_string *rstr, struct hstring *out) +{ + if (NULL == rstr || NULL == out) { + return -1; + } + + if (http_decoder_string_state(rstr) == STRING_STATE_COMMIT) { + out->str = rstr->commit.str; + out->str_len = rstr->commit.str_len; + } else { + out->str = NULL; + out->str_len = 0; + } + + return 0; +} + +void http_decoder_string_dump(struct http_decoder_string *rstr, const char *desc) +{ + if (NULL == rstr) { + return; + } + + char *refer_str = safe_dup(rstr->refer.str, rstr->refer.str_len); + char *cache_str = safe_dup(rstr->cache.str, rstr->cache.str_len); + char *commit_str = safe_dup(rstr->commit.str, rstr->commit.str_len); + + printf("%s: state: %s, refer: {len: %02zu, str: %s}, cache: {len: %02zu, str: %s}, commit: {len: %02zu, str: %s}\n", + desc, string_state_to_desc(rstr->state), + rstr->refer.str_len, refer_str, + rstr->cache.str_len, cache_str, + rstr->commit.str_len, commit_str); + + FREE(refer_str); + FREE(cache_str); + FREE(commit_str); +} \ No newline at end of file diff --git a/src/http_decoder_string.h b/src/http_decoder_string.h new file mode 100644 index 0000000..f9d81dd --- /dev/null +++ b/src/http_decoder_string.h @@ -0,0 +1,95 @@ +/* +********************************************************************************************** +* File: http_decoder_string.h +* Description: +* Authors: LuWenPeng +* Date: 2022-10-31 +* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved. +*********************************************************************************************** +*/ + +#ifndef _HTTP_DECODER_STRING_H_ +#define _HTTP_DECODER_STRING_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "http_decoder.h" + + +enum string_state { + STRING_STATE_INIT, + STRING_STATE_REFER, + STRING_STATE_CACHE, + STRING_STATE_COMMIT, +}; + +/* state transition diagram + * +----------+ + * | | + * \|/ | + * +------+ | + * | init | | + * +------+ | + * | | + * +---->| | + * | \|/ | + * | +-------+ | + * | | refer |--+ | + * | +-------+ | | + * | | | | + * | \|/ | | + * | +-------+ | | + * +--| cache | | | + * +-------+ | | + * | | | + * |<------+ | + * \|/ | + * +--------+ | + * | commit | | + * +--------+ | + * | | + * \|/ | + * +--------+ | + * | reset |----+ + * +--------+ + */ + + +//http decoder string +struct http_decoder_string { + struct hstring refer; // shallow copy + struct hstring cache; // deep copy + struct hstring commit; + + enum string_state state; + size_t max_cache_size; +}; + +void http_decoder_string_refer(struct http_decoder_string *rstr, + const char *at, size_t length); + +void http_decoder_string_cache(struct http_decoder_string *rstr); + +void http_decoder_string_commit(struct http_decoder_string *rstr); + +void http_decoder_string_reset(struct http_decoder_string *rstr); + +void http_decoder_string_init(struct http_decoder_string *rstr, + size_t max_cache_size); + +void http_decoder_string_reinit(struct http_decoder_string *rstr); + +enum string_state http_decoder_string_state(struct http_decoder_string *rstr); + +int http_decoder_string_get(struct http_decoder_string *rstr, struct hstring *out); + +void http_decoder_string_dump(struct http_decoder_string *rstr, const char *desc); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/http_decoder_table.c b/src/http_decoder_table.c new file mode 100644 index 0000000..ede7989 --- /dev/null +++ b/src/http_decoder_table.c @@ -0,0 +1,556 @@ +/* +********************************************************************************************** +* File: http_decoder_table.c +* Description: +* Authors: LuWenPeng +* Date: 2022-10-31 +* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved. +*********************************************************************************************** +*/ + + +#include +#include +#include + +#include "http_decoder_table.h" +#include "http_decoder.h" +#include "http_decoder_string.h" +#include "stellar/utils.h" + +#define INIT_HEADER_CNT 16 +#define MAX_URI_CACHE_SIZE 2048 +#define MAX_STATUS_CACHE_SIZE 32 +#define MAX_METHOD_CACHE_SIZE 8 +#define MAX_VERSION_CACHE_SIZE 4 +#define MAX_HEADER_KEY_CACHE_SIZE 4096 +#define MAX_HEADER_VALUE_CACHE_SIZE 4096 + +struct http_decoder_header { + struct http_decoder_string key; + struct http_decoder_string val; +}; + +struct http_decoder_table { + struct http_decoder_string uri; + struct http_decoder_string status; + struct http_decoder_string method; + struct http_decoder_string version; + struct http_decoder_string body; + + nmx_pool_t *ref_mempool; + int header_complete; // flag for all headers parsed completely + size_t header_cnt; + size_t header_index; + size_t header_iter; + struct http_decoder_header *headers; +}; + +static void http_decoder_table_init(struct http_decoder_table *table) +{ + if (NULL == table) { + return; + } + + struct http_decoder_header *header = NULL; + assert(table); + + http_decoder_string_init(&table->uri, MAX_URI_CACHE_SIZE); + http_decoder_string_init(&table->status, MAX_STATUS_CACHE_SIZE); + http_decoder_string_init(&table->method, MAX_METHOD_CACHE_SIZE); + http_decoder_string_init(&table->version, MAX_METHOD_CACHE_SIZE); + + for (size_t i = 0; i < table->header_cnt; i++) { + header = &table->headers[i]; + http_decoder_string_init(&header->key, MAX_HEADER_KEY_CACHE_SIZE); + http_decoder_string_init(&header->val, MAX_HEADER_VALUE_CACHE_SIZE); + } + + http_decoder_string_init(&table->body, 0); +} + +struct http_decoder_table *http_decoder_table_new(nmx_pool_t *mempool) +{ + struct http_decoder_table *table = + MEMPOOL_CALLOC(mempool, struct http_decoder_table, 1); + assert(table); + + table->ref_mempool = mempool; + table->header_cnt = INIT_HEADER_CNT; + table->headers = MEMPOOL_CALLOC(mempool, struct http_decoder_header, + table->header_cnt); + + http_decoder_table_init(table); + + return table; +} + +void http_decoder_table_free(struct http_decoder_table *table) +{ + if (NULL == table) { + return; + } + + if (table->uri.cache.str != NULL) { + FREE(table->uri.cache.str); + } + + if (table->status.cache.str != NULL) { + FREE(table->status.cache.str); + } + + if (table->method.cache.str != NULL) { + FREE(table->method.cache.str); + } + + if (table->version.cache.str != NULL) { + FREE(table->version.cache.str); + } + + if (table->body.cache.str != NULL) { + FREE(table->body.cache.str); + } + + if (table->headers != NULL) { + for (size_t i = 0; i < table->header_cnt; i++) { + if (table->headers[i].key.cache.str != NULL) { + FREE(table->headers[i].key.cache.str); + } + + if (table->headers[i].val.cache.str != NULL) { + FREE(table->headers[i].val.cache.str); + } + } + + MEMPOOL_FREE(table->ref_mempool, table->headers); + table->headers = NULL; + } + + MEMPOOL_FREE(table->ref_mempool, table); +} + +enum string_state +http_decoder_table_state(struct http_decoder_table *table, enum http_item type) +{ + if (NULL == table) { + return STRING_STATE_INIT; + } + + struct http_decoder_header *header = NULL; + enum string_state state = STRING_STATE_INIT; + assert(table); + + switch (type) { + case HTTP_ITEM_URI: + state = http_decoder_string_state(&table->uri); + break; + case HTTP_ITEM_STATUS: + state = http_decoder_string_state(&table->status); + break; + case HTTP_ITEM_METHOD: + state = http_decoder_string_state(&table->method); + break; + case HTTP_ITEM_VERSION: + state = http_decoder_string_state(&table->version); + break; + case HTTP_ITEM_HDRKEY: + assert(table->header_index < table->header_cnt); + header = &table->headers[table->header_index]; + state = http_decoder_string_state(&header->key); + break; + case HTTP_ITEM_HDRVAL: + assert(table->header_index < table->header_cnt); + header = &table->headers[table->header_index]; + state = http_decoder_string_state(&header->val); + break; + case HTTP_ITEM_BODY: + state = http_decoder_string_state(&table->body); + break; + default: + abort(); + break; + } + + return state; +} + +void http_decoder_table_refer(struct http_decoder_table *table, enum http_item type, + const char *at, size_t len) +{ + if (NULL == table) { + return; + } + + struct http_decoder_header *header = NULL; + assert(table); + + switch (type) { + case HTTP_ITEM_URI: + http_decoder_string_refer(&table->uri, at, len); + break; + case HTTP_ITEM_STATUS: + http_decoder_string_refer(&table->status, at, len); + break; + case HTTP_ITEM_METHOD: + http_decoder_string_refer(&table->method, at, len); + break; + case HTTP_ITEM_VERSION: + http_decoder_string_refer(&table->version, at, len); + break; + case HTTP_ITEM_HDRKEY: + assert(table->header_index < table->header_cnt); + header = &table->headers[table->header_index]; + http_decoder_string_refer(&header->key, at, len); + break; + case HTTP_ITEM_HDRVAL: + assert(table->header_index < table->header_cnt); + header = &table->headers[table->header_index]; + http_decoder_string_refer(&header->val, at, len); + break; + case HTTP_ITEM_BODY: + http_decoder_string_refer(&table->body, at, len); + break; + default: + abort(); + break; + } +} + +void http_decoder_table_cache(struct http_decoder_table *table, enum http_item type) +{ + if (NULL == table) { + return; + } + + struct http_decoder_header *header = NULL; + assert(table); + + switch (type) { + case HTTP_ITEM_URI: + http_decoder_string_cache(&table->uri); + break; + case HTTP_ITEM_STATUS: + http_decoder_string_cache(&table->status); + break; + case HTTP_ITEM_METHOD: + http_decoder_string_cache(&table->method); + break; + case HTTP_ITEM_VERSION: + http_decoder_string_cache(&table->version); + break; + case HTTP_ITEM_HDRKEY: + assert(table->header_index < table->header_cnt); + header = &table->headers[table->header_index]; + http_decoder_string_cache(&header->key); + break; + case HTTP_ITEM_HDRVAL: + assert(table->header_index < table->header_cnt); + header = &table->headers[table->header_index]; + http_decoder_string_cache(&header->val); + break; + case HTTP_ITEM_BODY: + http_decoder_string_cache(&table->body); + break; + default: + abort(); + break; + } +} + +void http_decoder_table_commit(struct http_decoder_table *table, enum http_item type) +{ + if (NULL == table) { + return; + } + + size_t i = 0; + struct http_decoder_header *header = NULL; + assert(table); + + switch (type) { + case HTTP_ITEM_URI: + http_decoder_string_commit(&table->uri); + break; + case HTTP_ITEM_STATUS: + http_decoder_string_commit(&table->status); + break; + case HTTP_ITEM_METHOD: + http_decoder_string_commit(&table->method); + break; + case HTTP_ITEM_VERSION: + http_decoder_string_commit(&table->version); + break; + case HTTP_ITEM_HDRKEY: + assert(table->header_index < table->header_cnt); + header = &table->headers[table->header_index]; + http_decoder_string_commit(&header->key); + break; + case HTTP_ITEM_HDRVAL: + header = &table->headers[table->header_index]; + http_decoder_string_commit(&header->val); + // inc index + if ((table->header_index + 1) >= table->header_cnt) { + struct http_decoder_header *old_headers = table->headers; + table->headers = + MEMPOOL_CALLOC(table->ref_mempool, struct http_decoder_header, + table->header_cnt * 2); + table->header_cnt *= 2; + + for (i = 0; i <= table->header_index; i++) { + table->headers[i] = old_headers[i]; + } + + MEMPOOL_FREE(table->ref_mempool, old_headers); + + for (i = table->header_index + 1; i < table->header_cnt; i++) { + header = &table->headers[i]; + memset(header, 0, sizeof(struct http_decoder_header)); + http_decoder_string_init(&header->key, MAX_HEADER_KEY_CACHE_SIZE); + http_decoder_string_init(&header->val, MAX_HEADER_VALUE_CACHE_SIZE); + } + } + table->header_index++; + break; + case HTTP_ITEM_BODY: + http_decoder_string_commit(&table->body); + break; + default: + abort(); + break; + } +} + +void http_decoder_table_reset(struct http_decoder_table *table, enum http_item type) +{ + if (NULL == table) { + return; + } + + struct http_decoder_header *header = NULL; + assert(table); + + switch (type) { + case HTTP_ITEM_URI: + http_decoder_string_reset(&table->uri); + break; + case HTTP_ITEM_STATUS: + http_decoder_string_reset(&table->status); + break; + case HTTP_ITEM_METHOD: + http_decoder_string_reset(&table->method); + break; + case HTTP_ITEM_VERSION: + http_decoder_string_reset(&table->version); + break; + case HTTP_ITEM_HDRKEY: + header = &table->headers[table->header_index]; + http_decoder_string_reset(&header->key); + break; + case HTTP_ITEM_HDRVAL: + header = &table->headers[table->header_index]; + http_decoder_string_reset(&header->val); + break; + case HTTP_ITEM_BODY: + http_decoder_string_reset(&table->body); + break; + default: + abort(); + break; + } +} + +void http_decoder_table_reinit(struct http_decoder_table *table) +{ + if (NULL == table) { + return; + } + + struct http_decoder_header *header = NULL; + assert(table); + + http_decoder_string_reinit(&table->uri); + http_decoder_string_reinit(&table->status); + http_decoder_string_reinit(&table->method); + http_decoder_string_reinit(&table->version); + for (size_t i = 0; i < table->header_iter; i++) { + header = &table->headers[i]; + http_decoder_string_reinit(&header->key); + http_decoder_string_reinit(&header->val); + } + + http_decoder_string_reinit(&table->body); +} + +void http_decoder_table_dump(struct http_decoder_table *table) +{ + if (NULL == table) { + return; + } + + http_decoder_string_dump(&table->uri, "uri"); + http_decoder_string_dump(&table->status, "status"); + http_decoder_string_dump(&table->method, "method"); + http_decoder_string_dump(&table->version, "version"); + http_decoder_string_dump(&table->body, "body"); + + for (size_t i = 0; i < table->header_cnt; i++) { + struct http_decoder_header *header = &table->headers[i]; + if (NULL == header) { + continue; + } + + http_decoder_string_dump(&header->key, "key"); + http_decoder_string_dump(&header->val, "val"); + } +} + +int http_decoder_table_get_uri(struct http_decoder_table *table, struct hstring *out) +{ + if (NULL == table || NULL == out) { + return -1; + } + + return http_decoder_string_get(&table->uri, out); +} + +int http_decoder_table_get_method(struct http_decoder_table *table, struct hstring *out) +{ + if (NULL == table || NULL == out) { + return -1; + } + + return http_decoder_string_get(&table->method, out); +} + +int http_decoder_table_get_status(struct http_decoder_table *table, struct hstring *out) +{ + if (NULL == table || NULL == out) { + return -1; + } + + return http_decoder_string_get(&table->status, out); +} + +int http_decoder_table_get_version(struct http_decoder_table *table, struct hstring *out) +{ + if (NULL == table || NULL == out) { + return -1; + } + + return http_decoder_string_get(&table->version, out); +} + +int http_decoder_table_get_body(struct http_decoder_table *table, struct hstring *out) +{ + if (NULL == table || NULL == out) { + return -1; + } + + return http_decoder_string_get(&table->body, out); +} + +int http_decoder_table_get_header(struct http_decoder_table *table, struct hstring *key, + struct http_header *hdr_array, size_t array_size) +{ + if (NULL == table || NULL == key->str || 0 == key->str_len) { + return 0; + } + + int header_cnt = 0; + for (size_t i = 0; i < table->header_cnt && header_cnt < array_size; i++) { + struct http_decoder_header *tmp_header = &table->headers[i]; + if (tmp_header->key.commit.str_len != key->str_len) { + continue; + } + + if (http_decoder_string_state(&tmp_header->key) == STRING_STATE_COMMIT && + http_decoder_string_state(&tmp_header->val) == STRING_STATE_COMMIT) { + struct hstring tmp_key; + http_decoder_string_get(&tmp_header->key, &tmp_key); + + if (tmp_key.str_len == key->str_len && + (0 == strncasecmp(tmp_key.str, key->str, key->str_len))) { + http_decoder_string_get(&tmp_header->key, &hdr_array[header_cnt].key); + http_decoder_string_get(&tmp_header->val, &hdr_array[header_cnt].val); + header_cnt++; + } + } + } + + return header_cnt; +} + +int http_decoder_table_iter_header(struct http_decoder_table *table, + struct http_header *hdr) +{ + if (NULL == table || NULL == hdr) { + return -1; + } + + if (table->header_iter >= table->header_cnt) { + return 0; + } + + struct http_decoder_header *tmp_header = &table->headers[table->header_iter]; + if (tmp_header != NULL) { + if (http_decoder_string_state(&tmp_header->key) == STRING_STATE_COMMIT && + http_decoder_string_state(&tmp_header->val) == STRING_STATE_COMMIT) { + + http_decoder_string_get(&tmp_header->key, &hdr->key); + http_decoder_string_get(&tmp_header->val, &hdr->val); + table->header_iter++; + + return 1; + } + } + + hdr->key.str = NULL; + hdr->key.str_len = 0; + + hdr->val.str = NULL; + hdr->val.str_len = 0; + + return 0; +} + +int http_decoder_table_has_parsed_header(struct http_decoder_table *table) +{ + if (NULL == table || (table->header_iter == table->header_index)) { + return 0; + } + + struct http_decoder_header *tmp_header = &table->headers[table->header_iter]; + if (http_decoder_string_state(&tmp_header->key) == STRING_STATE_COMMIT && + http_decoder_string_state(&tmp_header->val) == STRING_STATE_COMMIT) { + return 1; + } + + return 0; +} + +int http_decoder_table_header_complete(struct http_decoder_table *table) +{ + if (NULL == table) { + return -1; + } + + return table->header_complete; +} + +void http_decoder_table_set_header_complete(struct http_decoder_table *table) +{ + if (NULL == table) { + return; + } + + table->header_complete = 1; +} + +void http_decoder_table_reset_header_complete(struct http_decoder_table *table) +{ + if (NULL == table) { + return; + } + + table->header_complete = 0; +} \ No newline at end of file diff --git a/src/http_decoder_table.h b/src/http_decoder_table.h new file mode 100644 index 0000000..fe443bb --- /dev/null +++ b/src/http_decoder_table.h @@ -0,0 +1,99 @@ +/* +********************************************************************************************** +* File: http_decoder_table.h +* Description: +* Authors: LuWenPeng +* Date: 2022-10-31 +* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved. +*********************************************************************************************** +*/ + + +#ifndef _HTTP_DECODER_TABLE_H_ +#define _HTTP_DECODER_TABLE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "http_decoder.h" +#include "http_decoder_inc.h" +#include "http_decoder_string.h" + +enum http_item { + HTTP_ITEM_URI = 0x01, + HTTP_ITEM_STATUS = 0x02, + HTTP_ITEM_METHOD = 0x03, + HTTP_ITEM_VERSION = 0x04, + HTTP_ITEM_HDRKEY = 0x05, + HTTP_ITEM_HDRVAL = 0x06, + HTTP_ITEM_BODY = 0x07, +}; + +struct http_decoder_table; +struct http_decoder_table *http_decoder_table_new(nmx_pool_t *mempool); + +void http_decoder_table_free(struct http_decoder_table *table); + +enum string_state +http_decoder_table_state(struct http_decoder_table *table, enum http_item type); + +void http_decoder_table_refer(struct http_decoder_table *table, enum http_item type, + const char *at, size_t len); + +void http_decoder_table_cache(struct http_decoder_table *table, enum http_item type); + +void http_decoder_table_commit(struct http_decoder_table *table, enum http_item type); + +void http_decoder_table_reset(struct http_decoder_table *table, enum http_item type); + +void http_decoder_table_reinit(struct http_decoder_table *table); + +void http_decoder_table_dump(struct http_decoder_table *table); + +int http_decoder_table_get_uri(struct http_decoder_table *table, struct hstring *out); + +int http_decoder_table_get_method(struct http_decoder_table *table, struct hstring *out); + +int http_decoder_table_get_status(struct http_decoder_table *table, struct hstring *out); + +int http_decoder_table_get_version(struct http_decoder_table *table, struct hstring *out); + +int http_decoder_table_get_body(struct http_decoder_table *table, struct hstring *out); + +int http_decoder_table_get_header(struct http_decoder_table *table, + struct hstring *key, + struct http_header *hdr_array, + size_t array_size); + +int http_decoder_table_iter_header(struct http_decoder_table *table, + struct http_header *hdr); + +/** + * @brief Is there a parsed header + * + * @retval yes(1) no(0) +*/ +int http_decoder_table_has_parsed_header(struct http_decoder_table *table); + +/** + * @brief If headers have been parsed completely + * + * @retval yes(1) no(0) + */ +int http_decoder_table_header_complete(struct http_decoder_table *table); + +/** + * @brief set flag for headers parsed completely +*/ +void http_decoder_table_set_header_complete(struct http_decoder_table *table); + +void http_decoder_table_reset_header_complete(struct http_decoder_table *table); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/http_decoder_utils.c b/src/http_decoder_utils.c new file mode 100644 index 0000000..a5dfbe1 --- /dev/null +++ b/src/http_decoder_utils.c @@ -0,0 +1,25 @@ +/* +********************************************************************************************** +* File: http_decoder_utils.c +* Description: +* Authors: LuWenPeng +* Date: 2022-10-31 +* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved. +*********************************************************************************************** +*/ + +#include + +#include "stellar/utils.h" + +char *safe_dup(const char *str, size_t len) +{ + if (str == NULL || len == 0) { + return NULL; + } + + char *dup = CALLOC(char, len + 1); + memcpy(dup, str, len); + + return dup; +} \ No newline at end of file diff --git a/src/http_decoder_utils.h b/src/http_decoder_utils.h new file mode 100644 index 0000000..9c031a3 --- /dev/null +++ b/src/http_decoder_utils.h @@ -0,0 +1,66 @@ +/* +********************************************************************************************** +* File: http_decoder_utils.h +* Description: +* Authors: LuWenPeng +* Date: 2022-10-31 +* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved. +*********************************************************************************************** +*/ + + +#ifndef _HTTP_DECODER_UTILS_H_ +#define _HTTP_DECODER_UTILS_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + + +char *safe_dup(const char *str, size_t len); + +/****************************************************************************** + * Logger + ******************************************************************************/ + +enum http_decoder_log_level { + DEBUG = 0x11, + WARN = 0x12, + INFO = 0x13, + ERROR = 0x14, +}; + +#ifndef http_decoder_log +#define http_decoder_log(level, format, ...) \ + { \ + switch (level) \ + { \ + case DEBUG: \ + fprintf(stdout, "HTTP_DECODER [DEBUG] " format "\n", ##__VA_ARGS__); \ + fflush(stdout); \ + break; \ + case WARN: \ + fprintf(stdout, "HTTP_DECODER [WARN] " format "\n", ##__VA_ARGS__); \ + fflush(stdout); \ + break; \ + case INFO: \ + fprintf(stdout, "HTTP_DECODER [INFO] " format "\n", ##__VA_ARGS__); \ + fflush(stdout); \ + break; \ + case ERROR: \ + fprintf(stderr, "HTTP_DECODER [ERROR] " format "\n", ##__VA_ARGS__); \ + fflush(stderr); \ + break; \ + } \ + } +#endif + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/version.map b/src/version.map new file mode 100644 index 0000000..d26b83a --- /dev/null +++ b/src/version.map @@ -0,0 +1,9 @@ +VERS_3.0{ +global: + extern "C" { + http_message_*; + http_decoder_init; + http_decoder_entry; + }; +local: *; +}; \ No newline at end of file diff --git a/test/.gitkeep b/test/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..6ee5c6f --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,149 @@ +set(DECODER_NAME http_decoder) + +add_library(${DECODER_NAME}_test SHARED http_decoder_gtest.cpp) +add_dependencies(${DECODER_NAME}_test ${DECODER_NAME}) +target_link_libraries(${DECODER_NAME}_test MESA_prof_load) +set_target_properties(${DECODER_NAME}_test PROPERTIES PREFIX "") + +set(TEST_RUN_DIR ${CMAKE_BINARY_DIR}/testing) + +include_directories(${CMAKE_SOURCE_DIR}/include) +include_directories(/usr/local/include/cjson) +include_directories(/opt/tsg/framework/include/stellar) +include_directories(/opt/MESA/include/MESA) + +add_executable(gtest_http_decoder http_decoder_driver.cpp http_decoder_stub.cpp http_decoder_gtest.cpp) +link_directories(${CMAKE_BINARY_DIR}/src) +target_link_libraries(gtest_http_decoder http_decoder gtest pcap MESA_jump_layer cjson-static) + +set(TEST_MAIN gtest_http_decoder) + +add_test(NAME CREATE_RUN_ENV COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}") +add_test(NAME COPY_TEST_MAIN COMMAND sh -c "cp ${CMAKE_BINARY_DIR}/test/${TEST_MAIN} ${TEST_RUN_DIR}/${TEST_MAIN}") +add_test(NAME COPY_HTTP_DECODER_CONF COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/etc/http && cp ${PROJECT_SOURCE_DIR}/conf/http_decoder.toml ${TEST_RUN_DIR}/etc/http/") + + +set_tests_properties(CREATE_RUN_ENV COPY_TEST_MAIN COPY_HTTP_DECODER_CONF + PROPERTIES FIXTURES_SETUP TestFixture) + +# run tests +add_test(NAME HTTP_GET_SINGLE_TRANS_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_get_single_trans.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_get_single_trans.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_GET_SINGLE_TRANS_MSS1_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_get_single_trans.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_get_single_trans.pcap -m 1 WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_GET_MULTI_TRANS_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_get_multi_trans.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_get_multi_trans.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_GET_LONG_COOKIE_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_get_long_cookie.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_get_long_cookie.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_GET_ENCODED_URI_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_get_encoded_uri.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_get_encoded_uri.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_RES_GZIP_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_res_gzip.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_res_gzip.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_CHUNKED_RES_GZIP_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_chunked_res_gzip.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_chunked_res_gzip.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_OVER_TCP_KEEPALIVE_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_over_tcp_keepalive.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_over_tcp_keepalive.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_TUNNEL_FOR_POP3_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_tunnel_for_pop3.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_tunnel_for_pop3.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_OVER_PPPOE_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_over_pppoe.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_over_pppoe.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_OVER_TLS_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_over_tls.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_over_tls.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME NON_HTTP_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/non_http.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/non_http.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_REQ_1BYTE_SLIDING_WINDOW_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_req_1byte_sliding_window.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_req_1byte_sliding_window.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_RES_1BYTE_SLIDING_WINDOW_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_res_1byte_sliding_window.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_res_1byte_sliding_window.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_NO_CONTENT_LENGTH_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_no_content_length.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_no_content_length.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_POST_MULTIPART_FORM_DATA_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_post_multipart_form_data.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_post_multipart_form_data.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_HEADERS_EXCEED_MAXIMUM_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_hdrs_exceed_maximum.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_hdrs_exceed_maximum.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +#add_test(NAME HTTP_CONNECT_FLOOD_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_connect_flood.json +# -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_connect_flood.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_GET_MALFORMED_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_get_malformed.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_get_malformed.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_HEADER_VALUE_EMPTY_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_hdr_value_empty.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_hdr_value_empty.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_UPGRADE_WEBSOCKET_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_upgrade_websocket.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_upgrade_websocket.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_UPGRADE_HTTP2_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_upgrade_http2.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_upgrade_http2.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_MULTI_PARSE_ERROR_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_multi_parse_error.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_multi_parse_error.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_GET_REQ_PIPELINE_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_get_req_pipeline.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_get_req_pipeline.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_TRANS_PIPELINE_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_trans_pipeline.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_trans_pipeline.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_HEADER_TRUNCATED_IN_KV_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_hdr_truncated_in_kv.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_hdr_truncated_in_kv.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_HEADER_TRUNCATED_AFTER_KV_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_hdr_truncated_after_kv.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_hdr_truncated_after_kv.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_URL_WITH_HOST_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_url_test_with_host.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_url_test_with_host.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_URL_WITHOUT_HOST_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_url_test_without_host.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_url_test_without_host.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +add_test(NAME HTTP_6OVER4_SINGLE_TRANS_TEST COMMAND ${TEST_MAIN} -b ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/http_6over4_single_trans.json + -p ${CMAKE_CURRENT_SOURCE_DIR}/http_pcap/http_6over4_single_trans.pcap WORKING_DIRECTORY ${TEST_RUN_DIR}) + +set_tests_properties(HTTP_GET_SINGLE_TRANS_TEST + HTTP_GET_SINGLE_TRANS_MSS1_TEST + HTTP_GET_MULTI_TRANS_TEST + HTTP_GET_LONG_COOKIE_TEST + HTTP_GET_ENCODED_URI_TEST + HTTP_RES_GZIP_TEST + HTTP_CHUNKED_RES_GZIP_TEST + HTTP_OVER_TCP_KEEPALIVE_TEST + HTTP_TUNNEL_FOR_POP3_TEST + HTTP_OVER_PPPOE_TEST + HTTP_OVER_TLS_TEST + NON_HTTP_TEST + HTTP_REQ_1BYTE_SLIDING_WINDOW_TEST + HTTP_RES_1BYTE_SLIDING_WINDOW_TEST + HTTP_NO_CONTENT_LENGTH_TEST + HTTP_POST_MULTIPART_FORM_DATA_TEST + HTTP_HEADERS_EXCEED_MAXIMUM_TEST + HTTP_GET_MALFORMED_TEST + HTTP_HEADER_VALUE_EMPTY_TEST + HTTP_MULTI_PARSE_ERROR_TEST + HTTP_UPGRADE_WEBSOCKET_TEST + HTTP_UPGRADE_HTTP2_TEST + HTTP_GET_REQ_PIPELINE_TEST + HTTP_TRANS_PIPELINE_TEST + HTTP_HEADER_TRUNCATED_IN_KV_TEST + HTTP_HEADER_TRUNCATED_AFTER_KV_TEST + HTTP_URL_WITH_HOST_TEST + HTTP_URL_WITHOUT_HOST_TEST + HTTP_6OVER4_SINGLE_TRANS_TEST + PROPERTIES FIXTURES_REQUIRED TestFixture) diff --git a/test/http_decoder_driver.cpp b/test/http_decoder_driver.cpp new file mode 100644 index 0000000..b1e24dc --- /dev/null +++ b/test/http_decoder_driver.cpp @@ -0,0 +1,923 @@ +/* + Http Decoder Google Test driver module +*/ +#include +#include +#ifndef __USE_MISC +#define __USE_MISC 1 +#endif +#ifndef __FAVOR_BSD +#define __FAVOR_BSD 1 +#endif +#ifndef __USE_BSD +#define __USE_BSD 1 +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "http_decoder_gtest.h" +#include "MESA_jump_layer.h" + +extern "C" int http_decoder_entry(struct session *sess, int events, + const struct packet *pkt, void *cb_arg); +extern "C" void http_decoder_test_exit(void *test_ctx); +extern "C" void *http_decoder_test_init(struct stellar *st); +extern "C" void *http_decoder_init(struct stellar *st); +struct fake_stellar *g_fake_stellar; // used for plugin commit_test_result_json() + +static const char *hdgt_cla_short_options = "hb:p:s:m:"; +static const struct option hdgt_cla_long_options[] = + { + {"help", no_argument, NULL, 'h'}, + {"benchmark-file", required_argument, NULL, 'b'}, + {"data-pcap-file", required_argument, NULL, 'p'}, + {"data-json-file", required_argument, NULL, 's'}, + {"mss", required_argument, NULL, 'm'}, + {NULL, 0, NULL, 0}}; + +static const char *g_data_src_json_non_headers[] = { + "__X_HTTP_TUPLE4", + "__X_HTTP_TRANSACTION", + "__X_HTTP_RESULT_INDEX", + "__X_HTTP_URL", + "method", + "uri", + "req_version", + "major_version", + "minor_version", + "res_version", + "res_status", + "status_code", + NULL}; + +static void hdgt_cmd_usage(int argc, char **argv) +{ + fprintf(stderr, "Usage:\n"); + fprintf(stderr, "\t -b set benchmark json file\n"); + fprintf(stderr, "\t -p set data source as pcap file\n"); + fprintf(stderr, "\t -s set data source as json file\n"); + fprintf(stderr, "\t -m set tcp Max Segment Size\n"); + exit(1); +} + +static int hdgt_parse_cmd_args(fake_stellar *fst, int argc, char **argv) +{ + int c, ret; + + fst->tcp_mss = 1460; // set default value + + while (1) + { + c = getopt_long(argc, argv, hdgt_cla_short_options, hdgt_cla_long_options, NULL); + if (c == -1) + { + ret = 0; + break; + } + + switch (c) + { + case 'h': + hdgt_cmd_usage(argc, argv); + break; + case 'b': + fst->benchmark_json_file_name = optarg; + break; + case 'p': + fst->data_source_type = DATA_SOURCE_PCAP; + fst->data_source_file_name = optarg; + break; + case 's': + fst->data_source_type = DATA_SOURCE_JSON; + fst->data_source_file_name = optarg; + break; + case 'm': + fst->tcp_mss = atoi(optarg); + if (fst->tcp_mss <= 0 || fst->tcp_mss > 65535) + { + DEBUG_PRINT("Invalid tcp mss value! must be [1, 65535]\n"); + hdgt_cmd_usage(argc, argv); + } + break; + default: + return -1; + break; + } + } + + // check args + if (!fst->benchmark_json_file_name) + { + DEBUG_PRINT("benchmark json file is not set!\n"); + hdgt_cmd_usage(argc, argv); + return -1; + } + + if (__DATA_SOURCE_NULL == fst->data_source_type) + { + DEBUG_PRINT("data source is not set!\n"); + hdgt_cmd_usage(argc, argv); + return -1; + } + + return ret; +} + +static int hdgt_compare_result(struct fake_stellar *fst) +{ + int final_result = 0; + cJSON_bool case_sensitive = FLASE; + + if (!fst->http_plug_test_result_root) + { + DEBUG_PRINT("Not found test json result!\n"); + return -1; + } + + if (!fst->load_benchmark_json_root) + { + DEBUG_PRINT("Not found benchmark json instance!\n"); + return -1; + } + + if (cJSON_GetArraySize(fst->load_benchmark_json_root) != cJSON_GetArraySize(fst->http_plug_test_result_root)) + { + DEBUG_PRINT("Compare json result: array size is diff!\n"); + final_result++; + } + + int ret = cJSON_Compare(fst->load_benchmark_json_root, fst->http_plug_test_result_root, case_sensitive); + if (ret != TRUE) + { + char *load_json_str = cJSON_Print(fst->load_benchmark_json_root); + printf("LOAD Raw:\n%s\n", load_json_str); + + char *result_json_str = cJSON_Print(fst->http_plug_test_result_root); + printf("TEST Raw:\n%s\n", result_json_str); + + int min_len = MIN(strlen(load_json_str), strlen(result_json_str)); + for (size_t i = 0; i < min_len; i++) + { + if (load_json_str[i] != result_json_str[i]) + { + printf("######### JSON Diff at len:%d: \n\tLOAD: %.*s\n\tTEST: %.*s\n", (int)i, 16, load_json_str + i, 16, result_json_str + i); + break; + } + } + + free(load_json_str); + free(result_json_str); + + cJSON *t_load = fst->load_benchmark_json_root->child; + cJSON *t_test = fst->http_plug_test_result_root->child; + while (t_load != NULL && t_test != NULL) + { + ret = cJSON_Compare(t_load, t_test, case_sensitive); + if (ret != TRUE) + { + load_json_str = cJSON_Print(t_load); + printf("LOAD Diff:\n%s\n", load_json_str); + free(load_json_str); + result_json_str = cJSON_Print(t_test); + printf("TEST Diff:\n%s\n", result_json_str); + free(result_json_str); + final_result++; + } + t_load = t_load->next; + t_test = t_test->next; + } + } + else + { + DEBUG_PRINT("Compare json result success!\n"); + } + + return final_result; +} + +static char *hdgt_get_file_content(const char *filename) +{ + FILE *fp = fopen(filename, "r"); + if (NULL == fp) + { + DEBUG_PRINT("fopen() fail!\n"); + return NULL; + } + fseek(fp, 0, SEEK_END); + long file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + char *file_content = MMALLOC(char, file_size + 1); + if (fread(file_content, 1, file_size, fp) != file_size) + { + DEBUG_PRINT("fread() %s fail!\n", filename); + fclose(fp); + MFREE(file_content); + return NULL; + } + fclose(fp); + return file_content; +} + +static struct fake_session *hdgt_session_new(struct fake_stellar *fst) +{ + struct fake_session *fses = MMALLOC(struct fake_session, sizeof(struct fake_session)); + + fses->fst = fst; + fses->tcp_mss = fst->tcp_mss; + // todo : get protocol type from packet, not fixed + fses->type = SESSION_TYPE_TCP; + return fses; +} + +static int hdgt_get_packet_from_pcap(struct fake_stellar *fst, struct fake_packet *fpkt) +{ + struct pcap_pkthdr *pkt_hdr; + const u_char *pkt_data; + int ret = pcap_next_ex(fst->pcap_ins, &pkt_hdr, &pkt_data); + if (1 == ret) + { + fpkt->raw_pkt_data = (char *)pkt_data; + fpkt->raw_pkt_data_len = pkt_hdr->caplen; + DEBUG_PRINT("Warning! this is a rough packet decoder, not support tcp out of order, seq overlap...\n"); + return 1; + } + else if (-2 == ret) + { + DEBUG_PRINT("pcap file over!\n"); + return 0; + } + DEBUG_PRINT("pcap_next_ex() fail: %s!\n", pcap_geterr(fst->pcap_ins)); + return -1; +} + +static int hdgt_get_packet_from_json(struct fake_stellar *fst, struct fake_packet *fpkt) +{ + cJSON *json_root = fst->data_src_json_para.json_root; + cJSON *json_item = cJSON_GetArrayItem(json_root, fst->data_src_json_para.current_json_array_idx); + if (NULL == json_item) + { + DEBUG_PRINT("Not found json object at index %d!\n", fst->data_src_json_para.current_json_array_idx); + return -1; + } + fst->data_src_json_para.current_object = json_item; + fst->data_src_json_para.current_json_array_idx++; + return 1; +} + +static int hdgt_is_reserverd_json_header(const cJSON *json_object) +{ + for (size_t i = 0; g_data_src_json_non_headers[i] != NULL; i++) + { + if (strlen(json_object->string) == strlen(g_data_src_json_non_headers[i]) && (0 == strncmp(json_object->string, g_data_src_json_non_headers[i], strlen(g_data_src_json_non_headers[i])))) + { + return 1; + } + } + + return 0; +} + +static int hdgt_get_packet_dir_from_json(cJSON *json) +{ + cJSON *json_dir = cJSON_GetObjectItem(json, GTEST_HTTP_TRANS_NAME); + if (NULL == json_dir) + { + return -1; + } + if (strncasecmp("request", json_dir->valuestring, strlen("request")) == 0) + { + return PACKET_DIRECTION_C2S; + } + else if (strncasecmp("response", json_dir->valuestring, strlen("response")) == 0) + { + return PACKET_DIRECTION_S2C; + } + else + { + return -1; + } +} + +static void hdgt_get_req_line_from_json(struct data_src_json_para_t *data_src_json_para) +{ + cJSON *json_item = data_src_json_para->current_object; + cJSON *method, *uri, *major_version, *minor_version; + + method = cJSON_GetObjectItem(json_item, "method"); + uri = cJSON_GetObjectItem(json_item, "uri"); + major_version = cJSON_GetObjectItem(json_item, "major_version"); + minor_version = cJSON_GetObjectItem(json_item, "minor_version"); + + if (method && uri && major_version && minor_version) + { + sprintf(data_src_json_para->key_value_buf, "%s %s HTTP/%d.%d\r\n", + method->valuestring, uri->valuestring, major_version->valueint, minor_version->valueint); + } + else + { + DEBUG_PRINT("get request line from json fail!\n"); + } +} + +static void hdgt_get_res_line_from_json(struct data_src_json_para_t *data_src_json_para) +{ + cJSON *json_item = data_src_json_para->current_object; + cJSON *res_status, *status_code, *major_version, *minor_version; + + res_status = cJSON_GetObjectItem(json_item, "res_status"); + status_code = cJSON_GetObjectItem(json_item, "status_code"); + major_version = cJSON_GetObjectItem(json_item, "major_version"); + minor_version = cJSON_GetObjectItem(json_item, "minor_version"); + + if (res_status && status_code && major_version && minor_version) + { + sprintf(data_src_json_para->key_value_buf, "HTTP/%d.%d %d %s\r\n", + major_version->valueint, minor_version->valueint, status_code->valueint, res_status->valuestring); + } + else + { + DEBUG_PRINT("get response line from json fail!\n"); + } +} + +static void hdgt_get_headers_from_json(struct data_src_json_para_t *data_src_json_para) +{ + cJSON *json_item = data_src_json_para->current_object->child; + char *data_ptr = data_src_json_para->key_value_buf + strlen(data_src_json_para->key_value_buf); + int len; + + while (json_item) + { + if (0 == hdgt_is_reserverd_json_header(json_item)) + { + if (cJSON_IsString(json_item)) + { + len = sprintf(data_ptr, "%s: %s\r\n", json_item->string, json_item->valuestring); + } + else if (cJSON_IsNumber(json_item)) + { + len = sprintf(data_ptr, "%s: %d\r\n", json_item->string, json_item->valueint); + } + else if (cJSON_IsBool(json_item)) + { + len = sprintf(data_ptr, "%s: %s\r\n", json_item->string, json_item->valueint ? "true" : "false"); + } + else + { + len = sprintf(data_ptr, "%s: %s\r\n", json_item->string, cJSON_Print(json_item)); + } + data_ptr += len; + } + json_item = json_item->next; + } + sprintf(data_ptr, "\r\n"); // headers EOF +} + +static int hdgt_update_packet_detail_by_json(struct fake_stellar *fst, struct fake_packet *fpkt) +{ + fpkt->dir = hdgt_get_packet_dir_from_json(fst->data_src_json_para.current_object); + if (PACKET_DIRECTION_C2S != fpkt->dir && PACKET_DIRECTION_S2C != fpkt->dir) + { + DEBUG_PRINT("get packet direction from json fail!\n"); + return -1; + } + + if (PACKET_DIRECTION_C2S == fpkt->dir) + { + hdgt_get_req_line_from_json(&fst->data_src_json_para); + } + else + { + hdgt_get_res_line_from_json(&fst->data_src_json_para); + } + hdgt_get_headers_from_json(&fst->data_src_json_para); + + fpkt->payload_data = fst->data_src_json_para.key_value_buf; + fpkt->payload_data_len = strlen(fst->data_src_json_para.key_value_buf); + fpkt->payload_submit_offset = 0; + + return 0; +} + +static int hdgt_get_packet(struct fake_stellar *fst, struct fake_packet *fpkt) +{ + int ret = -1; + memset(fpkt, 0, sizeof(struct fake_packet)); + if (DATA_SOURCE_PCAP == fst->data_source_type) + { + ret = hdgt_get_packet_from_pcap(fst, fpkt); + } + else if (DATA_SOURCE_JSON == fst->data_source_type) + { + ret = hdgt_get_packet_from_json(fst, fpkt); + } + else + { + DEBUG_PRINT("Invalid data source type!\n"); + ret = -1; + } + + return ret; +} + +static int hdgt_has_data_left(struct fake_packet *fpkt) +{ + return fpkt->payload_submit_offset < fpkt->payload_data_len; +} + +static int hdgt_get_packet_dir_of_tcp(const struct tcphdr *th) +{ + int dir; + + if (TH_SYN == th->th_flags) + { + dir = PACKET_DIRECTION_C2S; + } + else if ((TH_SYN | TH_ACK) == th->th_flags) + { + dir = PACKET_DIRECTION_S2C; + } + else + { + if (ntohs(th->th_sport) > ntohs(th->th_dport)) // Unwritten rule, not sure + { + dir = PACKET_DIRECTION_C2S; + } + else + { + dir = PACKET_DIRECTION_S2C; + } + } + + return dir; +} + +static void hdgt_determine_packet_dir(struct fake_session *fses, struct fake_packet *fpkt, const struct tcphdr *th) +{ + if (SESSION_ADDR_TYPE_IPV4_TCP == fses->addr_type) + { + if (th->th_sport == fses->addr->ipv4.sport) + { + fpkt->dir = PACKET_DIRECTION_C2S; + } + else + { + fpkt->dir = PACKET_DIRECTION_S2C; + } + } + else + { + if (th->th_sport == fses->addr->ipv6.sport) + { + fpkt->dir = PACKET_DIRECTION_C2S; + } + else + { + fpkt->dir = PACKET_DIRECTION_S2C; + } + } +} + +static void hdgt_update_packet_payload(struct fake_packet *fpkt, const struct tcphdr *th) +{ + // todo, support UDP? + fpkt->payload_data = (char *)th + th->th_off * 4; + fpkt->payload_data_len = fpkt->raw_pkt_data_len - ((char *)th - fpkt->raw_pkt_data) - (th->th_off * 4); + fpkt->payload_submit_offset = 0; +} + +static int hdgt_update_packet_detail_by_pcap(struct fake_session *fses, struct fake_packet *fpkt) +{ + struct tcphdr *th = (struct tcphdr *)MESA_jump_layer_greedy(fpkt->raw_pkt_data, ADDR_TYPE_MAC, ADDR_TYPE_TCP); + if (NULL == th) + { + DEBUG_PRINT("Not found tcp header!\n"); + return -1; + } + + hdgt_determine_packet_dir(fses, fpkt, th); + hdgt_update_packet_payload(fpkt, th); + return 0; +} + +static int hdgt_update_packet_detail(struct fake_session *fses, struct fake_packet *fpkt) +{ + int ret; + if (DATA_SOURCE_PCAP == fses->fst->data_source_type) + { + ret = hdgt_update_packet_detail_by_pcap(fses, fpkt); + } + else + { + ret = hdgt_update_packet_detail_by_json(fses->fst, fpkt); + } + return ret; +} + +static struct session_addr *hgdt_get_session_addr_by_pcap(struct fake_session *fses, struct fake_packet *fpkt) +{ + if (fses->addr != NULL) + { + return fses->addr; + } + + struct session_addr tmp_addr = {}; + int innermost_l3_layer_type; + + struct tcphdr *th = (struct tcphdr *)MESA_jump_layer_greedy(fpkt->raw_pkt_data, ADDR_TYPE_MAC, ADDR_TYPE_TCP); + if (NULL == th) + { + DEBUG_PRINT("Not found tcp header!\n"); + // todo, support UDP ? + return NULL; + } + fpkt->l4_header = (char *)th; + struct ip *i4h = (struct ip *)MESA_jump_layer_greedy(fpkt->raw_pkt_data, ADDR_TYPE_MAC, ADDR_TYPE_IPV4); + struct ip6_hdr *i6h = (struct ip6_hdr *)MESA_jump_layer_greedy(fpkt->raw_pkt_data, ADDR_TYPE_MAC, ADDR_TYPE_IPV6); + if ((NULL == i6h) && (NULL == i4h)) + { + DEBUG_PRINT("Not found ipv4 or ipv6 header!\n"); + return NULL; + } + + if (i4h && (NULL == i6h)) + { + innermost_l3_layer_type = ADDR_TYPE_IPV4; + } + else if (i6h && (NULL == i4h)) + { + innermost_l3_layer_type = ADDR_TYPE_IPV6; + } + else // both ipv4 and ipv6 exist, maybe 4over6, 6over4, gtp etc. + { + if ((char *)i4h - (char *)i6h > 0) + { + innermost_l3_layer_type = ADDR_TYPE_IPV4; + } + else + { + innermost_l3_layer_type = ADDR_TYPE_IPV6; + } + } + + int cur_pkt_dir = hdgt_get_packet_dir_of_tcp(th); + + if (ADDR_TYPE_IPV4 == innermost_l3_layer_type) + { + fses->addr_type = SESSION_ADDR_TYPE_IPV4_TCP; + if (PACKET_DIRECTION_C2S == cur_pkt_dir) + { + + tmp_addr.ipv4.saddr = i4h->ip_src.s_addr; + tmp_addr.ipv4.daddr = i4h->ip_dst.s_addr; + tmp_addr.ipv4.sport = th->th_sport; + tmp_addr.ipv4.dport = th->th_dport; + } + else + { + tmp_addr.ipv4.saddr = i4h->ip_dst.s_addr; + tmp_addr.ipv4.daddr = i4h->ip_src.s_addr; + tmp_addr.ipv4.sport = th->th_dport; + tmp_addr.ipv4.dport = th->th_sport; + } + } + else + { + fses->addr_type = SESSION_ADDR_TYPE_IPV6_TCP; + if (PACKET_DIRECTION_C2S == cur_pkt_dir) + { + memcpy(tmp_addr.ipv6.saddr, i6h->ip6_src.s6_addr, IPV6_ADDR_LEN); + memcpy(tmp_addr.ipv6.daddr, i6h->ip6_dst.s6_addr, IPV6_ADDR_LEN); + tmp_addr.ipv6.sport = th->th_sport; + tmp_addr.ipv6.dport = th->th_dport; + } + else + { + memcpy(tmp_addr.ipv6.saddr, i6h->ip6_dst.s6_addr, IPV6_ADDR_LEN); + memcpy(tmp_addr.ipv6.daddr, i6h->ip6_src.s6_addr, IPV6_ADDR_LEN); + tmp_addr.ipv6.sport = th->th_dport; + tmp_addr.ipv6.dport = th->th_sport; + } + } + + fses->addr = MMALLOC(struct session_addr, sizeof(struct session_addr)); + memcpy(fses->addr, &tmp_addr, sizeof(struct session_addr)); + + return fses->addr; +} + +/* example: "1.1.1.1.12345>2.2.2.2.80" */ +static void hdgt_json_session_adddr_pton_v4(struct session_addr *addr, const char *tuple4_cstr) +{ + const char *delim = ".>"; + char *dup_str = strdup(tuple4_cstr); + char *sip1 = strtok(dup_str, delim); + char *sip2 = strtok(NULL, delim); + char *sip3 = strtok(NULL, delim); + char *sip4 = strtok(NULL, delim); + char *sport = strtok(NULL, delim); + + char *dip1 = strtok(NULL, delim); + char *dip2 = strtok(NULL, delim); + char *dip3 = strtok(NULL, delim); + char *dip4 = strtok(NULL, delim); + char *dport = strtok(NULL, delim); + + char sip_str[INET_ADDRSTRLEN] = {}; + char dip_str[INET_ADDRSTRLEN] = {}; + sprintf(sip_str, "%s.%s.%s.%s", sip1, sip2, sip3, sip4); + inet_pton(AF_INET, sip_str, &addr->ipv4.saddr); + sprintf(dip_str, "%s.%s.%s.%s", dip1, dip2, dip3, dip4); + inet_pton(AF_INET, dip_str, &addr->ipv4.daddr); + addr->ipv4.sport = htons(atoi(sport)); + addr->ipv4.dport = htons(atoi(dport)); +} + +/* fe80::8c19:7aff:fef2:11e5.12345>fe80::8c19:7aff:fef2:11e5.80 */ +static void hdgt_json_session_adddr_pton_v6(struct session_addr *addr, const char *tuple4_cstr) +{ + char *dup_str = strdup(tuple4_cstr); + char *sip = dup_str; + char *ptr = strchr(dup_str, '.'); + *ptr = '\0'; + ptr++; + char *sport = ptr; + ptr = strchr(sport, '>'); + *ptr = '\0'; + ptr++; + char *dip = ptr; + ptr = strchr(dip, '.'); + *ptr = '\0'; + ptr++; + char *dport = ptr; + + char sip_str[INET6_ADDRSTRLEN] = {}; + char dip_str[INET6_ADDRSTRLEN] = {}; + inet_pton(AF_INET6, sip, addr->ipv6.saddr); + inet_pton(AF_INET6, dip, addr->ipv6.daddr); + addr->ipv6.sport = htons(atoi(sport)); + addr->ipv6.dport = htons(atoi(dport)); +} + +static void hdgt_json_session_adddr_pton(struct fake_session *fses, const char *tuple4_cstr) +{ + struct session_addr *addr = fses->addr; + if (strchr(tuple4_cstr, ':') != NULL) + { + hdgt_json_session_adddr_pton_v6(addr, tuple4_cstr); + fses->addr_type = SESSION_ADDR_TYPE_IPV6_TCP; + return; + } + else + { + hdgt_json_session_adddr_pton_v4(addr, tuple4_cstr); + fses->addr_type = SESSION_ADDR_TYPE_IPV4_TCP; + } +} + +static struct session_addr *hgdt_get_session_addr_by_json(struct fake_session *fses, struct fake_packet *fpkt) +{ + if (fses->addr != NULL) + { + return fses->addr; + } + + cJSON *tuple4_obj = cJSON_GetObjectItem(fses->fst->data_src_json_para.current_object, GTEST_HTTP_TUPLE4_NAME); + if (NULL == tuple4_obj) + { + return NULL; + } + fses->addr = MMALLOC(struct session_addr, sizeof(struct session_addr)); + hdgt_json_session_adddr_pton(fses, tuple4_obj->valuestring); + return fses->addr; +} + +static struct session_addr *hgdt_get_session_addr(struct fake_session *fses, struct fake_packet *fpkt) +{ + if (fses->fst->data_source_type == DATA_SOURCE_PCAP) + { + return hgdt_get_session_addr_by_pcap(fses, fpkt); + } + return hgdt_get_session_addr_by_json(fses, fpkt); +} + +static void hdgt_session_update(struct fake_stellar *fst, struct fake_session *fses, int event) +{ + fst->http_decoder_entry((struct session *)fses, event, (struct packet *)fses->fpkt, fst->http_decoder_ctx); +} + +static int hdgt_data_source_init(struct fake_stellar *fst) +{ + if (DATA_SOURCE_PCAP == fst->data_source_type) + { + char errbuf[PCAP_ERRBUF_SIZE]; + fst->pcap_ins = pcap_open_offline(fst->data_source_file_name, errbuf); + if (NULL == fst->pcap_ins) + { + fprintf(stderr, "pcap_open_offline() fail: %s\n", errbuf); + return -1; + } + } + else if (DATA_SOURCE_JSON == fst->data_source_type) + { + char *file_cont = hdgt_get_file_content(fst->data_source_file_name); + if (NULL == file_cont) + { + fprintf(stderr, "Open json file fail: %s\n", fst->data_source_file_name); + return -1; + } + fst->data_src_json_para.json_root = cJSON_Parse(file_cont); + if (NULL == fst->data_src_json_para.json_root) + { + fprintf(stderr, "cJSON_Parse() %s fail!\n", fst->data_source_file_name); + MFREE(file_cont); + return -1; + } + fst->data_src_json_para.json_array_size = cJSON_GetArraySize(fst->data_src_json_para.json_root); + fst->data_src_json_para.current_json_array_idx = 0; + MFREE(file_cont); + } + else + { + DEBUG_PRINT("Invalid data source type!\n"); + return -1; + } + + return 0; +} + +static int hdgt_benchmakr_json_parse(struct fake_stellar *fst) +{ + char *file_cont = hdgt_get_file_content(fst->benchmark_json_file_name); + if (NULL == file_cont) + { + fprintf(stderr, "Open json file fail: %s\n", fst->benchmark_json_file_name); + return -1; + } + fst->load_benchmark_json_root = cJSON_Parse(file_cont); + if (NULL == fst->load_benchmark_json_root) + { + fprintf(stderr, "cJSON_Parse() %s fail!\n", fst->benchmark_json_file_name); + MFREE(file_cont); + return -1; + } + fst->http_plug_test_result_root = cJSON_CreateArray(); + MFREE(file_cont); + return 0; +} + +static int hdgt_under_test_module_init(struct fake_stellar *fst) +{ + fst->http_decoder_ctx = http_decoder_init((struct stellar *)fst); + fst->http_decoder_entry = http_decoder_entry; + + return 0; +} + +static int hdgt_test_plug_init(struct fake_stellar *fst) +{ + fst->http_http_plug_ctx = http_decoder_test_init((struct stellar *)fst); + return 0; +} + +static struct fake_stellar *hdgt_init(int argc, char **argv) +{ + struct fake_stellar *fst = MMALLOC(struct fake_stellar, sizeof(struct fake_stellar)); + + if (hdgt_parse_cmd_args(fst, argc, argv) < 0) + { + fprintf(stderr, "hdgt_parse_cmd_args() fail!\n"); + goto fail_exit; + } + if (hdgt_data_source_init(fst) < 0) + { + fprintf(stderr, "hdgt_data_source_init() fail!\n"); + goto fail_exit; + } + if (hdgt_benchmakr_json_parse(fst) < 0) + { + fprintf(stderr, "hdgt_benchmakr_json_parse() fail!\n"); + goto fail_exit; + } + if (hdgt_under_test_module_init(fst) < 0) + { + fprintf(stderr, "hdgt_under_test_module_init() fail!\n"); + goto fail_exit; + } + if (hdgt_test_plug_init(fst) < 0) + { + fprintf(stderr, "hdgt_test_plug_init() fail!\n"); + goto fail_exit; + } + + g_fake_stellar = fst; + return fst; + +fail_exit: + MFREE(fst); + return NULL; +} + +static void hdgt_exit(struct fake_stellar *fst) +{ + cJSON_free(fst->load_benchmark_json_root); + cJSON_free(fst->http_plug_test_result_root); + if (fst->data_src_json_para.json_root) + { + cJSON_free(fst->data_src_json_para.json_root); + } + if (fst->pcap_ins) + { + pcap_close(fst->pcap_ins); + } + + MFREE(fst); + return; +} + +static void hdgt_session_free(struct fake_session *fses) +{ + const struct fake_exdata_manage *ex_mgr = fses->fst->fake_exdata_mgr; + + for (int i = 0; i < EX_DATA_MAX_SIZE; i++) + { + if (fses->plug_exdata_array[i] != NULL) + { + ex_mgr[i].free_func((struct session *)fses, i, fses->plug_exdata_array[i], ex_mgr[i].arg); + } + } + + if (fses->readable_addr_cstr) + { + MFREE(fses->readable_addr_cstr); + } + + if (fses->addr) + { + MFREE(fses->addr); + } + + MFREE(fses); +} + +static void hdgt_main_loop(struct fake_stellar *fst) +{ + struct fake_packet __null_pkt = {}; + struct fake_packet fpkt = {}; + struct fake_session *fses = hdgt_session_new(fst); + + fses->fpkt = &__null_pkt; + hdgt_session_update(fst, fses, SESS_EV_OPENING); + + fses->fpkt = &fpkt; + while (hdgt_get_packet(fst, &fpkt) == 1) + { + if (NULL == hgdt_get_session_addr(fses, &fpkt)) + { + continue; // not tcp, not ip + } + if (hdgt_update_packet_detail(fses, &fpkt) < 0) + { + continue; + } + while (hdgt_has_data_left(&fpkt)) + { + hdgt_session_update(fst, fses, SESS_EV_PACKET); + } + } + + fses->fpkt = &__null_pkt; + hdgt_session_update(fst, fses, SESS_EV_CLOSING); + + hdgt_session_free(fses); +} + +TEST(HTTP_DECODER, GTEST) +{ + ASSERT_EQ(0, hdgt_compare_result(g_fake_stellar)); +} + +int main(int argc, char **argv) +{ + struct fake_stellar *fake_st = hdgt_init(argc, argv); + if (NULL == fake_st) + { + fprintf(stderr, "hdgt_init() fail!\n"); + exit(1); + } + + hdgt_main_loop(fake_st); + + ::testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + + hdgt_exit(fake_st); + + return ret; +} diff --git a/test/http_decoder_gtest.cpp b/test/http_decoder_gtest.cpp new file mode 100644 index 0000000..aa81df2 --- /dev/null +++ b/test/http_decoder_gtest.cpp @@ -0,0 +1,370 @@ +/* +********************************************************************************************** +* File: http_decoder_gtest.cpp +* Description: +* Authors: Liu WenTan +* Date: 2023-12-15 +* Copyright: (c) Since 2023 Geedge Networks, Ltd. All rights reserved. +*********************************************************************************************** +*/ + +#include +#include +#include +#include +#include + +#include "../include/http_decoder.h" + +#ifdef __cplusplus +extern "C" +{ + +#include "cJSON.h" +#include "http_decoder_gtest.h" +#include "stellar/utils.h" +#include "stellar/stellar.h" +#include "stellar/session_exdata.h" +#include "stellar/session_mq.h" + + int commit_test_result_json(cJSON *node, const char *name); +} +#endif + +#define MAX_KEY_STR_LEN 2048 + +enum http_transaction_type +{ + HTTP_TRANSACTION_REQ = 0, + HTTP_TRANSACTION_RES, + HTTP_TRANSACTION_SESSION, + HTTP_TRANSACTION_MAX +}; + +struct gtest_plug_exdata_t +{ + cJSON *result_jnode[HTTP_TRANSACTION_MAX]; +}; + +static int g_result_count = 0; +static int g_header_count = 1; +static int g_exdata_idx = 0; +static int g_topic_id = 0; + +#if 1 +void output_http_req_line(struct http_request_line *req_line) +{ + char tmp_str[MAX_KEY_STR_LEN] = {0}; + memcpy(tmp_str, req_line->method.str, req_line->method.str_len); + printf("req_method:%s\n", tmp_str); + + memset(tmp_str, 0, sizeof(tmp_str)); + memcpy(tmp_str, req_line->uri.str, req_line->uri.str_len); + printf("req_uri:%s\n", tmp_str); + + memset(tmp_str, 0, sizeof(tmp_str)); + memcpy(tmp_str, req_line->version.str, req_line->version.str_len); + printf("req_version:%s\n", tmp_str); +} + +void output_http_res_line(struct http_response_line *res_line) +{ + char tmp_str[MAX_KEY_STR_LEN] = {0}; + memcpy(tmp_str, res_line->version.str, res_line->version.str_len); + printf("res_version:%s\n", tmp_str); + + memset(tmp_str, 0, sizeof(tmp_str)); + memcpy(tmp_str, res_line->status.str, res_line->status.str_len); + printf("res_status:%s\n", tmp_str); +} + +void output_http_header(struct http_header *header) +{ + char tmp_key[MAX_KEY_STR_LEN] = {0}; + char tmp_val[MAX_KEY_STR_LEN] = {0}; + + memcpy(tmp_key, header->key.str, header->key.str_len); + memcpy(tmp_val, header->val.str, header->val.str_len); + printf("<%s:%s>\n", tmp_key, tmp_val); +} +#endif + +void output_http_body(struct hstring *body, int decompress_flag) +{ + int counter = 0; + + if (1 == decompress_flag) + { + printf("\n\n----------------decompress body len:%zu---------------\n", + body->str_len); + } + else + { + printf("\n\n----------------raw body len:%zu---------------\n", + body->str_len); + } + + for (size_t i = 0; i < body->str_len; i++) + { + if (counter % 16 == 0) + { + printf("\n"); + } + printf("%02x ", (unsigned char)body->str[i]); + counter++; + } + printf("\n"); +} + +int http_field_to_json(cJSON *object, const char *key, char *val, size_t val_len) +{ + if (NULL == object || NULL == key || NULL == val || 0 == val_len) + { + return -1; + } + + char *tmp = CALLOC(char, val_len + 1); + memcpy(tmp, val, val_len); + cJSON_AddStringToObject(object, key, tmp); + FREE(tmp); + + return 0; +} + +void req_line_to_json(cJSON *ctx, struct http_request_line *req_line) +{ + http_field_to_json(ctx, "method", req_line->method.str, + req_line->method.str_len); + http_field_to_json(ctx, "uri", req_line->uri.str, req_line->uri.str_len); + http_field_to_json(ctx, "req_version", req_line->version.str, + req_line->version.str_len); + + cJSON_AddNumberToObject(ctx, "major_version", req_line->major_version); + cJSON_AddNumberToObject(ctx, "minor_version", req_line->minor_version); +} + +void res_line_to_json(cJSON *ctx, struct http_response_line *res_line) +{ + http_field_to_json(ctx, "res_version", res_line->version.str, + res_line->version.str_len); + http_field_to_json(ctx, "res_status", res_line->status.str, + res_line->status.str_len); + + cJSON_AddNumberToObject(ctx, "major_version", res_line->major_version); + cJSON_AddNumberToObject(ctx, "minor_version", res_line->minor_version); + cJSON_AddNumberToObject(ctx, "status_code", res_line->status_code); +} + +void http_header_to_json(cJSON *ctx, struct http_header *header) +{ + char key[MAX_KEY_STR_LEN] = {0}; + + memcpy(key, header->key.str, header->key.str_len); + + if (cJSON_HasObjectItem(ctx, key) == FALSE) + { + http_field_to_json(ctx, key, header->val.str, header->val.str_len); + } + else + { + // ctx already has the key, so rename key by key%d + char new_key[MAX_KEY_STR_LEN] = {0}; + sprintf(new_key, "%s%d", key, g_header_count++); + http_field_to_json(ctx, new_key, header->val.str, header->val.str_len); + } +} + +void http_url_add_to_json(cJSON *ctx, struct http_message *msg) +{ + struct hstring url_result = {}; + + if (cJSON_GetObjectItem(ctx, GTEST_HTTP_URL_NAME)) + { + return; + } + + if (http_message_get_url(msg, &url_result) < 0) + { + // printf("url:%s\n", url_result.str); + return; + } + + struct http_header url_header_result = {}; + + url_header_result.key.str = (char *)GTEST_HTTP_URL_NAME; + url_header_result.key.str_len = strlen(GTEST_HTTP_URL_NAME); + url_header_result.val = url_result; + + http_header_to_json(ctx, &url_header_result); +} + +// Full duplex +static void commit_last_half_flow_data(struct session *sess, struct gtest_plug_exdata_t *gtest_plug_exdata, enum http_transaction_type type) +{ + char result_name[MAX_KEY_STR_LEN] = {0}; + + cJSON *last_jnode = gtest_plug_exdata->result_jnode[type]; + if (last_jnode) + { + sprintf(result_name, "%d", g_result_count); + commit_test_result_json(last_jnode, result_name); + gtest_plug_exdata->result_jnode[type] = NULL; + g_result_count++; + } + + gtest_plug_exdata->result_jnode[type] = cJSON_CreateObject(); + if (HTTP_TRANSACTION_REQ == type) + { + cJSON_AddStringToObject(gtest_plug_exdata->result_jnode[type], GTEST_HTTP_TRANS_NAME, "request"); + } + else if (HTTP_TRANSACTION_RES == type) + { + cJSON_AddStringToObject(gtest_plug_exdata->result_jnode[type], GTEST_HTTP_TRANS_NAME, "response"); + } +} + +static void http_decoder_test_update_session_tuple4(struct session *sess, struct gtest_plug_exdata_t *gtest_plug_exdata) +{ + if (gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_SESSION] == NULL) + { + char result_name[MAX_KEY_STR_LEN] = {0}; + gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_SESSION] = cJSON_CreateObject(); + cJSON_AddStringToObject(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_SESSION], GTEST_HTTP_TUPLE4_NAME, session_get0_readable_addr(sess)); + sprintf(result_name, "%d", g_result_count++); + commit_test_result_json(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_SESSION], result_name); + } +} + +extern "C" int +http_decoder_test_entry(struct session *sess, int topic_id, const void *data, + void *cb_arg) +{ + struct http_request_line req_line = {0}; + struct http_response_line res_line = {0}; + struct http_header header = {0}; + struct hstring body = {0}; + struct http_message *msg = (struct http_message *)data; + enum http_message_type msg_type = http_message_type(msg); + + struct gtest_plug_exdata_t *gtest_plug_exdata = (struct gtest_plug_exdata_t *)session_get_ex_data(sess, g_exdata_idx); + if (NULL == gtest_plug_exdata) + { + gtest_plug_exdata = (struct gtest_plug_exdata_t *)calloc(1, sizeof(struct gtest_plug_exdata_t)); + session_set_ex_data(sess, g_exdata_idx, gtest_plug_exdata); + } + + if (msg_type == HTTP_MESSAGE_REQ_LINE || msg_type == HTTP_MESSAGE_REQ_HEADER || msg_type == HTTP_MESSAGE_REQ_BODY) + { + cJSON *json = gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_REQ]; + } + else + { + cJSON *json = gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_RES]; + } + + http_decoder_test_update_session_tuple4(sess, gtest_plug_exdata); + + switch (msg_type) + { + case HTTP_MESSAGE_REQ_LINE: + commit_last_half_flow_data(sess, gtest_plug_exdata, HTTP_TRANSACTION_REQ); + http_message_get_request_line(msg, &req_line); + req_line_to_json(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_REQ], &req_line); + break; + case HTTP_MESSAGE_REQ_HEADER: + while (http_message_request_header_next(msg, &header) > 0) + { + http_header_to_json(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_REQ], &header); + } + g_header_count = 1; + http_url_add_to_json(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_REQ], msg); + break; + case HTTP_MESSAGE_REQ_BODY: + http_message_get_request_raw_body(msg, &body); + // output_http_body(&body, 0); + + http_message_get_request_decompress_body(msg, &body); + // output_http_body(&body, 1); + break; + case HTTP_MESSAGE_RES_LINE: + commit_last_half_flow_data(sess, gtest_plug_exdata, HTTP_TRANSACTION_RES); + http_message_get_response_line(msg, &res_line); + res_line_to_json(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_RES], &res_line); + break; + case HTTP_MESSAGE_RES_HEADER: + while (http_message_response_header_next(msg, &header) > 0) + { + http_header_to_json(gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_RES], &header); + } + g_header_count = 1; + break; + case HTTP_MESSAGE_RES_BODY: + http_message_get_response_raw_body(msg, &body); + // output_http_body(&body, 0); + + http_message_get_response_decompress_body(msg, &body); + // output_http_body(&body, 1); + break; + + // to do: check payload + default: + break; + } + + return 0; +} + +void http_decoder_test_exdata_free(struct session *sess, int idx, void *ex_ptr, + void *arg) +{ + if (ex_ptr != NULL) + { + struct gtest_plug_exdata_t *gtest_plug_exdata = (struct gtest_plug_exdata_t *)ex_ptr; + if (gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_REQ]) + { + commit_last_half_flow_data(sess, gtest_plug_exdata, HTTP_TRANSACTION_REQ); + } + if (gtest_plug_exdata->result_jnode[HTTP_TRANSACTION_RES]) + { + commit_last_half_flow_data(sess, gtest_plug_exdata, HTTP_TRANSACTION_RES); + } + free(ex_ptr); + } +} + +extern "C" void *http_decoder_test_init(struct stellar *st) +{ + g_exdata_idx = + stellar_session_get_ex_new_index(st, "HTTP_DECODER_REQ_TEST", + http_decoder_test_exdata_free, + NULL); + if (g_exdata_idx < 0) + { + printf("[%s:%d]: can't get http_decoder exdata index !!!\n", + __FUNCTION__, __LINE__); + exit(-1); + } + + g_topic_id = session_mq_get_topic_id(st, "HTTP_DECODER_MESSAGE"); + if (g_topic_id < 0) + { + printf("[%s:%d]: can't get http_decoder topic id !!!\n", + __FUNCTION__, __LINE__); + exit(-1); + } + + session_mq_subscribe_topic(st, g_topic_id, http_decoder_test_entry, NULL); + printf("http_decoder_test_init OK!\n"); + + return NULL; +} + +extern "C" void http_decoder_test_exit(void *test_ctx) +{ + if (test_ctx != NULL) + { + FREE(test_ctx); + } + + printf("http_decoder_test_exit OK!\n"); +} \ No newline at end of file diff --git a/test/http_decoder_gtest.h b/test/http_decoder_gtest.h new file mode 100644 index 0000000..46b0c43 --- /dev/null +++ b/test/http_decoder_gtest.h @@ -0,0 +1,114 @@ +#pragma once + +#include "http_decoder.h" +#include "session.h" +#include "md5.h" +#include +#include +#include +#include +#include "cJSON.h" + +#define TRUE 1 +#define FLASE 0 + +#define JSON_KEY_VALUE_STRING_MAX_LEN (4096) + +#define MAX(a, b) ((a) >= (b) ? (a) : (b)) +#define MIN(a, b) ((a) >= (b) ? (b) : (a)) + +#define MMALLOC(type, size) ((type *)calloc(1, size)) +#define MFREE(p) \ + do \ + { \ + free(p); \ + p = NULL; \ + } while (0) + +#if 0 +#define DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else +#define DEBUG_PRINT(fmt, ...) +#endif + +#define EX_DATA_MAX_SIZE 10 +#define PIPELINE_MAX_NUM 8 + +#define KEY_NAME_REQ_LINE "__REQ_LINE__" +#define KEY_NAME_RES_LINE "__RES_LINE__" +#define KEY_NAME_REQ_URL "__REQ_URL__" +#define KEY_NAME_PAYLOAD_MD5 "__PAYLOAD_MD5__" + +#define GTEST_FIX_PAYLOAD_CSTR "" +#define GTEST_FIX_PAYLOAD_MD5 "e91e072f772737c7a45013cc3b1a916c" + +#define GTEST_HTTP_URL_NAME "__X_HTTP_URL" +#define GTEST_HTTP_TRANS_NAME "__X_HTTP_TRANSACTION" +#define GTEST_HTTP_TUPLE4_NAME "__X_HTTP_TUPLE4" + +struct fake_exdata_manage +{ + char *name; + int name_len; + void *arg; + void (*free_func)(struct session *sess, int idx, void *ex_ptr, void *arg); +}; + +struct fake_packet +{ + int dir; + size_t raw_pkt_data_len; + const char *raw_pkt_data; // referred to packet Ethernet MAC header + const char *l4_header; // referred to TCP, UDP header + size_t payload_data_len; + const char *payload_data; // referred to L7 data, such as HTTP, DNS, etc. + size_t payload_submit_offset; +}; + +enum data_source_type_t +{ + __DATA_SOURCE_NULL = 0, + DATA_SOURCE_PCAP, + DATA_SOURCE_JSON, + __DATA_SOURCE_MAX +}; + +struct data_src_json_para_t +{ + cJSON *json_root; + cJSON *current_object; + int json_array_size; + int current_json_array_idx; + char key_value_buf[JSON_KEY_VALUE_STRING_MAX_LEN]; +}; + +struct fake_stellar +{ + struct fake_exdata_manage fake_exdata_mgr[EX_DATA_MAX_SIZE]; + int tcp_mss; + void *http_decoder_ctx; + void *http_http_plug_ctx; + int (*http_decoder_entry)(struct session *sess, int events, + const struct packet *pkt, void *cb_arg); + cJSON *http_plug_test_result_root; + enum data_source_type_t data_source_type; + const char *benchmark_json_file_name; + const char *data_source_file_name; + cJSON *load_benchmark_json_root; + pcap_t *pcap_ins; + + struct data_src_json_para_t data_src_json_para; +}; + +struct fake_session +{ + enum session_type type; + struct fake_stellar *fst; + struct fake_packet *fpkt; + int events; + int tcp_mss; + enum session_addr_type addr_type; + struct session_addr *addr; + char *readable_addr_cstr; + void *plug_exdata_array[EX_DATA_MAX_SIZE]; +}; diff --git a/test/http_decoder_stub.cpp b/test/http_decoder_stub.cpp new file mode 100644 index 0000000..c879630 --- /dev/null +++ b/test/http_decoder_stub.cpp @@ -0,0 +1,250 @@ +/* + Http Decoder Google Test stub module +*/ +#include +#include +#include +#include +#include +#include "http_decoder.h" + +#ifdef __cplusplus +extern "C" +{ +#endif +#include "session.h" +#include "session_exdata.h" +#include "session_mq.h" +#include "stellar.h" +#include "http_decoder_gtest.h" +#include "MESA_jump_layer.h" + + static int g_topic_id = -1; + static msg_free_cb_func *g_msg_free_cb = NULL; + static void *g_msg_free_cb_arg = NULL; + + extern struct fake_stellar *g_fake_stellar; + + int commit_test_result_json(cJSON *node, const char *name) + { + assert(node != NULL || name != NULL); + if (g_fake_stellar->http_plug_test_result_root) + { + cJSON_AddStringToObject(node, "__X_HTTP_RESULT_INDEX", name); + cJSON_AddItemToArray(g_fake_stellar->http_plug_test_result_root, node); + return 0; + } + return -1; + } + + int packet_get_direction(const struct packet *pkt) + { + struct fake_packet *fpkt = (struct fake_packet *)pkt; + return fpkt->dir; + } + + int session_event_assign(struct session_event *ev, struct stellar *st, struct session *sess, int events, session_event_cb_func *cb, void *cb_arg) + { + DEBUG_PRINT("todo: fake session_event_assign()\n"); + return 0; + } + + const char *session_get0_readable_addr(struct session *sess) + { + struct fake_session *fses = (struct fake_session *)sess; + + if (fses->readable_addr_cstr) + { + return fses->readable_addr_cstr; + } + + char ip_src_buf[INET6_ADDRSTRLEN] = {}; + char ip_dst_buf[INET6_ADDRSTRLEN] = {}; + char port_src_buf[16] = {}; + char port_dst_buf[16] = {}; + char tuple4_buf[256] = {}; + + if (SESSION_ADDR_TYPE_IPV4_TCP == fses->addr_type) + { + inet_ntop(AF_INET, &fses->addr->ipv4.saddr, ip_src_buf, INET_ADDRSTRLEN); + inet_ntop(AF_INET, &fses->addr->ipv4.daddr, ip_dst_buf, INET_ADDRSTRLEN); + sprintf(port_src_buf, "%u", ntohs(fses->addr->ipv4.sport)); + sprintf(port_dst_buf, "%u", ntohs(fses->addr->ipv4.dport)); + } + else + { + inet_ntop(AF_INET6, fses->addr->ipv6.saddr, ip_src_buf, INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, fses->addr->ipv6.daddr, ip_dst_buf, INET6_ADDRSTRLEN); + sprintf(port_src_buf, "%u", ntohs(fses->addr->ipv6.sport)); + sprintf(port_dst_buf, "%u", ntohs(fses->addr->ipv6.dport)); + } + + snprintf(tuple4_buf, sizeof(tuple4_buf), "%s.%s>%s.%s", ip_src_buf, port_src_buf, ip_dst_buf, port_dst_buf); + fses->readable_addr_cstr = MMALLOC(char, strlen(tuple4_buf) + 1); + memcpy(fses->readable_addr_cstr, tuple4_buf, strlen(tuple4_buf)); + + return fses->readable_addr_cstr; + } + + struct session_addr *session_get0_addr(struct session *sess, enum session_addr_type *addr_type) + { + struct fake_session *fses = (struct fake_session *)sess; + *addr_type = fses->addr_type; + return fses->addr; + } + + static int __find_ex_data(struct fake_stellar *fst, const char *name) + { + int find_name_len = strlen(name); + + for (int i = 0; i < EX_DATA_MAX_SIZE; i++) + { + if ((fst->fake_exdata_mgr[i].name != NULL) && (strncasecmp(name, fst->fake_exdata_mgr[i].name, find_name_len) == 0) && (find_name_len == fst->fake_exdata_mgr[i].name_len)) + { + return i; + } + } + + return -1; + } + + static int __save_ex_data(struct fake_stellar *fst, const char *name, session_ex_free *free_func, void *arg) + { + for (int i = 0; i < EX_DATA_MAX_SIZE; i++) + { + if (fst->fake_exdata_mgr[i].name == NULL && fst->fake_exdata_mgr[i].name_len == 0) + { + fst->fake_exdata_mgr[i].name = MMALLOC(char, strlen(name) + 1); + fst->fake_exdata_mgr[i].name_len = strlen(name) + 1; + memcpy(fst->fake_exdata_mgr[i].name, name, strlen(name)); + fst->fake_exdata_mgr[i].free_func = free_func; + fst->fake_exdata_mgr[i].arg = arg; + return i; + } + } + + return -1; + } + + int stellar_session_get_ex_new_index(struct stellar *st, const char *name, session_ex_free *free_func, void *arg) + { + int ex_id = __find_ex_data((struct fake_stellar *)st, name); + if (-1 == ex_id) + { + ex_id = __save_ex_data((struct fake_stellar *)st, name, free_func, arg); + } + + return ex_id; + } + + int session_mq_get_topic_id(struct stellar *st, const char *topic_name) + { + return g_topic_id; + } + + int session_get_current_thread_id(struct session *sess) + { + return 0; + } + + int session_mq_destroy_topic(struct stellar *st, int topic_id) + { + return 0; + } + + int session_set_ex_data(struct session *sess, int idx, void *ex_ptr) + { + struct fake_session *fses = (struct fake_session *)sess; + fses->plug_exdata_array[idx] = ex_ptr; + + return 0; + } + + void *session_get_ex_data(struct session *sess, int idx) + { + struct fake_session *fses = (struct fake_session *)sess; + return fses->plug_exdata_array[idx]; + } + + void hdd_session_free_exdata(struct fake_session *fake_ses) + { + for (int i = 0; i < EX_DATA_MAX_SIZE; i++) + { + if (fake_ses->plug_exdata_array[i] != NULL) + { + fake_ses->fst->fake_exdata_mgr[i].free_func((struct session *)fake_ses, i, fake_ses->plug_exdata_array[i], NULL); + } + } + } + + int stellar_plugin_register(struct stellar *st, int events, session_event_cb_func *cb, void *cb_arg) + { + return 0; // fix plugin id + } + + extern int http_decoder_test_entry(struct session *sess, int topic_id, const void *data, void *cb_arg); + int session_mq_publish_message(struct session *sess, int topic_id, void *data) + { + http_decoder_test_entry(sess, topic_id, data, NULL); + + g_msg_free_cb(data, g_msg_free_cb_arg); + return 0; + } + + int stellar_get_worker_thread_num(struct stellar *st) + { + return 1; + } + + int session_mq_create_topic(struct stellar *st, const char *topic_name, msg_free_cb_func *free_cb, void *cb_arg) + { + g_msg_free_cb = free_cb; + g_msg_free_cb_arg = cb_arg; + + g_topic_id = 0; // KISS, use fix value + return g_topic_id; + } + + const char *session_get0_current_payload(struct session *sess, size_t *payload_len) + { + struct fake_session *fses = (struct fake_session *)sess; + struct fake_packet *fpkt = fses->fpkt; + const char *payload_ptr = NULL; + + if (!fpkt || !fpkt->payload_data || fpkt->payload_data_len == 0) + { + return NULL; + } + + int submit_len = MIN(fses->tcp_mss, fpkt->payload_data_len - fpkt->payload_submit_offset); + if(submit_len <= 0) + { + *payload_len = 0; + return NULL; + } + payload_ptr = fpkt->payload_data + fpkt->payload_submit_offset; + *payload_len = submit_len; + fpkt->payload_submit_offset += submit_len; + + return payload_ptr; + } + + struct session_event *session_get_intrinsic_event(struct session *sess, int plugin_id) + { + return NULL; + } + + int session_is_inner_most(struct session *sess, uint64_t *flag) + { + return 1; // no tunnel + } + + int session_mq_subscribe_topic(struct stellar *st, int topic_id, on_msg_cb_func *sub_cb, void *cb_arg) + { + // to do + return 0; + } + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/test/http_pcap/http_6over4_single_trans.pcap b/test/http_pcap/http_6over4_single_trans.pcap new file mode 100644 index 0000000..6b074e5 Binary files /dev/null and b/test/http_pcap/http_6over4_single_trans.pcap differ diff --git a/test/http_pcap/http_chunked_res_gzip.pcap b/test/http_pcap/http_chunked_res_gzip.pcap new file mode 100644 index 0000000..fa237fb Binary files /dev/null and b/test/http_pcap/http_chunked_res_gzip.pcap differ diff --git a/test/http_pcap/http_connect_flood.pcap b/test/http_pcap/http_connect_flood.pcap new file mode 100644 index 0000000..11b7e14 Binary files /dev/null and b/test/http_pcap/http_connect_flood.pcap differ diff --git a/test/http_pcap/http_get_encoded_uri.pcap b/test/http_pcap/http_get_encoded_uri.pcap new file mode 100644 index 0000000..633b50b Binary files /dev/null and b/test/http_pcap/http_get_encoded_uri.pcap differ diff --git a/test/http_pcap/http_get_long_cookie.pcap b/test/http_pcap/http_get_long_cookie.pcap new file mode 100644 index 0000000..8857615 Binary files /dev/null and b/test/http_pcap/http_get_long_cookie.pcap differ diff --git a/test/http_pcap/http_get_malformed.pcap b/test/http_pcap/http_get_malformed.pcap new file mode 100644 index 0000000..08deaeb Binary files /dev/null and b/test/http_pcap/http_get_malformed.pcap differ diff --git a/test/http_pcap/http_get_multi_trans.pcap b/test/http_pcap/http_get_multi_trans.pcap new file mode 100644 index 0000000..9c0d2e7 Binary files /dev/null and b/test/http_pcap/http_get_multi_trans.pcap differ diff --git a/test/http_pcap/http_get_req_pipeline.pcap b/test/http_pcap/http_get_req_pipeline.pcap new file mode 100644 index 0000000..f805493 Binary files /dev/null and b/test/http_pcap/http_get_req_pipeline.pcap differ diff --git a/test/http_pcap/http_get_single_trans.pcap b/test/http_pcap/http_get_single_trans.pcap new file mode 100644 index 0000000..a4b6bea Binary files /dev/null and b/test/http_pcap/http_get_single_trans.pcap differ diff --git a/test/http_pcap/http_hdr_truncated_after_kv.pcap b/test/http_pcap/http_hdr_truncated_after_kv.pcap new file mode 100644 index 0000000..de9f018 Binary files /dev/null and b/test/http_pcap/http_hdr_truncated_after_kv.pcap differ diff --git a/test/http_pcap/http_hdr_truncated_in_kv.pcap b/test/http_pcap/http_hdr_truncated_in_kv.pcap new file mode 100644 index 0000000..b4015c9 Binary files /dev/null and b/test/http_pcap/http_hdr_truncated_in_kv.pcap differ diff --git a/test/http_pcap/http_hdr_value_empty.pcap b/test/http_pcap/http_hdr_value_empty.pcap new file mode 100644 index 0000000..bb22243 Binary files /dev/null and b/test/http_pcap/http_hdr_value_empty.pcap differ diff --git a/test/http_pcap/http_hdrs_exceed_maximum.pcap b/test/http_pcap/http_hdrs_exceed_maximum.pcap new file mode 100644 index 0000000..537bffa Binary files /dev/null and b/test/http_pcap/http_hdrs_exceed_maximum.pcap differ diff --git a/test/http_pcap/http_multi_parse_error.pcap b/test/http_pcap/http_multi_parse_error.pcap new file mode 100644 index 0000000..b57e275 Binary files /dev/null and b/test/http_pcap/http_multi_parse_error.pcap differ diff --git a/test/http_pcap/http_no_content_length.pcap b/test/http_pcap/http_no_content_length.pcap new file mode 100644 index 0000000..28e6881 Binary files /dev/null and b/test/http_pcap/http_no_content_length.pcap differ diff --git a/test/http_pcap/http_over_pppoe.pcap b/test/http_pcap/http_over_pppoe.pcap new file mode 100644 index 0000000..a6587cb Binary files /dev/null and b/test/http_pcap/http_over_pppoe.pcap differ diff --git a/test/http_pcap/http_over_tcp_keepalive.pcap b/test/http_pcap/http_over_tcp_keepalive.pcap new file mode 100644 index 0000000..5b2db18 Binary files /dev/null and b/test/http_pcap/http_over_tcp_keepalive.pcap differ diff --git a/test/http_pcap/http_over_tls.pcap b/test/http_pcap/http_over_tls.pcap new file mode 100644 index 0000000..71c557e Binary files /dev/null and b/test/http_pcap/http_over_tls.pcap differ diff --git a/test/http_pcap/http_post_multipart_form_data.pcap b/test/http_pcap/http_post_multipart_form_data.pcap new file mode 100644 index 0000000..052d326 Binary files /dev/null and b/test/http_pcap/http_post_multipart_form_data.pcap differ diff --git a/test/http_pcap/http_req_1byte_sliding_window.pcap b/test/http_pcap/http_req_1byte_sliding_window.pcap new file mode 100644 index 0000000..632c676 Binary files /dev/null and b/test/http_pcap/http_req_1byte_sliding_window.pcap differ diff --git a/test/http_pcap/http_res_1byte_sliding_window.pcap b/test/http_pcap/http_res_1byte_sliding_window.pcap new file mode 100644 index 0000000..6d1d6a4 Binary files /dev/null and b/test/http_pcap/http_res_1byte_sliding_window.pcap differ diff --git a/test/http_pcap/http_res_gzip.pcap b/test/http_pcap/http_res_gzip.pcap new file mode 100644 index 0000000..04b9998 Binary files /dev/null and b/test/http_pcap/http_res_gzip.pcap differ diff --git a/test/http_pcap/http_trans_pipeline.pcap b/test/http_pcap/http_trans_pipeline.pcap new file mode 100644 index 0000000..43438e1 Binary files /dev/null and b/test/http_pcap/http_trans_pipeline.pcap differ diff --git a/test/http_pcap/http_tunnel_for_pop3.pcap b/test/http_pcap/http_tunnel_for_pop3.pcap new file mode 100644 index 0000000..6df9669 Binary files /dev/null and b/test/http_pcap/http_tunnel_for_pop3.pcap differ diff --git a/test/http_pcap/http_upgrade_http2.pcap b/test/http_pcap/http_upgrade_http2.pcap new file mode 100644 index 0000000..a77847a Binary files /dev/null and b/test/http_pcap/http_upgrade_http2.pcap differ diff --git a/test/http_pcap/http_upgrade_websocket.pcap b/test/http_pcap/http_upgrade_websocket.pcap new file mode 100644 index 0000000..b823d7e Binary files /dev/null and b/test/http_pcap/http_upgrade_websocket.pcap differ diff --git a/test/http_pcap/http_url_test_with_host.pcap b/test/http_pcap/http_url_test_with_host.pcap new file mode 100644 index 0000000..8a57c4a Binary files /dev/null and b/test/http_pcap/http_url_test_with_host.pcap differ diff --git a/test/http_pcap/http_url_test_without_host.pcap b/test/http_pcap/http_url_test_without_host.pcap new file mode 100644 index 0000000..0036379 Binary files /dev/null and b/test/http_pcap/http_url_test_without_host.pcap differ diff --git a/test/http_pcap/non_http.pcap b/test/http_pcap/non_http.pcap new file mode 100644 index 0000000..931b43b Binary files /dev/null and b/test/http_pcap/non_http.pcap differ diff --git a/test/md5.c b/test/md5.c new file mode 100644 index 0000000..13a5d16 --- /dev/null +++ b/test/md5.c @@ -0,0 +1,356 @@ +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + rights reserved. + + License to copy and use this software is granted provided that it + is identified as the "RSA Data Security, Inc. MD5 Message-Digest + Algorithm" in all material mentioning or referencing this software + or this function. + + License is also granted to make and use derivative works provided + that such works are identified as "derived from the RSA Data + Security, Inc. MD5 Message-Digest Algorithm" in all material + mentioning or referencing the derived work. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. */ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include "md5.h" + + /* Constants for MD5Transform routine. */ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + + static void MD5Transform PROTO_LIST((UINT4[4], unsigned char[64])); + static void Encode PROTO_LIST((unsigned char *, UINT4 *, unsigned int)); + static void Decode PROTO_LIST((UINT4 *, unsigned char *, unsigned int)); + static void MD5_memcpy PROTO_LIST((POINTER, POINTER, unsigned int)); + static void MD5_memset PROTO_LIST((POINTER, int, unsigned int)); + + static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +/* F, G, H and I are basic MD5 functions. */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. */ +#define FF(a, b, c, d, x, s, ac) \ + { \ + (a) += F((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + { \ + (a) += G((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + { \ + (a) += H((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + { \ + (a) += I((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT((a), (s)); \ + (a) += (b); \ + } + + /* MD5 initialization. Begins an MD5 operation, writing a new context. */ + void MD5Init(MD5_CTX *context) + { + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants.*/ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; + } + + /* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. */ + void MD5Update( + MD5_CTX *context, /* context */ + unsigned char *input, /* input block */ + unsigned int inputLen) /* length of input block */ + { + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible.*/ + if (inputLen >= partLen) + { + MD5_memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen); + MD5Transform(context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform(context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy((POINTER)&context->buffer[index], (POINTER)&input[i], + inputLen - i); + } + + /* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. */ + void MD5Final( + unsigned char digest[16], /* message digest */ + MD5_CTX *context) /* context */ + { + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode(bits, context->count, 8); + + /* Pad out to 56 mod 64.*/ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update(context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update(context, bits, 8); + + /* Store state in digest */ + Encode(digest, context->state, 16); + + /* Zeroize sensitive information.*/ + MD5_memset((POINTER)context, 0, sizeof(*context)); + } + + /* MD5 basic transformation. Transforms state based on block. */ + static void MD5Transform( + UINT4 state[4], + unsigned char block[64]) + { + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode(x, block, 64); + + /* Round 1 */ + FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */ + FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */ + FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */ + FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */ + FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */ + FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */ + FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */ + FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */ + FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */ + FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */ + FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */ + GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */ + GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */ + GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */ + GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */ + GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */ + GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */ + + GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */ + GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */ + GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */ + GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */ + HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */ + HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */ + HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */ + HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */ + HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */ + HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */ + HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */ + HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */ + HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */ + II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */ + II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */ + II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */ + II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */ + II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */ + II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */ + II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */ + II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */ + II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. */ + MD5_memset((POINTER)x, 0, sizeof(x)); + } + + /* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. */ + static void Encode( + unsigned char *output, + const UINT4 *input, + unsigned int len) + { + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + { + output[j] = (unsigned char)(input[i] & 0xff); + output[j + 1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j + 2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j + 3] = (unsigned char)((input[i] >> 24) & 0xff); + } + } + + /* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. */ + static void Decode( + UINT4 *output, + unsigned char *input, + unsigned int len) + { + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j + 1]) << 8) | + (((UINT4)input[j + 2]) << 16) | (((UINT4)input[j + 3]) << 24); + } + + /* Note: Replace "for loop" with standard memcpy if possible. */ + + static void MD5_memcpy( + POINTER output, + const POINTER input, + unsigned int len) + { + unsigned int i; + + for (i = 0; i < len; i++) + + output[i] = input[i]; + } + + /* Note: Replace "for loop" with standard memset if possible. */ + static void MD5_memset( + POINTER output, + int value, + unsigned int len) + { + unsigned int i; + + for (i = 0; i < len; i++) + ((char *)output)[i] = (char)value; + } + + char *MESA_MD5_sum_str(unsigned char *raw_data, unsigned int raw_data_len, char result[33]) + { + int i; + MD5_CTX context; + unsigned char digest[16]; + + MD5Init(&context); + MD5Update(&context, raw_data, raw_data_len); + MD5Final(digest, &context); + + for (i = 0; i < 16; i++) + sprintf(result + 2 * i, "%02x", digest[i]); + result[32] = 0; + + return result; + } + + char *MESA_MD5_sum_bin(unsigned char *raw_data, unsigned int raw_data_len, char result[16]) + { + MD5_CTX context; + + MD5Init(&context); + MD5Update(&context, raw_data, raw_data_len); + MD5Final(result, &context); + + return result; + } + +#ifdef __cplusplus +} +#endif diff --git a/test/md5.h b/test/md5.h new file mode 100644 index 0000000..9f5b5d2 --- /dev/null +++ b/test/md5.h @@ -0,0 +1,70 @@ +/* MD5.H - header file for MD5C.C */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + rights reserved. + + License to copy and use this software is granted provided that it + is identified as the "RSA Data Security, Inc. MD5 Message-Digest + Algorithm" in all material mentioning or referencing this software + or this function. + + License is also granted to make and use derivative works provided + that such works are identified as "derived from the RSA Data + Security, Inc. MD5 Message-Digest Algorithm" in all material + mentioning or referencing the derived work. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. */ +#ifndef __MD5_H +#define __MD5_H + +#ifndef PROTOTYPES +#define PROTOTYPES 0 +#endif + +/* POINTER defines a generic pointer type */ +typedef unsigned char *POINTER; + +/* UINT2 defines a two byte word */ +typedef unsigned short int UINT2; + +/* UINT4 defines a four byte word */ +//typedef unsigned long int UINT4; +typedef unsigned int UINT4; + +/* PROTO_LIST is defined depending on how PROTOTYPES is defined above. + If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it + returns an empty list. */ + +#if PROTOTYPES +#define PROTO_LIST(list) list +#else +#define PROTO_LIST(list) () +#endif + +/* MD5 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +void MD5Init PROTO_LIST ((MD5_CTX *)); +void MD5Update PROTO_LIST ((MD5_CTX *, unsigned char *, unsigned int)); +void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *)); + +#ifdef __cplusplus +extern "C" +{ +char *MESA_MD5_sum_str(unsigned char *raw_data, unsigned int raw_data_len, char result[33]); +char *MESA_MD5_sum_bin(unsigned char *raw_data, unsigned int raw_data_len, char result[16]); +} +#endif + + +#endif diff --git a/test/test_env/conflist.inf b/test/test_env/conflist.inf new file mode 100644 index 0000000..2e8144d --- /dev/null +++ b/test/test_env/conflist.inf @@ -0,0 +1,9 @@ +[platform] +./plug/stellar_on_sapp/start_loader.inf + + +[protocol] + + +[business] +#./plug/stellar_on_sapp/defer_loader.inf diff --git a/test/test_env/sapp4.el8.x86_64.rpm b/test/test_env/sapp4.el8.x86_64.rpm new file mode 100644 index 0000000..e43fe2f Binary files /dev/null and b/test/test_env/sapp4.el8.x86_64.rpm differ diff --git a/test/test_env/spec.toml b/test/test_env/spec.toml new file mode 100644 index 0000000..b8c7eb9 --- /dev/null +++ b/test/test_env/spec.toml @@ -0,0 +1,11 @@ +# stellar_plugin.toml +# +[[plugin]] +path = "./stellar_plugin/http_decoder.so" +init = "http_decoder_init" +exit = "http_decoder_exit" + +[[plugin]] +path = "./stellar_plugin/http_decoder_test.so" +init = "http_decoder_test_init" +exit = "http_decoder_test_exit" \ No newline at end of file diff --git a/test/test_env/start_loader.inf b/test/test_env/start_loader.inf new file mode 100644 index 0000000..89b2f94 --- /dev/null +++ b/test/test_env/start_loader.inf @@ -0,0 +1,17 @@ +[PLUGINFO] +PLUGNAME=stellar_start_loader +SO_PATH=./plug/stellar_on_sapp/stellar_on_sapp.so +INIT_FUNC=STELLAR_START_LOADER_INIT +DESTROY_FUNC=STELLAR_START_LOADER_EXIT + +#[TCP_ALL] +#FUNC_FLAG=ALL +#FUNC_NAME=stellar_on_sapp_tcpall_entry + +[TCP] +FUNC_FLAG=ALL +FUNC_NAME=stellar_on_sapp_tcp_entry + +[UDP] +FUNC_FLAG=ALL +FUNC_NAME=stellar_on_sapp_udp_entry \ No newline at end of file diff --git a/test/test_env/tsg_l7_protocol.conf b/test/test_env/tsg_l7_protocol.conf new file mode 100644 index 0000000..1075a8f --- /dev/null +++ b/test/test_env/tsg_l7_protocol.conf @@ -0,0 +1,57 @@ +#TYPE:1:UCHAR,2:USHORT,3:USTRING,4:ULOG,5:USTRING,6:FILE,7:UBASE64,8:PACKET +#TYPE FIELD VALUE +STRING UNCATEGORIZED 8000 +#STRING UNCATEGORIZED 8001 +#STRING UNKNOWN_OTHER 8002 +STRING DNS 32 +STRING FTP 45 +STRING FTPS 751 +STRING HTTP 67 +STRING HTTPS 68 +STRING ICMP 70 +STRING IKE 8003 +STRING MAIL 8004 +STRING IMAP 75 +STRING IMAPS 76 +STRING IPSEC 85 +STRING XMPP 94 +STRING L2TP 98 +STRING NTP 137 +STRING POP3 147 +STRING POP3S 148 +STRING PPTP 153 +STRING QUIC 2521 +STRING SIP 182 +STRING SMB 185 +STRING SMTP 186 +STRING SMTPS 187 +STRING SPDY 1469 +STRING SSH 198 +STRING SSL 199 +STRING SOCKS 8005 +STRING TELNET 209 +STRING DHCP 29 +STRING RADIUS 158 +STRING OPENVPN 336 +STRING STUN 201 +STRING TEREDO 555 +STRING DTLS 1291 +STRING DoH 8006 +STRING ISAKMP 92 +STRING MDNS 3835 +STRING NETBIOS 129 +STRING NETFLOW 130 +STRING RDP 150 +STRING RTCP 174 +STRING RTP 175 +STRING SLP 8007 +STRING SNMP 190 +STRING SSDP 197 +STRING TFTP 211 +STRING BJNP 2481 +STRING LDAP 100 +STRING RTMP 337 +STRING RTSP 176 +STRING ESNI 8008 +STRING QQ 156 +STRING WeChat 1296 diff --git a/test/test_result_json/http_6over4_single_trans.json b/test/test_result_json/http_6over4_single_trans.json new file mode 100644 index 0000000..4f6244a --- /dev/null +++ b/test/test_result_json/http_6over4_single_trans.json @@ -0,0 +1,33 @@ +[ + { + "__X_HTTP_TUPLE4": "2001:da8:200:900e:200:5efe:d24d:58a3.52556>2600:140e:6::1702:1058.80", + "__X_HTTP_RESULT_INDEX": "0" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/ncsi.txt", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Connection": "Close", + "User-Agent": "Microsoft NCSI", + "Host": "www.msftncsi.com", + "__X_HTTP_URL": "www.msftncsi.com/ncsi.txt", + "__X_HTTP_RESULT_INDEX": "1" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Content-Length": "14", + "Date": "Tue, 01 Dec 2015 03:41:27 GMT", + "Connection": "close", + "Content-Type": "text/plain", + "Cache-Control": "max-age=30, must-revalidate", + "__X_HTTP_RESULT_INDEX": "2" + } +] \ No newline at end of file diff --git a/test/test_result_json/http_chunked_res_gzip.json b/test/test_result_json/http_chunked_res_gzip.json new file mode 100644 index 0000000..4da91fd --- /dev/null +++ b/test/test_result_json/http_chunked_res_gzip.json @@ -0,0 +1,41 @@ +[{ + "__X_HTTP_TUPLE4": "127.0.0.1.33412>127.0.0.1.8080", + "__X_HTTP_RESULT_INDEX": "0" + }, { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "www.wireshark.org:8080", + "User-Agent": "curl/7.46.0", + "Accept": "*/*", + "Connection": "close", + "Accept-Encoding": "chunked, gzip", + "__X_HTTP_URL": "www.wireshark.org:8080/", + "__X_HTTP_RESULT_INDEX": "1" + }, { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Server": "cloudflare-nginx", + "Date": "Wed, 06 Jan 2016 20:42:10 GMT", + "Content-Type": "text/html", + "Transfer-Encoding": "chunked", + "Connection": "close", + "Set-Cookie": "__cfduid=d8d37b52eaa3137bdfd7fd67a4ffc8a7a1452112929; expires=Thu, 05-Jan-17 20:42:09 GMT; path=/; domain=.wireshark.org; HttpOnly", + "X-Frame-Options": "SAMEORIGIN", + "Strict-Transport-Security": "max-age=31536000;", + "X-Slogan": "It's a great product with a great story to tell. I'm pumped!", + "X-Mod-Pagespeed": "1.9.32.11-7550", + "Vary": "Accept-Encoding", + "Cache-control": "max-age=0, no-cache, no-store", + "X-Slogan1": "Go deep.", + "CF-RAY": "260a3f709d7b0761-AMS", + "Content-Encoding": "gzip", + "__X_HTTP_RESULT_INDEX": "2" + }] diff --git a/test/test_result_json/http_connect_flood.json b/test/test_result_json/http_connect_flood.json new file mode 100644 index 0000000..199c038 --- /dev/null +++ b/test/test_result_json/http_connect_flood.json @@ -0,0 +1,686 @@ +[ + { + "Tuple4": "10.128.0.2.18762>10.0.0.2.80", + "method": "CONNECT", + "uri": "test.mazebolt.com:80", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.mazebolt.com", + "User-Agent": "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.16) Gecko/20110929 Iceweasel/3.5.16", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "0", + "name": "HTTP_DECODER_RESULT_1" + }, + { + "Tuple4": "10.128.0.2.18746>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:38 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_2" + }, + { + "Tuple4": "10.128.0.2.18744>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:38 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_3" + }, + { + "Tuple4": "10.128.0.2.18748>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:38 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_4" + }, + { + "Tuple4": "10.128.0.2.18750>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:38 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_5" + }, + { + "Tuple4": "10.128.0.2.18752>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:38 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_6" + }, + { + "Tuple4": "10.128.0.2.18754>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:38 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_7" + }, + { + "Tuple4": "10.128.0.2.18756>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:38 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_8" + }, + { + "Tuple4": "10.128.0.2.18758>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:38 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_9" + }, + { + "Tuple4": "10.128.0.2.18760>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:38 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_10" + }, + { + "Tuple4": "10.128.0.2.18762>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:38 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_11" + }, + { + "Tuple4": "10.128.0.2.18768>10.0.0.2.80", + "method": "CONNECT", + "uri": "test.mazebolt.com:80", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.mazebolt.com", + "User-Agent": "Safari/5.00 (Macintosh; U; en)", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "0", + "name": "HTTP_DECODER_RESULT_12" + }, + { + "Tuple4": "10.128.0.2.18766>10.0.0.2.80", + "method": "CONNECT", + "uri": "test.mazebolt.com:80", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.mazebolt.com", + "User-Agent": "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "0", + "name": "HTTP_DECODER_RESULT_13" + }, + { + "Tuple4": "10.128.0.2.18770>10.0.0.2.80", + "method": "CONNECT", + "uri": "test.mazebolt.com:80", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.mazebolt.com", + "User-Agent": "Opera/9.00 (Windows NT 5.1; U; en)", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "0", + "name": "HTTP_DECODER_RESULT_14" + }, + { + "Tuple4": "10.128.0.2.18772>10.0.0.2.80", + "method": "CONNECT", + "uri": "test.mazebolt.com:80", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.mazebolt.com", + "User-Agent": "Opera/9.00 (Windows NT 5.1; U; en)", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "0", + "name": "HTTP_DECODER_RESULT_15" + }, + { + "Tuple4": "10.128.0.2.18776>10.0.0.2.80", + "method": "CONNECT", + "uri": "test.mazebolt.com:80", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.mazebolt.com", + "User-Agent": "GooglePocket/2.1 ( http://www.googlePocket.com/Pocket.html)", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "0", + "name": "HTTP_DECODER_RESULT_16" + }, + { + "Tuple4": "10.128.0.2.18774>10.0.0.2.80", + "method": "CONNECT", + "uri": "test.mazebolt.com:80", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.mazebolt.com", + "User-Agent": "Mozilla/5.0 (X11; Linux i686; rv:31.0) Gecko/20100101 Firefox/31.0 Iceweasel/31.4.0", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "0", + "name": "HTTP_DECODER_RESULT_17" + }, + { + "Tuple4": "10.128.0.2.18780>10.0.0.2.80", + "method": "CONNECT", + "uri": "test.mazebolt.com:80", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.mazebolt.com", + "User-Agent": "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4b) Gecko/20030505 Mozilla Firebird/0.6", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "0", + "name": "HTTP_DECODER_RESULT_18" + }, + { + "Tuple4": "10.128.0.2.18778>10.0.0.2.80", + "method": "CONNECT", + "uri": "test.mazebolt.com:80", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.mazebolt.com", + "User-Agent": "DoCoMo/2.0 SH902i (compatible; Y!J-SRD/1.0; http://help.yahoo.co.jp/help/jp/search/indexing/indexing-27.html)", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "0", + "name": "HTTP_DECODER_RESULT_19" + }, + { + "Tuple4": "10.128.0.2.18782>10.0.0.2.80", + "method": "CONNECT", + "uri": "test.mazebolt.com:80", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.mazebolt.com", + "User-Agent": "IE/5.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 1.1.4322;)", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "0", + "name": "HTTP_DECODER_RESULT_20" + }, + { + "Tuple4": "10.128.0.2.18784>10.0.0.2.80", + "method": "CONNECT", + "uri": "test.mazebolt.com:80", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.mazebolt.com", + "User-Agent": "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.16) Gecko/20110929 Iceweasel/3.5.16", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "0", + "name": "HTTP_DECODER_RESULT_21" + }, + { + "Tuple4": "10.128.0.2.18768>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:39 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_22" + }, + { + "Tuple4": "10.128.0.2.18766>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:39 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_23" + }, + { + "Tuple4": "10.128.0.2.18770>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:39 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_24" + }, + { + "Tuple4": "10.128.0.2.18772>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:39 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_25" + }, + { + "Tuple4": "10.128.0.2.18776>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:39 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_26" + }, + { + "Tuple4": "10.128.0.2.18780>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:39 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_27" + }, + { + "Tuple4": "10.128.0.2.18774>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:39 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_28" + }, + { + "Tuple4": "10.128.0.2.18778>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:39 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_29" + }, + { + "Tuple4": "10.128.0.2.18782>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:39 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_30" + }, + { + "Tuple4": "10.128.0.2.18784>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:39 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_31" + }, + { + "Tuple4": "10.128.0.2.18790>10.0.0.2.80", + "method": "CONNECT", + "uri": "test.mazebolt.com:80", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.mazebolt.com", + "User-Agent": "Safari/5.00 (Macintosh; U; en)", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "0", + "name": "HTTP_DECODER_RESULT_32" + }, + { + "Tuple4": "10.128.0.2.18792>10.0.0.2.80", + "method": "CONNECT", + "uri": "test.mazebolt.com:80", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.mazebolt.com", + "User-Agent": "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "0", + "name": "HTTP_DECODER_RESULT_33" + }, + { + "Tuple4": "10.128.0.2.18796>10.0.0.2.80", + "method": "CONNECT", + "uri": "test.mazebolt.com:80", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.mazebolt.com", + "User-Agent": "Opera/9.00 (Windows NT 5.1; U; en)", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "0", + "name": "HTTP_DECODER_RESULT_34" + }, + { + "Tuple4": "10.128.0.2.18794>10.0.0.2.80", + "method": "CONNECT", + "uri": "test.mazebolt.com:80", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.mazebolt.com", + "User-Agent": "Opera/9.00 (Windows NT 5.1; U; en)", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "0", + "name": "HTTP_DECODER_RESULT_35" + }, + { + "Tuple4": "10.128.0.2.18798>10.0.0.2.80", + "method": "CONNECT", + "uri": "test.mazebolt.com:80", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.mazebolt.com", + "User-Agent": "GooglePocket/2.1 ( http://www.googlePocket.com/Pocket.html)", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "0", + "name": "HTTP_DECODER_RESULT_36" + }, + { + "Tuple4": "10.128.0.2.18800>10.0.0.2.80", + "method": "CONNECT", + "uri": "test.mazebolt.com:80", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.mazebolt.com", + "User-Agent": "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4b) Gecko/20030505 Mozilla Firebird/0.6", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "0", + "name": "HTTP_DECODER_RESULT_37" + }, + { + "Tuple4": "10.128.0.2.18804>10.0.0.2.80", + "method": "CONNECT", + "uri": "test.mazebolt.com:80", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.mazebolt.com", + "User-Agent": "DoCoMo/2.0 SH902i (compatible; Y!J-SRD/1.0; http://help.yahoo.co.jp/help/jp/search/indexing/indexing-27.html)", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "0", + "name": "HTTP_DECODER_RESULT_38" + }, + { + "Tuple4": "10.128.0.2.18802>10.0.0.2.80", + "method": "CONNECT", + "uri": "test.mazebolt.com:80", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.mazebolt.com", + "User-Agent": "Mozilla/5.0 (X11; Linux i686; rv:31.0) Gecko/20100101 Firefox/31.0 Iceweasel/31.4.0", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "0", + "name": "HTTP_DECODER_RESULT_39" + }, + { + "Tuple4": "10.128.0.2.18806>10.0.0.2.80", + "method": "CONNECT", + "uri": "test.mazebolt.com:80", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.mazebolt.com", + "User-Agent": "IE/5.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 1.1.4322;)", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "0", + "name": "HTTP_DECODER_RESULT_40" + }, + { + "Tuple4": "10.128.0.2.18808>10.0.0.2.80", + "method": "CONNECT", + "uri": "test.mazebolt.com:80", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.mazebolt.com", + "User-Agent": "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.16) Gecko/20110929 Iceweasel/3.5.16", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Content-Length": "0", + "name": "HTTP_DECODER_RESULT_41" + }, + { + "Tuple4": "10.128.0.2.18790>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:41 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_42" + }, + { + "Tuple4": "10.128.0.2.18792>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:41 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_43" + }, + { + "Tuple4": "10.128.0.2.18796>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:41 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_44" + }, + { + "Tuple4": "10.128.0.2.18798>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:41 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_45" + }, + { + "Tuple4": "10.128.0.2.18794>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:41 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_46" + }, + { + "Tuple4": "10.128.0.2.18800>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:41 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_47" + }, + { + "Tuple4": "10.128.0.2.18804>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:41 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_48" + }, + { + "Tuple4": "10.128.0.2.18802>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:41 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_49" + }, + { + "Tuple4": "10.128.0.2.18806>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:41 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_50" + }, + { + "Tuple4": "10.128.0.2.18808>10.0.0.2.80", + "res_version": "1.1", + "res_status": "Forbidden", + "major_version": 1, + "minor_version": 1, + "status_code": 403, + "Date": "Thu, 03 Sep 2020 10:26:41 GMT", + "Server": "Apache", + "Content-Length": "199", + "Content-Type": "text/html; charset=iso-8859-1", + "name": "HTTP_DECODER_RESULT_51" + } +] \ No newline at end of file diff --git a/test/test_result_json/http_get_encoded_uri.json b/test/test_result_json/http_get_encoded_uri.json new file mode 100644 index 0000000..189bf88 --- /dev/null +++ b/test/test_result_json/http_get_encoded_uri.json @@ -0,0 +1,71 @@ +[{ + "__X_HTTP_TUPLE4": "192.168.117.60.39655>58.16.70.122.80", + "__X_HTTP_RESULT_INDEX": "0" + }, { + "__X_HTTP_TRANSACTION": "request", + "method": "POST", + "uri": "/disAll/tcCertType.html", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "User-Agent": "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36", + "Accept": "*/*", + "Accept-Language": "en-US,en;q=0.8,en-us,en;q=0.5", + "Origin": "http://58.16.70.122", + "X-Requested-With": "XMLHttpRequest", + "Referer": "http://58.16.70.122/register.jsp?redirect:http://58.16.70.122.r87.com/?", + "Cache-Control": "no-cache", + "X-Scanner": "Netsparker", + "Cookie": "JSESSIONID=385C79E211D561C0CA13D90F150F603D", + "Host": "58.16.70.122", + "Content-Length": "0", + "Accept-Encoding": "gzip, deflate", + "__X_HTTP_URL": "58.16.70.122/disAll/tcCertType.html", + "__X_HTTP_RESULT_INDEX": "1" + }, { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Server": "Apache-Coyote/1.1", + "Pragma": "No-cache", + "Expires": "Thu, 01 Jan 1970 00:00:00 GMT", + "Content-Type": "text/html;charset=UTF-8", + "Transfer-Encoding": "chunked", + "Date": "Sat, 18 May 2019 01:36:57 GMT", + "__X_HTTP_RESULT_INDEX": "2" + }, { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/upload/%E6%B3%95%E5%BE%8B%E6%B3%95%E8%A7%84/%E5%B8%82%E4%BA%BA%E6%B0%91%E6%94%BF%E5%BA%9C%E5%8A%9E%E5%85%AC%E5%8E%85%E5%8D%B0%E5%8F%91%E8%B4%B5%E9%98%B3%E5%B8%82%E5%85%B3%E4%BA%8E%E6%8E%A8%E8%BF%9B%E5%B7%A5%E5%95%86%E8%90%A5%E4%B8%9A%E6%89%A7%E7%85%A7%E3%80%81%E7%BB%84%E7%BB%87%E6%9C%BA%E6%9E%84%E4%BB%A3%E7%A0%81%E8%AF%81%E5%92%8C%E7%A8%8E%E5%8A%A1%E7%99%BB%E8%AE%B0%E8%AF%81%E2%80%9C%E4%B8%89%E8%AF%81%E5%90%88%E4%B8%80%E2%80%9D%E7%99%BB%E8%AE%B0%E5%88%B6%E5%BA%A6%E6%94%B9%E9%9D%A9%E5%AE%9E%E6%96%BD%E6%96%B9%E6%A1%88%E7%9A%84%E9%80%9A%E7%9F%A5%EF%BC%88%E7%AD%91%E5%BA%9C%E5%8A%9E%E5%87%BD%E3%80%902015%E3%80%91162%E5%8F%B7%EF%BC%89.docx?nsextt=N3TSP4RKE2", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", + "User-Agent": "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36", + "Cache-Control": "no-cache", + "Accept-Language": "en-us,en;q=0.5", + "X-Scanner": "Netsparker", + "Cookie": "JSESSIONID=385C79E211D561C0CA13D90F150F603D", + "Host": "58.16.70.122", + "Accept-Encoding": "gzip, deflate", + "__X_HTTP_URL": "58.16.70.122/upload/%E6%B3%95%E5%BE%8B%E6%B3%95%E8%A7%84/%E5%B8%82%E4%BA%BA%E6%B0%91%E6%94%BF%E5%BA%9C%E5%8A%9E%E5%85%AC%E5%8E%85%E5%8D%B0%E5%8F%91%E8%B4%B5%E9%98%B3%E5%B8%82%E5%85%B3%E4%BA%8E%E6%8E%A8%E8%BF%9B%E5%B7%A5%E5%95%86%E8%90%A5%E4%B8%9A%E6%89%A7%E7%85%A7%E3%80%81%E7%BB%84%E7%BB%87%E6%9C%BA%E6%9E%84%E4%BB%A3%E7%A0%81%E8%AF%81%E5%92%8C%E7%A8%8E%E5%8A%A1%E7%99%BB%E8%AE%B0%E8%AF%81%E2%80%9C%E4%B8%89%E8%AF%81%E5%90%88%E4%B8%80%E2%80%9D%E7%99%BB%E8%AE%B0%E5%88%B6%E5%BA%A6%E6%94%B9%E9%9D%A9%E5%AE%9E%E6%96%BD%E6%96%B9%E6%A1%88%E7%9A%84%E9%80%9A%E7%9F%A5%EF%BC%88%E7%AD%91%E5%BA%9C%E5%8A%9E%E5%87%BD%E3%80%902015%E3%80%91162%E5%8F%B7%EF%BC%89.docx?nsextt=N3TSP4RKE2", + "__X_HTTP_RESULT_INDEX": "3" + }, { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Server": "Apache-Coyote/1.1", + "Accept-Ranges": "bytes", + "ETag": "W/\"1703517-1546572172000\"", + "Last-Modified": "Fri, 04 Jan 2019 03:22:52 GMT", + "Content-Type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document;charset=UTF-8", + "Content-Length": "1703517", + "Date": "Sat, 18 May 2019 01:37:00 GMT", + "__X_HTTP_RESULT_INDEX": "4" + }] diff --git a/test/test_result_json/http_get_long_cookie.json b/test/test_result_json/http_get_long_cookie.json new file mode 100644 index 0000000..8c73779 --- /dev/null +++ b/test/test_result_json/http_get_long_cookie.json @@ -0,0 +1,24 @@ +[ + { + "__X_HTTP_TUPLE4": "202.127.156.91.27282>14.17.32.203.80", + "__X_HTTP_RESULT_INDEX": "0" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/livemsg?imagemd5=02f5efd8a349c50280f8540b2735bd54&tailroll=1&plugin=1.3.8&pf=out&si=3766845706&url=http%3A%2F%2Fsports.qq.com%2Fa%2F20160106%2F008987.htm&soid=CA7F9C5B0120568CDC2F68726300&chid=0&ping_data=dXNlcl9pbmZvPXVCWDluVDg5SFJhOUFQK0JQVGdKRUxVYi9Kdz0&t=0&iptype=0&vptag=&pid=7F993E38C0E676ACC07DE764D1F3DEF56AA8F90A&adtype=LD&oadid=6012&ev=3236&l=4020&ufc_filter=0&imagelog=1&pid2=7F993E38C0E676ACC07DE764D1F3DEF56AA8F90A&mt=15000&coverid=&reqtime=1452071981&requestl=4020&isthirdip=0&cid=0&isfloatindex=0&o=100654557&lcount=2&refluence=4020&from=0&vid=m01794rm5ej&cip=202.127.156.91&aver=0&ip_filter=0&adlength=30000&tagid=&v=TencentPlayerOutV3.2.19.346&live=0&dura=105", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "livep.l.qq.com", + "Connection": "keep-alive", + "User-Agent": "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36", + "Accept": "*/*", + "Referer": "http://imgcache.qq.com/tencentvideo_v1/player/TPout.swf?max_age=86400&v=20140714", + "Accept-Encoding": "gzip,deflate,sdch", + "Accept-Language": "zh-CN,zh;q=0.8", + "__X_HTTP_URL": "livep.l.qq.com/livemsg?imagemd5=02f5efd8a349c50280f8540b2735bd54&tailroll=1&plugin=1.3.8&pf=out&si=3766845706&url=http%3A%2F%2Fsports.qq.com%2Fa%2F20160106%2F008987.htm&soid=CA7F9C5B0120568CDC2F68726300&chid=0&ping_data=dXNlcl9pbmZvPXVCWDluVDg5SFJhOUFQK0JQVGdKRUxVYi9Kdz0&t=0&iptype=0&vptag=&pid=7F993E38C0E676ACC07DE764D1F3DEF56AA8F90A&adtype=LD&oadid=6012&ev=3236&l=4020&ufc_filter=0&imagelog=1&pid2=7F993E38C0E676ACC07DE764D1F3DEF56AA8F90A&mt=15000&coverid=&reqtime=1452071981&requestl=4020&isthirdip=0&cid=0&isfloatindex=0&o=100654557&lcount=2&refluence=4020&from=0&vid=m01794rm5ej&cip=202.127.156.91&aver=0&ip_filter=0&adlength=30000&tagid=&v=TencentPlayerOutV3.2.19.346&live=0&dura=105", + "Cookie": "flashuser=95621BA8CB862E09; piao_city=179; lv_irt_id=3628e1bbe25a6c941da9fac02ec2df8b; cm_cookie=V1,10017&-EP5mRruXhQarsCl5LD-2YzgjVTvyr2K&AQEBh7uoLMUB9lnaB5Tz9XdYnGIWflXmsDrU&150723&150723,10035&7t-tEmfJ076VAsM9&AQEBh7uoLMUB9lnc4tpW7vbazqdrRdBYOUCi&150724&150807,110054&ucO0Z0gctNn3&AQEBh7uoLMUB9llxMNl45F3RAIsKK0iMOJAG&150716&151008,10040&ACZ1r0A70NaEFcGT&AQEBh7uoLMUB9lmVgSoTwuuXZi896zSVsXIF&150818&151014,110015&1&AQEBh7uoLMUB9lkt2LUHO6ARwODHLI_Y51rj&150928&151103,10037&1433388364186289251984&AQEBh7uoLMUB9llIBencOqTAEh2aQ2SURSSQ&150909&151110,10011&jL40Z03uUFI0&AQEBh7uoLMUB9lkfw2sJVNx9g12Fzs12rPSN&150717&151125,10016&F64E6LFZs0W&AQEBh7uoLMUB9llE4yoPFNUykSj7WKaRK5lH&150805&151127,10019&WQAO-C1K9qI5OP8W_t2pSw&AQEBh7uoLMUB9llhpZE87GmOk3XGo_MJgV6K&150826&151130,10015&820490997316506147&AQEBh7uoLMUB9llXiynsGYRhMO3XuPnkuHUt&150715&151201,10012&x3X1yY6b&AQEBh7uoLMUB9ll9mraU_LJCDBYsE0Sbk_V9&151202&151202,110065&ucO0Z0gctNn3&AQEBh7uoLMUB9lkJcK3KDBQTKF0YfZ5wB7r5&150716&151203,110066&jL40Z03uUFI0&AQEBh7uoLMUB9lnyvKSYhcJD1X_rSs_DLVWx&150916&151221,10013&ePyYB2MSKa0TCbebpxKjmU&AQEBh7uoLMUB9ln6_6nGNidqml4nFKXhtE58&151221&151221,110061&d9cfa518d82abee&AQEBh7uoLMUB9llj2NYzmCjxaLWXALTcAGIH&150818&151224,10038&CAESEPZbUhToZJ39CS9MlgXGUSQ&AQEBh7uoLMUB9lmhnrDM5lIGtl6vc1NxMD6F&151110&151224,10077&820490997316506147&AQEBh7uoLMUB9lmkUdUe2xSHGkvM0IRu9Jt9&151214&151228,10008&0yPSvk92ie1nhB8wTUlTq&AQEBh7uoLMUB9lnL5ZCYvXJNvlv53G0CKEkj&150817&151228,10045&0&AQEBh7uoLMUB9llW3v1Vh7W72lv14RlAjUXn&151023&151228,110064&jL40Z03uUFI0&AQEBh7uoLMUB9lkBYuCUDLDrOcGURJcilogv&151016&160104,110069&26d49ecc&AQEBh7uoLMUB9lmlBLTxQY9BkCmimkMFqTo5&151204&160105,10079&B8hGto5y1e3uDXwCMsIun3rjk--dVCof&AQEBh7uoLMUB9llxnFrhDtdNMjZ1hs1il5J4&151214&160105; LHTturn=24; ptisp=ctc; RK=hRWyd82Gd8; pgv_pvi=7567882240; image_md5=bd21d5fb2f401b37cf3a02724dc06545; LTPturn=27; pt2gguin=o0583115900; uin=o0583115900; skey=@Mp9aCinaO; ptcz=10d4b1b7bde835d64663338a8008fd4f81e2c6b5f0ba81a90da3627ee617c7ee; pgv_info=ssid=s4768939310; pgv_pvid=6872592818; o_cookie=583115900; lv_play_index_textAd=47; lv_play_indexl.=32; dc_vplaying=1; LKBturn=29; Lturn=29; adid=583115900; appuser=95621BA8CB862E09; o_minduid=phhdxyNLkxBWMa74VTm5zU4y5EbUv5vR; appuser_95621BA8CB862E09_0=2b7gwp=1453219199_6&2btemv=1455551999_1&2c8311=1453305599_3&2cfx4j=1453651199_3&2cfx9l=1453651199_1&2d49y9=1453823999_2&2d67kl=1454255999_2&2d69mf=1454255999_3&2dxv8l=1455465599_6&2dzhfl=1452614399_1&f_pogvwp=1452095999_1&f_pogvwv=1452095999_2&f_pogw0m=1452095999_1&fd_15bm2t7=1452095999_1&fd_1h2pbsd=1452095999_2&fd_1k6so62=1452095999_1&fd_rhmjmq=1452095999_2&m_roiw0t=1452095999_3&m_xty8wl=1452095999_1&pogree=1452095999_2; TX.boid=100655474=1452072582_1&701041365=1452072585_1; appuser_95621BA8CB862E09_effect_0=fd_1ez2rcc=1452095999_1&fd_qdh7zw=1452095999_1&fd_ul215j=1452095999_1; psessionid=ca7f9c5b_1452071982_583115900_30754; psessiontime=1452071990", + "__X_HTTP_RESULT_INDEX": "1" + } +] \ No newline at end of file diff --git a/test/test_result_json/http_get_malformed.json b/test/test_result_json/http_get_malformed.json new file mode 100644 index 0000000..fdaa350 --- /dev/null +++ b/test/test_result_json/http_get_malformed.json @@ -0,0 +1,28 @@ + [ + { + "__X_HTTP_TUPLE4": "192.168.4.2.36598>108.61.176.217.80", + "__X_HTTP_RESULT_INDEX": "0" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "__X_HTTP_RESULT_INDEX": "1" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.0", + "res_status": "OK", + "major_version": 1, + "minor_version": 0, + "status_code": 200, + "Server": "FakeNet/1.3", + "Date": "Wed, 12 Sep 2018 19:45:55 GMT", + "Content-Type": "text/html", + "Content-Length": "1547", + "__X_HTTP_RESULT_INDEX": "2" + } + ] \ No newline at end of file diff --git a/test/test_result_json/http_get_multi_trans.json b/test/test_result_json/http_get_multi_trans.json new file mode 100644 index 0000000..6bb4ca4 --- /dev/null +++ b/test/test_result_json/http_get_multi_trans.json @@ -0,0 +1,139 @@ + +[{ + "__X_HTTP_TUPLE4": "192.168.50.18.60400>192.168.42.1.80", + "__X_HTTP_RESULT_INDEX": "0" + }, { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/account/login.htm", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.pro.testin.cn", + "Connection": "keep-alive", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.0.0 Safari/537.36", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", + "Accept-Encoding": "gzip, deflate", + "Accept-Language": "zh-CN,zh;q=0.9", + "Cookie": "Hm_lvt_1b8c1194303ef64e02f003f0cb8a1906=1653898514; _gcl_au=1.1.1010551181.1653898515; _ga=GA1.2.1419569885.1653898515; _gid=GA1.2.2007113907.1653898515; authtoken_pro=tea83b3beef07488bb8571811385db42; userId_pro=1160; pid_pro=1; eid_pro=1; pname_pro=name; Hm_lpvt_1b8c1194303ef64e02f003f0cb8a1906=1653961741; JSESSIONID=531AACA879469EDAB825E28113490E10", + "__X_HTTP_URL": "test.pro.testin.cn/account/login.htm", + "__X_HTTP_RESULT_INDEX": "1" + }, { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "major_version": 1, + "minor_version": 1, + "status_code": 302, + "Server": "nginx/1.16.1", + "Date": "Tue, 31 May 2022 06:41:23 GMT", + "Content-Length": "0", + "Connection": "keep-alive", + "Set-Cookie": "JSESSIONID=CFAB9C0C3F4D9D6C2837E3BA9425AFCA; Path=/; HttpOnly", + "Set-Cookie1": "authtoken_pro=tea83b3beef07488bb8571811385db42; Max-Age=28800; Expires=Tue, 31-May-2022 14:41:23 GMT; Domain=testin.cn; Path=/; HttpOnly", + "Set-Cookie2": "userId_pro=1160; Max-Age=28800; Expires=Tue, 31-May-2022 14:41:23 GMT; Domain=testin.cn; Path=/; HttpOnly", + "Location": "http://test.pro.testin.cn/enterprise/index.htm", + "Content-Language": "zh-CN", + "__X_HTTP_RESULT_INDEX": "2" + }, { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/enterprise/index.htm", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.pro.testin.cn", + "Connection": "keep-alive", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.0.0 Safari/537.36", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", + "Accept-Encoding": "gzip, deflate", + "Accept-Language": "zh-CN,zh;q=0.9", + "Cookie": "Hm_lvt_1b8c1194303ef64e02f003f0cb8a1906=1653898514; _gcl_au=1.1.1010551181.1653898515; _ga=GA1.2.1419569885.1653898515; _gid=GA1.2.2007113907.1653898515; authtoken_pro=tea83b3beef07488bb8571811385db42; userId_pro=1160; pid_pro=1; eid_pro=1; pname_pro=name; Hm_lpvt_1b8c1194303ef64e02f003f0cb8a1906=1653961741; JSESSIONID=CFAB9C0C3F4D9D6C2837E3BA9425AFCA", + "__X_HTTP_URL": "test.pro.testin.cn/enterprise/index.htm", + "__X_HTTP_RESULT_INDEX": "3" + }, { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "major_version": 1, + "minor_version": 1, + "status_code": 302, + "Server": "nginx/1.16.1", + "Date": "Tue, 31 May 2022 06:41:23 GMT", + "Content-Length": "0", + "Connection": "keep-alive", + "Set-Cookie": "authtoken_pro=tea83b3beef07488bb8571811385db42; Max-Age=28800; Expires=Tue, 31-May-2022 14:41:23 GMT; Domain=testin.cn; Path=/; HttpOnly", + "Set-Cookie1": "userId_pro=1160; Max-Age=28800; Expires=Tue, 31-May-2022 14:41:23 GMT; Domain=testin.cn; Path=/; HttpOnly", + "Location": "http://test.pro.testin.cn/enterprise/into.htm?eid=1", + "Content-Language": "zh-CN", + "__X_HTTP_RESULT_INDEX": "4" + }, { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/enterprise/into.htm?eid=1", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.pro.testin.cn", + "Connection": "keep-alive", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.0.0 Safari/537.36", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", + "Accept-Encoding": "gzip, deflate", + "Accept-Language": "zh-CN,zh;q=0.9", + "Cookie": "Hm_lvt_1b8c1194303ef64e02f003f0cb8a1906=1653898514; _gcl_au=1.1.1010551181.1653898515; _ga=GA1.2.1419569885.1653898515; _gid=GA1.2.2007113907.1653898515; authtoken_pro=tea83b3beef07488bb8571811385db42; userId_pro=1160; pid_pro=1; eid_pro=1; pname_pro=name; Hm_lpvt_1b8c1194303ef64e02f003f0cb8a1906=1653961741; JSESSIONID=CFAB9C0C3F4D9D6C2837E3BA9425AFCA", + "__X_HTTP_URL": "test.pro.testin.cn/enterprise/into.htm?eid=1", + "__X_HTTP_RESULT_INDEX": "5" + }, { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "major_version": 1, + "minor_version": 1, + "status_code": 302, + "Server": "nginx/1.16.1", + "Date": "Tue, 31 May 2022 06:41:23 GMT", + "Content-Length": "0", + "Connection": "keep-alive", + "Set-Cookie": "authtoken_pro=tea83b3beef07488bb8571811385db42; Max-Age=28800; Expires=Tue, 31-May-2022 14:41:23 GMT; Domain=testin.cn; Path=/; HttpOnly", + "Set-Cookie1": "userId_pro=1160; Max-Age=28800; Expires=Tue, 31-May-2022 14:41:23 GMT; Domain=testin.cn; Path=/; HttpOnly", + "Location": "http://test.pro.testin.cn/realmachine/index.htm", + "Content-Language": "zh-CN", + "__X_HTTP_RESULT_INDEX": "6" + }, { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/realmachine/index.htm", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "test.pro.testin.cn", + "Connection": "keep-alive", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.0.0 Safari/537.36", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", + "Accept-Encoding": "gzip, deflate", + "Accept-Language": "zh-CN,zh;q=0.9", + "Cookie": "Hm_lvt_1b8c1194303ef64e02f003f0cb8a1906=1653898514; _gcl_au=1.1.1010551181.1653898515; _ga=GA1.2.1419569885.1653898515; _gid=GA1.2.2007113907.1653898515; authtoken_pro=tea83b3beef07488bb8571811385db42; userId_pro=1160; pid_pro=1; eid_pro=1; pname_pro=name; Hm_lpvt_1b8c1194303ef64e02f003f0cb8a1906=1653961741; JSESSIONID=CFAB9C0C3F4D9D6C2837E3BA9425AFCA", + "__X_HTTP_URL": "test.pro.testin.cn/realmachine/index.htm", + "__X_HTTP_RESULT_INDEX": "7" + }, { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Server": "nginx/1.16.1", + "Date": "Tue, 31 May 2022 06:41:23 GMT", + "Content-Type": "text/html;charset=UTF-8", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "Vary": "Accept-Encoding", + "Set-Cookie": "authtoken_pro=tea83b3beef07488bb8571811385db42; Max-Age=28800; Expires=Tue, 31-May-2022 14:41:23 GMT; Domain=testin.cn; Path=/; HttpOnly", + "Set-Cookie1": "userId_pro=1160; Max-Age=28800; Expires=Tue, 31-May-2022 14:41:23 GMT; Domain=testin.cn; Path=/; HttpOnly", + "Set-Cookie2": "pid_pro=1; Max-Age=28800; Expires=Tue, 31-May-2022 14:41:23 GMT; Domain=testin.cn; Path=/; HttpOnly", + "Set-Cookie3": "eid_pro=1; Max-Age=28800; Expires=Tue, 31-May-2022 14:41:23 GMT; Domain=testin.cn; Path=/; HttpOnly", + "Set-Cookie4": "pname_pro=name; Max-Age=28800; Expires=Tue, 31-May-2022 14:41:23 GMT; Domain=testin.cn; Path=/; HttpOnly", + "Content-Language": "zh-CN", + "Content-Encoding": "gzip", + "__X_HTTP_RESULT_INDEX": "8" + }] \ No newline at end of file diff --git a/test/test_result_json/http_get_req_pipeline.json b/test/test_result_json/http_get_req_pipeline.json new file mode 100644 index 0000000..a442274 --- /dev/null +++ b/test/test_result_json/http_get_req_pipeline.json @@ -0,0 +1,67 @@ +[ + { + "__X_HTTP_TUPLE4": "192.168.40.81.52802>192.168.40.137.80", + "__X_HTTP_RESULT_INDEX": "0" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/aa.mp4?asf=sdaf", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "113.31.27.226", + "Connection": "keep-alive", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1636.2 Safari/537.36", + "Accept-Encoding": "gzip,deflate,sdch", + "Accept-Language": "zh-CN,zh;q=0.8", + "__X_HTTP_URL": "113.31.27.226/aa.mp4?asf=sdaf", + "__X_HTTP_RESULT_INDEX": "1" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/fetch_ldns.png", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "ns.pb.cachecn.net", + "Connection": "keep-alive", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1636.2 Safari/537.36", + "Accept-Encoding": "gzip,deflate,sdch", + "Accept-Language": "zh-CN,zh;q=0.8", + "__X_HTTP_URL": "ns.pb.cachecn.net/fetch_ldns.png", + "__X_HTTP_RESULT_INDEX": "2" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/40x.jpg", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "ns.pb.cachecn.net", + "Connection": "keep-alive", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1636.2 Safari/537.36", + "Accept-Encoding": "gzip,deflate,sdch", + "Accept-Language": "zh-CN,zh;q=0.8", + "__X_HTTP_URL": "ns.pb.cachecn.net/40x.jpg", + "__X_HTTP_RESULT_INDEX": "3" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.0", + "res_status": "File not found", + "major_version": 1, + "minor_version": 0, + "status_code": 404, + "Server": "SimpleHTTP/0.6 Python/2.7.5", + "Date": "Wed, 25 Oct 2023 06:43:35 GMT", + "Content-Type": "text/html", + "Connection": "close", + "__X_HTTP_RESULT_INDEX": "4" + } +] \ No newline at end of file diff --git a/test/test_result_json/http_get_single_trans.json b/test/test_result_json/http_get_single_trans.json new file mode 100644 index 0000000..b227989 --- /dev/null +++ b/test/test_result_json/http_get_single_trans.json @@ -0,0 +1,29 @@ +[{ + "__X_HTTP_TUPLE4": "192.168.38.73.50806>192.168.40.137.80", + "__X_HTTP_RESULT_INDEX": "0" + }, { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/index.html", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "192.168.40.137", + "User-Agent": "curl/7.79.1", + "Accept": "*/*", + "__X_HTTP_URL": "192.168.40.137/index.html", + "__X_HTTP_RESULT_INDEX": "1" + }, { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.0", + "res_status": "OK", + "major_version": 1, + "minor_version": 0, + "status_code": 200, + "Server": "SimpleHTTP/0.6 Python/2.7.5", + "Date": "Thu, 30 Nov 2023 08:42:24 GMT", + "Content-type": "text/html", + "Content-Length": "144", + "Last-Modified": "Thu, 30 Nov 2023 08:38:54 GMT", + "__X_HTTP_RESULT_INDEX": "2" + }] diff --git a/test/test_result_json/http_hdr_truncated_after_kv.json b/test/test_result_json/http_hdr_truncated_after_kv.json new file mode 100644 index 0000000..68b5f45 --- /dev/null +++ b/test/test_result_json/http_hdr_truncated_after_kv.json @@ -0,0 +1,285 @@ +[{ + "__X_HTTP_TUPLE4": "196.188.112.76.51494>23.246.50.149.80", + "__X_HTTP_RESULT_INDEX": "0" + }, { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABQSwLpJi-yDFZ7k9eqarY3x3-b99vNxxOt5xDeIOapZB6Y5QXPSa54b7cbWYTXYdFimDKCeJ4s7ngqpqByvtt0aLh85nSucLTcR3-OKleuNwVltHUscQhSgVfHc.jpg?r=392", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Accept": "image/*", + "Accept-Encoding": "deflate, gzip", + "Connection": "Keep-Alive", + "Host": "occ-0-778-360.1.nflxso.net", + "Language": "en-ET,en", + "Referer": "https://secure.netflix.com/us/tvui/ql/patch/20211109_18752/2/release/darwinBootstrap.js?getMainUrlFromCodex=true&taskDefaultTimeoutV2=120000&bootloader_trace=apiusernotnull__false&nq=true&nq_control_tag=tvui-main&startup_key=e38d3c70814421931b0189d91d872546326f05d8f470150d07340df9e9d72275&device_type=TCL-TVS88L&e=TCL-TVS88L0000000000000000342914&env=prod&fromNM=true&nm_prefetch=true&nrdapp_version=2015.1.1&plain=true&dh=720&dw=1280&dar=16_9®=undefined&authType=login&authclid=845becf0-992c-46f7-a938-1bc22b097c58&q=source_type%3D1%26additionalDataUrl%3Dhttp%253A%252F%252Flocalhost%253A56789%252Fapps%252FNetflix%252Fdial_data%26source_type_payload%3D", + "User-Agent": "Gibbon/2015.1.1/2015.1.1: Netflix/2015.1.1 (DEVTYPE=TCL-TVS88L; CERTVER=1)", + "__X_HTTP_URL": "occ-0-778-360.1.nflxso.net/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABQSwLpJi-yDFZ7k9eqarY3x3-b99vNxxOt5xDeIOapZB6Y5QXPSa54b7cbWYTXYdFimDKCeJ4s7ngqpqByvtt0aLh85nSucLTcR3-OKleuNwVltHUscQhSgVfHc.jpg?r=392", + "X-Gibbon-Cache-Control": "max-age=2592000, priority=4, key=/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABQSwLpJi-yDFZ7k9eqarY3x3-b99vNxxOt5xDeIOapZB6Y5QXPSa54b7cbWYTXYdFimDKCeJ4s7ngqpqByvtt0aLh85nSucLTcR3-OKleuNwVltHUscQhSgVfHc.jpg?r=392", + "__X_HTTP_RESULT_INDEX": "1" + }, { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABUWN8S1k3yM2coX2bwxbP699Jdr0BUqBRzIfiAJXKC5Ywt7DXqJOCjrBSYs36Tny8277IXm2BF_cgTmY18NJlocglKjhaoJhFeGoIg1cwntFduyxyRPP2EJQL5Y.jpg?r=e0e", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Accept": "image/*", + "Accept-Encoding": "deflate, gzip", + "Connection": "Keep-Alive", + "Host": "occ-0-778-360.1.nflxso.net", + "Language": "en-ET,en", + "Referer": "https://secure.netflix.com/us/tvui/ql/patch/20211109_18752/2/release/darwinBootstrap.js?getMainUrlFromCodex=true&taskDefaultTimeoutV2=120000&bootloader_trace=apiusernotnull__false&nq=true&nq_control_tag=tvui-main&startup_key=e38d3c70814421931b0189d91d872546326f05d8f470150d07340df9e9d72275&device_type=TCL-TVS88L&e=TCL-TVS88L0000000000000000342914&env=prod&fromNM=true&nm_prefetch=true&nrdapp_version=2015.1.1&plain=true&dh=720&dw=1280&dar=16_9®=undefined&authType=login&authclid=845becf0-992c-46f7-a938-1bc22b097c58&q=source_type%3D1%26additionalDataUrl%3Dhttp%253A%252F%252Flocalhost%253A56789%252Fapps%252FNetflix%252Fdial_data%26source_type_payload%3D", + "User-Agent": "Gibbon/2015.1.1/2015.1.1: Netflix/2015.1.1 (DEVTYPE=TCL-TVS88L; CERTVER=1)", + "__X_HTTP_URL": "occ-0-778-360.1.nflxso.net/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABUWN8S1k3yM2coX2bwxbP699Jdr0BUqBRzIfiAJXKC5Ywt7DXqJOCjrBSYs36Tny8277IXm2BF_cgTmY18NJlocglKjhaoJhFeGoIg1cwntFduyxyRPP2EJQL5Y.jpg?r=e0e", + "X-Gibbon-Cache-Control": "max-age=2592000, priority=4, key=/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABUWN8S1k3yM2coX2bwxbP699Jdr0BUqBRzIfiAJXKC5Ywt7DXqJOCjrBSYs36Tny8277IXm2BF_cgTmY18NJlocglKjhaoJhFeGoIg1cwntFduyxyRPP2EJQL5Y.jpg?r=e0e", + "__X_HTTP_RESULT_INDEX": "2" + }, { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABR1YQS01SaHGzoxgWBJF1Gas0Gv9_DPebb4irdCTRjcQ_FUaVbXFTTrJ68_bvJds1sb28VMq22Qn3oSSKKJ7DdLN8ybgkJooYlCD3gAntrqgIFugqv5Z3kV8rRE.jpg?r=ec7", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Accept": "image/*", + "Accept-Encoding": "deflate, gzip", + "Connection": "Keep-Alive", + "Host": "occ-0-778-360.1.nflxso.net", + "Language": "en-ET,en", + "Referer": "https://secure.netflix.com/us/tvui/ql/patch/20211109_18752/2/release/darwinBootstrap.js?getMainUrlFromCodex=true&taskDefaultTimeoutV2=120000&bootloader_trace=apiusernotnull__false&nq=true&nq_control_tag=tvui-main&startup_key=e38d3c70814421931b0189d91d872546326f05d8f470150d07340df9e9d72275&device_type=TCL-TVS88L&e=TCL-TVS88L0000000000000000342914&env=prod&fromNM=true&nm_prefetch=true&nrdapp_version=2015.1.1&plain=true&dh=720&dw=1280&dar=16_9®=undefined&authType=login&authclid=845becf0-992c-46f7-a938-1bc22b097c58&q=source_type%3D1%26additionalDataUrl%3Dhttp%253A%252F%252Flocalhost%253A56789%252Fapps%252FNetflix%252Fdial_data%26source_type_payload%3D", + "__X_HTTP_URL": "occ-0-778-360.1.nflxso.net/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABR1YQS01SaHGzoxgWBJF1Gas0Gv9_DPebb4irdCTRjcQ_FUaVbXFTTrJ68_bvJds1sb28VMq22Qn3oSSKKJ7DdLN8ybgkJooYlCD3gAntrqgIFugqv5Z3kV8rRE.jpg?r=ec7", + "User-Agent": "Gibbon/2015.1.1/2015.1.1: Netflix/2015.1.1 (DEVTYPE=TCL-TVS88L; CERTVER=1)", + "X-Gibbon-Cache-Control": "max-age=2592000, priority=4, key=/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABR1YQS01SaHGzoxgWBJF1Gas0Gv9_DPebb4irdCTRjcQ_FUaVbXFTTrJ68_bvJds1sb28VMq22Qn3oSSKKJ7DdLN8ybgkJooYlCD3gAntrqgIFugqv5Z3kV8rRE.jpg?r=ec7", + "__X_HTTP_RESULT_INDEX": "3" + }, { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Server": "nginx", + "Date": "Sat, 13 Nov 2021 14:15:58 GMT", + "Content-Type": "image/jpeg", + "Content-Length": "18377", + "Connection": "keep-alive", + "Cache-Control": "max-age=2592000", + "Last-Modified": "Wed, 10 Nov 2021 08:00:14 GMT", + "ETag": "\"c289a42885b30107ad119e2a6e405e4d\"", + "__X_HTTP_RESULT_INDEX": "4" + }, { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABYo5IGwDn1LaWbeaa7amS0JhH3bU5MEVlcBsC4OK0mGbea97_xoi8EbqJt8_Zp0bMuuKPE80qUUjb4wq5po_lBtulA.jpg?r=c83", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Accept": "image/*", + "Accept-Encoding": "deflate, gzip", + "Connection": "Keep-Alive", + "Host": "occ-0-778-360.1.nflxso.net", + "Language": "en-ET,en", + "__X_HTTP_URL": "occ-0-778-360.1.nflxso.net/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABYo5IGwDn1LaWbeaa7amS0JhH3bU5MEVlcBsC4OK0mGbea97_xoi8EbqJt8_Zp0bMuuKPE80qUUjb4wq5po_lBtulA.jpg?r=c83", + "Referer": "https://secure.netflix.com/us/tvui/ql/patch/20211109_18752/2/release/darwinBootstrap.js?getMainUrlFromCodex=true&taskDefaultTimeoutV2=120000&bootloader_trace=apiusernotnull__false&nq=true&nq_control_tag=tvui-main&startup_key=e38d3c70814421931b0189d91d872546326f05d8f470150d07340df9e9d72275&device_type=TCL-TVS88L&e=TCL-TVS88L0000000000000000342914&env=prod&fromNM=true&nm_prefetch=true&nrdapp_version=2015.1.1&plain=true&dh=720&dw=1280&dar=16_9®=undefined&authType=login&authclid=845becf0-992c-46f7-a938-1bc22b097c58&q=source_type%3D1%26additionalDataUrl%3Dhttp%253A%252F%252Flocalhost%253A56789%252Fapps%252FNetflix%252Fdial_data%26source_type_payload%3D", + "User-Agent": "Gibbon/2015.1.1/2015.1.1: Netflix/2015.1.1 (DEVTYPE=TCL-TVS88L; CERTVER=1)", + "X-Gibbon-Cache-Control": "max-age=2592000, priority=4, key=/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABYo5IGwDn1LaWbeaa7amS0JhH3bU5MEVlcBsC4OK0mGbea97_xoi8EbqJt8_Zp0bMuuKPE80qUUjb4wq5po_lBtulA.jpg?r=c83", + "__X_HTTP_RESULT_INDEX": "5" + }, { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABaYF3Rmi954cg6Afu9jOtirnvF3iIMHPZCCnP34eDeYQXfRGG9Vg0qgn7hHpMVV4jOr8OZmcD2Nb7MhQv6gl-fNmVQ.jpg?r=257", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Accept": "image/*", + "Accept-Encoding": "deflate, gzip", + "Connection": "Keep-Alive", + "Host": "occ-0-778-360.1.nflxso.net", + "Language": "en-ET,en", + "__X_HTTP_URL": "occ-0-778-360.1.nflxso.net/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABaYF3Rmi954cg6Afu9jOtirnvF3iIMHPZCCnP34eDeYQXfRGG9Vg0qgn7hHpMVV4jOr8OZmcD2Nb7MhQv6gl-fNmVQ.jpg?r=257", + "Referer": "https://secure.netflix.com/us/tvui/ql/patch/20211109_18752/2/release/darwinBootstrap.js?getMainUrlFromCodex=true&taskDefaultTimeoutV2=120000&bootloader_trace=apiusernotnull__false&nq=true&nq_control_tag=tvui-main&startup_key=e38d3c70814421931b0189d91d872546326f05d8f470150d07340df9e9d72275&device_type=TCL-TVS88L&e=TCL-TVS88L0000000000000000342914&env=prod&fromNM=true&nm_prefetch=true&nrdapp_version=2015.1.1&plain=true&dh=720&dw=1280&dar=16_9®=undefined&authType=login&authclid=845becf0-992c-46f7-a938-1bc22b097c58&q=source_type%3D1%26additionalDataUrl%3Dhttp%253A%252F%252Flocalhost%253A56789%252Fapps%252FNetflix%252Fdial_data%26source_type_payload%3D", + "User-Agent": "Gibbon/2015.1.1/2015.1.1: Netflix/2015.1.1 (DEVTYPE=TCL-TVS88L; CERTVER=1)", + "X-Gibbon-Cache-Control": "max-age=2592000, priority=4, key=/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABaYF3Rmi954cg6Afu9jOtirnvF3iIMHPZCCnP34eDeYQXfRGG9Vg0qgn7hHpMVV4jOr8OZmcD2Nb7MhQv6gl-fNmVQ.jpg?r=257", + "__X_HTTP_RESULT_INDEX": "6" + }, { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABfC-ByjFJSfWZf7MFtjVl3ONnaQ69824xWP1l-cpAFGgAfaNFk4XR9uoHNWwnbG8N2UVDctVKz0a4Uyv0mEnC2kI9Q.jpg?r=532", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Accept": "image/*", + "Accept-Encoding": "deflate, gzip", + "Connection": "Keep-Alive", + "Host": "occ-0-778-360.1.nflxso.net", + "Language": "en-ET,en", + "__X_HTTP_URL": "occ-0-778-360.1.nflxso.net/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABfC-ByjFJSfWZf7MFtjVl3ONnaQ69824xWP1l-cpAFGgAfaNFk4XR9uoHNWwnbG8N2UVDctVKz0a4Uyv0mEnC2kI9Q.jpg?r=532", + "Referer": "https://secure.netflix.com/us/tvui/ql/patch/20211109_18752/2/release/darwinBootstrap.js?getMainUrlFromCodex=true&taskDefaultTimeoutV2=120000&bootloader_trace=apiusernotnull__false&nq=true&nq_control_tag=tvui-main&startup_key=e38d3c70814421931b0189d91d872546326f05d8f470150d07340df9e9d72275&device_type=TCL-TVS88L&e=TCL-TVS88L0000000000000000342914&env=prod&fromNM=true&nm_prefetch=true&nrdapp_version=2015.1.1&plain=true&dh=720&dw=1280&dar=16_9®=undefined&authType=login&authclid=845becf0-992c-46f7-a938-1bc22b097c58&q=source_type%3D1%26additionalDataUrl%3Dhttp%253A%252F%252Flocalhost%253A56789%252Fapps%252FNetflix%252Fdial_data%26source_type_payload%3D", + "User-Agent": "Gibbon/2015.1.1/2015.1.1: Netflix/2015.1.1 (DEVTYPE=TCL-TVS88L; CERTVER=1)", + "X-Gibbon-Cache-Control": "max-age=2592000, priority=4, key=/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABfC-ByjFJSfWZf7MFtjVl3ONnaQ69824xWP1l-cpAFGgAfaNFk4XR9uoHNWwnbG8N2UVDctVKz0a4Uyv0mEnC2kI9Q.jpg?r=532", + "__X_HTTP_RESULT_INDEX": "7" + }, { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABSyGFXaB0IqQ6VR92uKMi38mNtoz7eeWxDziAf9VYKfauhh5Qo7FnnCRb31ff6Ez9yTXsqRszsGuz0GA9FVDf_NLn-9F60IcUHm59J73eYX6BD0h4wLLK0Da6YM.jpg?r=aaa", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Accept": "image/*", + "Accept-Encoding": "deflate, gzip", + "Connection": "Keep-Alive", + "Host": "occ-0-778-360.1.nflxso.net", + "Language": "en-ET,en", + "__X_HTTP_URL": "occ-0-778-360.1.nflxso.net/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABSyGFXaB0IqQ6VR92uKMi38mNtoz7eeWxDziAf9VYKfauhh5Qo7FnnCRb31ff6Ez9yTXsqRszsGuz0GA9FVDf_NLn-9F60IcUHm59J73eYX6BD0h4wLLK0Da6YM.jpg?r=aaa", + "Referer": "https://secure.netflix.com/us/tvui/ql/patch/20211109_18752/2/release/darwinBootstrap.js?getMainUrlFromCodex=true&taskDefaultTimeoutV2=120000&bootloader_trace=apiusernotnull__false&nq=true&nq_control_tag=tvui-main&startup_key=e38d3c70814421931b0189d91d872546326f05d8f470150d07340df9e9d72275&device_type=TCL-TVS88L&e=TCL-TVS88L0000000000000000342914&env=prod&fromNM=true&nm_prefetch=true&nrdapp_version=2015.1.1&plain=true&dh=720&dw=1280&dar=16_9®=undefined&authType=login&authclid=845becf0-992c-46f7-a938-1bc22b097c58&q=source_type%3D1%26additionalDataUrl%3Dhttp%253A%252F%252Flocalhost%253A56789%252Fapps%252FNetflix%252Fdial_data%26source_type_payload%3D", + "User-Agent": "Gibbon/2015.1.1/2015.1.1: Netflix/2015.1.1 (DEVTYPE=TCL-TVS88L; CERTVER=1)", + "X-Gibbon-Cache-Control": "max-age=2592000, priority=4, key=/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABSyGFXaB0IqQ6VR92uKMi38mNtoz7eeWxDziAf9VYKfauhh5Qo7FnnCRb31ff6Ez9yTXsqRszsGuz0GA9FVDf_NLn-9F60IcUHm59J73eYX6BD0h4wLLK0Da6YM.jpg?r=aaa", + "__X_HTTP_RESULT_INDEX": "8" + }, { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABZbHXQr7bRUpSQ2vQe8F8p3xODTJjUbSjEcLgQrFVyGsPgQT1GVhqGWFetJhebcGrVeZGOTmQ3qvHTe9eBRJdjFsVg.jpg?r=723", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Accept": "image/*", + "Accept-Encoding": "deflate, gzip", + "Connection": "Keep-Alive", + "Host": "occ-0-778-360.1.nflxso.net", + "Language": "en-ET,en", + "__X_HTTP_URL": "occ-0-778-360.1.nflxso.net/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABZbHXQr7bRUpSQ2vQe8F8p3xODTJjUbSjEcLgQrFVyGsPgQT1GVhqGWFetJhebcGrVeZGOTmQ3qvHTe9eBRJdjFsVg.jpg?r=723", + "Referer": "https://secure.netflix.com/us/tvui/ql/patch/20211109_18752/2/release/darwinBootstrap.js?getMainUrlFromCodex=true&taskDefaultTimeoutV2=120000&bootloader_trace=apiusernotnull__false&nq=true&nq_control_tag=tvui-main&startup_key=e38d3c70814421931b0189d91d872546326f05d8f470150d07340df9e9d72275&device_type=TCL-TVS88L&e=TCL-TVS88L0000000000000000342914&env=prod&fromNM=true&nm_prefetch=true&nrdapp_version=2015.1.1&plain=true&dh=720&dw=1280&dar=16_9®=undefined&authType=login&authclid=845becf0-992c-46f7-a938-1bc22b097c58&q=source_type%3D1%26additionalDataUrl%3Dhttp%253A%252F%252Flocalhost%253A56789%252Fapps%252FNetflix%252Fdial_data%26source_type_payload%3D", + "User-Agent": "Gibbon/2015.1.1/2015.1.1: Netflix/2015.1.1 (DEVTYPE=TCL-TVS88L; CERTVER=1)", + "X-Gibbon-Cache-Control": "max-age=2592000, priority=4, key=/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABZbHXQr7bRUpSQ2vQe8F8p3xODTJjUbSjEcLgQrFVyGsPgQT1GVhqGWFetJhebcGrVeZGOTmQ3qvHTe9eBRJdjFsVg.jpg?r=723", + "__X_HTTP_RESULT_INDEX": "9" + }, { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Server": "nginx", + "Date": "Sat, 13 Nov 2021 14:15:58 GMT", + "Content-Type": "image/jpeg", + "Content-Length": "13488", + "Connection": "keep-alive", + "Cache-Control": "max-age=2592000", + "Last-Modified": "Mon, 08 Nov 2021 19:50:54 GMT", + "ETag": "\"35d56b6a0ef3b5857013f44620bd8888\"", + "__X_HTTP_RESULT_INDEX": "10" + }, { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Server": "nginx", + "Date": "Sat, 13 Nov 2021 14:15:58 GMT", + "Content-Type": "image/jpeg", + "Content-Length": "14129", + "Connection": "keep-alive", + "Cache-Control": "max-age=2592000", + "Last-Modified": "Tue, 26 Oct 2021 15:12:58 GMT", + "ETag": "\"bb83961ad5fe366dcbb5240ead69f650\"", + "__X_HTTP_RESULT_INDEX": "11" + }, { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Server": "nginx", + "Date": "Sat, 13 Nov 2021 14:15:58 GMT", + "Content-Type": "image/jpeg", + "Content-Length": "11493", + "Connection": "keep-alive", + "Cache-Control": "max-age=2592000", + "Last-Modified": "Thu, 04 Nov 2021 20:44:22 GMT", + "ETag": "\"c5be6a7137482da270bb2adc8d44a28d\"", + "__X_HTTP_RESULT_INDEX": "12" + }, { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Server": "nginx", + "Date": "Sat, 13 Nov 2021 14:15:58 GMT", + "Content-Type": "image/jpeg", + "Content-Length": "14219", + "Connection": "keep-alive", + "Cache-Control": "max-age=2592000", + "Last-Modified": "Sat, 25 Sep 2021 05:02:49 GMT", + "ETag": "\"61bec96775876749bb57139f86f6b0ca\"", + "__X_HTTP_RESULT_INDEX": "13" + }, { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Server": "nginx", + "Date": "Sat, 13 Nov 2021 14:15:58 GMT", + "Content-Type": "image/jpeg", + "Content-Length": "21967", + "Connection": "keep-alive", + "Cache-Control": "max-age=2592000", + "Last-Modified": "Fri, 02 Jul 2021 10:15:04 GMT", + "ETag": "\"841065529f1e89eabd0ef235a3d3291c\"", + "__X_HTTP_RESULT_INDEX": "14" + }, { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Server": "nginx", + "Date": "Sat, 13 Nov 2021 14:15:58 GMT", + "Content-Type": "image/jpeg", + "Content-Length": "21670", + "Connection": "keep-alive", + "Cache-Control": "max-age=2592000", + "Last-Modified": "Tue, 19 Oct 2021 14:00:02 GMT", + "ETag": "\"21d4b2c21a3a2f26ba665670e8513940\"", + "__X_HTTP_RESULT_INDEX": "15" + }, { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABSFXjTUaVrddq_nehO4yuLziSjuekxJuv3oEsyUpmt3oK3InJcXjtZUHrBBuu0EP05WRC8wFVe78VtxtW_ZuZQ65MIcApb0oZF4JoFlHHnv363RbgJn898Q4tQc.jpg?r=590", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Accept": "image/*", + "Accept-Encoding": "deflate, gzip", + "Connection": "Keep-Alive", + "Host": "occ-0-778-360.1.nflxso.net", + "Language": "en-ET,en", + "Referer": "https://secure.netflix.com/us/tvui/ql/patch/20211109_18752/2/release/darwinBootstrap.js?getMainUrlFromCodex=true&taskDefaultTimeoutV2=120000&bootloader_trace=apiusernotnull__false&nq=true&nq_control_tag=tvui-main&startup_key=e38d3c70814421931b0189d91d872546326f05d8f470150d07340df9e9d72275&device_type=TCL-TVS88L&e=TCL-TVS88L0000000000000000342914&env=prod&fromNM=true&nm_prefetch=true&nrdapp_version=2015.1.1&plain=true&dh=720&dw=1280&dar=16_9®=undefined&authType=login&authclid=845becf0-992c-46f7-a938-1bc22b097c58&q=source_type%3D1%26additionalDataUrl%3Dhttp%253A%252F%252Flocalhost%253A56789%252Fapps%252FNetflix%252Fdial_data%26source_type_payload%3D", + "User-Agent": "Gibbon/2015.1.1/2015.1.1: Netflix/2015.1.1 (DEVTYPE=TCL-TVS88L; CERTVER=1)", + "__X_HTTP_URL": "occ-0-778-360.1.nflxso.net/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABSFXjTUaVrddq_nehO4yuLziSjuekxJuv3oEsyUpmt3oK3InJcXjtZUHrBBuu0EP05WRC8wFVe78VtxtW_ZuZQ65MIcApb0oZF4JoFlHHnv363RbgJn898Q4tQc.jpg?r=590", + "X-Gibbon-Cache-Control": "max-age=2592000, priority=4, key=/dnm/api/v6/Da_vleYcahiCE7JMYt8LJRyoenc/AAAABSFXjTUaVrddq_nehO4yuLziSjuekxJuv3oEsyUpmt3oK3InJcXjtZUHrBBuu0EP05WRC8wFVe78VtxtW_ZuZQ65MIcApb0oZF4JoFlHHnv363RbgJn898Q4tQc.jpg?r=590", + "__X_HTTP_RESULT_INDEX": "16" + }, { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Server": "nginx", + "Date": "Sat, 13 Nov 2021 14:15:58 GMT", + "Content-Type": "image/jpeg", + "Content-Length": "14215", + "Connection": "keep-alive", + "Cache-Control": "max-age=2592000", + "Last-Modified": "Thu, 11 Nov 2021 17:32:01 GMT", + "ETag": "\"837a2051f7d0f2899baf54ff20b3d9ad\"", + "__X_HTTP_RESULT_INDEX": "17" + }] diff --git a/test/test_result_json/http_hdr_truncated_in_kv.json b/test/test_result_json/http_hdr_truncated_in_kv.json new file mode 100644 index 0000000..f123f6d --- /dev/null +++ b/test/test_result_json/http_hdr_truncated_in_kv.json @@ -0,0 +1,53 @@ +[ + { + "__X_HTTP_TUPLE4": "196.190.248.93.32727>94.130.141.49.80", + "__X_HTTP_RESULT_INDEX": "0" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/iframes2/e38f4959d33f4fa390045b0d7123997d.html?subid=65843620", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "tsyndicate.com", + "Connection": "keep-alive", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Mozilla/5.0 (Linux; Android 11; SM-A217F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.74 Mobile Safari/537.36", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", + "Referer": "http://the-sexy-tube.com/", + "Accept-Encoding": "gzip, deflate", + "Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8", + "__X_HTTP_URL": "tsyndicate.com/iframes2/e38f4959d33f4fa390045b0d7123997d.html?subid=65843620", + "__X_HTTP_RESULT_INDEX": "1" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Server": "nginx", + "Date": "Sat, 13 Nov 2021 13:58:01 GMT", + "Content-Type": "text/html; charset=utf-8", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "Vary": "Accept-Encoding", + "Cache-Control": "no-cache, no-store, no-transform, must-revalidate", + "Pragma": "no-cache", + "Expires": "0", + "Vary1": "*", + "X-Api-Version": "2", + "Link": "; rel=preload; as=script", + "X-Request-Id": "2ceed9968bf8c648", + "Set-Cookie": "ts_uid=6ec96511-9fa6-4e10-86b8-31fdb4531864; expires=Fri, 13 May 2022 13:58:01 GMT; domain=.tsyndicate.com; path=/; HttpOnly; secure; SameSite=None", + "Set-Cookie2": "bfq=e0SIEaFjiwwZNWjkkDGjCwsRYwpuifFQRJmJMWzMsIEjBw4ZOCr2URAQ; expires=Sun, 14 Nov 2021 13:58:01 GMT; domain=.tsyndicate.com; path=/; secure; SameSite=None", + "X-Robots-Tag": "none", + "Cache-Control3": "no-transform", + "X-Robots-Tag4": "noindex, nofollow", + "Report-To": "{ \"url\": \"https://pxl.tsyndicate.com/api/v1/heavy-ad/report\", \"max_age\": 86401 }", + "Content-Encoding": "gzip", + "__X_HTTP_RESULT_INDEX": "2" + } +] \ No newline at end of file diff --git a/test/test_result_json/http_hdr_value_empty.json b/test/test_result_json/http_hdr_value_empty.json new file mode 100644 index 0000000..aface09 --- /dev/null +++ b/test/test_result_json/http_hdr_value_empty.json @@ -0,0 +1,35 @@ +[ + { + "__X_HTTP_TUPLE4": "192.168.131.33.47164>192.168.204.67.4445", + "__X_HTTP_RESULT_INDEX": "0" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "POST", + "uri": "http://:4445/RPC2", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "User-Agent": "ulxmlrpcpp/1.7.5", + "Connection": "Close", + "Content-Type": "text/xml", + "Date": "Sat Sep 7 10:04:57 2019", + "Content-Length": "468", + "__X_HTTP_URL": "http://:4445/RPC2", + "__X_HTTP_RESULT_INDEX": "1" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Connection": "Close", + "Content-Type": "text/xml", + "Transfer-Encoding": "chunked", + "X-Powered-By": "ulxmlrpcpp/1.7.4", + "Date": "Sat Sep 7 01:09:08 2019", + "__X_HTTP_RESULT_INDEX": "2" + } +] \ No newline at end of file diff --git a/test/test_result_json/http_hdrs_exceed_maximum.json b/test/test_result_json/http_hdrs_exceed_maximum.json new file mode 100644 index 0000000..09b7311 --- /dev/null +++ b/test/test_result_json/http_hdrs_exceed_maximum.json @@ -0,0 +1,46 @@ +[{ + "__X_HTTP_TUPLE4": "10.0.0.1.61462>10.0.0.2.80", + "__X_HTTP_RESULT_INDEX": "0" + }, { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/x/xx/xxxxxxxxxxxxxxxxxxx/x/xxxxxx/xxxxxxxxxxxxxxx?xxx=1&xxx=1&x=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&vmf=xxxxxxxxxx.xxx.xxx.xxx&ce=UTF-8&ns=xxxxxxxxxx&pageName=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&g=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.jsp&r=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&events=xxxxxxxxxxxxxxxxxxxxxxxxxxx&products=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&v1=xxxxxxxxxxxxxxx&v2=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&v17=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&c49=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&AQE=1", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "xxxxx.xxxxxxxx.xxxxxxxxxx.xxx", + "Connection": "keep-alive", + "Accept": "image/webp,*/*;q=0.8", + "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36", + "Referer": "http://www.xxxxxxxxxx.xxx/xx/xxxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxxxxxxxx/xxxxxxxxxxxx.jsp", + "Accept-Encoding": "gzip,deflate,sdch", + "Accept-Language": "en-US,en;q=0.8,en-GB;q=0.6", + "__X_HTTP_URL": "xxxxx.xxxxxxxx.xxxxxxxxxx.xxx/x/xx/xxxxxxxxxxxxxxxxxxx/x/xxxxxx/xxxxxxxxxxxxxxx?xxx=1&xxx=1&x=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&vmf=xxxxxxxxxx.xxx.xxx.xxx&ce=UTF-8&ns=xxxxxxxxxx&pageName=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&g=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.jsp&r=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&events=xxxxxxxxxxxxxxxxxxxxxxxxxxx&products=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&v1=xxxxxxxxxxxxxxx&v2=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&v17=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&c49=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&AQE=1", + "Cookie": "xxxxxxxxxxxxxxxxxxx=ie; xxxxxxxxxxxxxxxxxxxxxx=true; lp=xxxxxx; rememberUn=false; xxx.xxxxxxxxxx.xxxxxxxxxx=xx; xxxxx=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; autocomplete=1; xxxx=xxxx; xxxx=xxxxv1|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; xxxxxx=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "__X_HTTP_RESULT_INDEX": "1" + }, { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Date": "Mon, 30 Jun 2014 13:35:21 GMT", + "Server": "xxxxxxxxxxxxxxxxx", + "Access-Control-Allow-Origin": "*", + "Set-Cookie": "xxxx=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; Expires=Wed, 29 Jun 2016 13:35:21 GMT; Domain=.xxxxxxxxxx.xxx; Path=/", + "X-C": "ms-4.9", + "Expires": "Sun, 29 Jun 2014 13:35:21 GMT", + "Last-Modified": "Tue, 01 Jul 2014 13:35:21 GMT", + "Cache-Control": "no-cache, no-store, max-age=0, no-transform, private", + "Pragma": "no-cache", + "ETag": "\"xxxxxxxxxxxxxxxxxxxxxx\"", + "Vary": "*", + "P3P": "policyref=\"/w3c/p3p.xml\", CP=\"NOI DSP COR NID PSA OUR IND COM NAV STA\"", + "xserver": "xxxxxx", + "Content-Length": "43", + "Keep-Alive": "timeout=15", + "Connection": "Keep-Alive", + "Content-Type": "image/gif", + "__X_HTTP_RESULT_INDEX": "2" + }] diff --git a/test/test_result_json/http_multi_parse_error.json b/test/test_result_json/http_multi_parse_error.json new file mode 100644 index 0000000..8111a6f --- /dev/null +++ b/test/test_result_json/http_multi_parse_error.json @@ -0,0 +1,35 @@ +[ + { + "__X_HTTP_TUPLE4": "192.168.131.33.47172>192.168.204.67.4445", + "__X_HTTP_RESULT_INDEX": "0" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "POST", + "uri": "http://:4445/RPC2", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "User-Agent": "ulxmlrpcpp/1.7.5", + "Connection": "Close", + "Content-Type": "text/xml", + "Date": "Sat Sep 7 10:05:13 2019", + "Content-Length": "468", + "__X_HTTP_URL": "http://:4445/RPC2", + "__X_HTTP_RESULT_INDEX": "1" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Connection": "Close", + "Content-Type": "text/xml", + "Transfer-Encoding": "chunked", + "X-Powered-By": "ulxmlrpcpp/1.7.4", + "Date": "Sat Sep 7 01:09:24 2019", + "__X_HTTP_RESULT_INDEX": "2" + } +] \ No newline at end of file diff --git a/test/test_result_json/http_no_content_length.json b/test/test_result_json/http_no_content_length.json new file mode 100644 index 0000000..93ca066 --- /dev/null +++ b/test/test_result_json/http_no_content_length.json @@ -0,0 +1,45 @@ +[ + { + "__X_HTTP_TUPLE4": "10.0.0.1.50384>10.0.0.2.80", + "__X_HTTP_RESULT_INDEX": "0" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/js/xxxxxx.js", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "xxxxxxx.xxxxxx.xx", + "User-Agent": "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3", + "Accept": "*/*", + "Accept-Language": "en-us,en;q=0.5", + "Accept-Encoding": "gzip,deflate", + "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", + "Keep-Alive": "115", + "Connection": "keep-alive", + "Referer": "http://www.xxxxxxxx.com/xxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxx.html", + "Cookie": "trafic_ranking=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "__X_HTTP_URL": "xxxxxxx.xxxxxx.xx/js/xxxxxx.js", + "__X_HTTP_RESULT_INDEX": "1" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.0", + "res_status": "OK", + "major_version": 1, + "minor_version": 0, + "status_code": 200, + "Date": "Mon, 10 May 2010 08:31:02 GMT", + "Server": "Apache", + "Content-type": "application/x-javascript", + "Expires": "Thu, 11 Jan 1973 16:00:00 GMT", + "Last-Modified": "Mon, 10 May 2010 08:31:02 GMT", + "Cache-Control": "no-store, no-cache, must-revalidate, post-check=0, pre-check=0", + "Pragma": "no-cache", + "P3P": "policyref=\"/w3c/p3p.xml\", CP=\"ALL IND DSP COR ADM CONo CUR IVAo IVDo PSA PSD TAI TELo OUR SAMo CNT COM INT NAV ONL PHY PRE PUR UNI\"", + "Set-Cookie": "trafic_ranking=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; expires=Sun, 11-Jan-2037 14:00:00 GMT; path=/; domain=.xxxxxx.xx", + "connection": "close", + "__X_HTTP_RESULT_INDEX": "2" + } +] \ No newline at end of file diff --git a/test/test_result_json/http_over_pppoe.json b/test/test_result_json/http_over_pppoe.json new file mode 100644 index 0000000..23856ca --- /dev/null +++ b/test/test_result_json/http_over_pppoe.json @@ -0,0 +1,35 @@ +[ + { + "__X_HTTP_TUPLE4": "2a00:5e80:101:212d:504:7b1:2572:db22.37034>2606:f200:0:7:bad:f00d:d00d:1.80", + "__X_HTTP_RESULT_INDEX": "0" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "User-Agent": "curl/7.34.0", + "Host": "ipv6.icanhazip.com", + "Accept": "*/*", + "__X_HTTP_URL": "ipv6.icanhazip.com/", + "__X_HTTP_RESULT_INDEX": "1" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Date": "Thu, 02 Jan 2014 08:38:06 GMT", + "Server": "Apache", + "Content-Length": "38", + "Content-Type": "text/plain; charset=UTF-8", + "X-RTFM": "Learn about this site at http://bit.ly/14DAh2o and don't abuse the service", + "X-YOU-SHOULD-APPLY-FOR-A-JOB": "If you're reading this, apply here: http://rackertalent.com/", + "X-ICANHAZNODE": "icanhazip1.nugget", + "__X_HTTP_RESULT_INDEX": "2" + } +] \ No newline at end of file diff --git a/test/test_result_json/http_over_tcp_keepalive.json b/test/test_result_json/http_over_tcp_keepalive.json new file mode 100644 index 0000000..1481cb7 --- /dev/null +++ b/test/test_result_json/http_over_tcp_keepalive.json @@ -0,0 +1,40 @@ +[{ + "__X_HTTP_TUPLE4": "192.168.56.66.55356>60.190.243.167.80", + "__X_HTTP_RESULT_INDEX": "0" + }, { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "www.yumi.com", + "Connection": "keep-alive", + "Cache-Control": "max-age=0", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", + "Accept-Encoding": "gzip, deflate", + "Accept-Language": "zh-CN,zh;q=0.9", + "Cookie": "UM_distinctid=17d37645f9c1a1-0281befa480414-b7a1a38-144000-17d37645f9d336; CNZZDATA1258295942=1778021578-1637307701-%7C1637307701; Hm_lvt_a6dc86f6e27435039966e994bd7f0792=1637311872; yumi_sid=JaMlFyTA07ikpZjTHZsRTWyGdMqFyFy%2B4hXGj%2FSoQRJYbrfBUQuOTIMZ8jUGmugDC594AYcbeRhg75xidhRxCW4zq9Y0gPwTmkhq4LQuprp4DrtDMLI3L5wLMqkG%2FuAX1aVFPfud5GRNxNFTSp%2Bos%2FKhfCFKhfN5%2BuT2xyVYSAjy2ftiSOGDi7FN13icuuyPhFCoWqOxWVu1CZ3AiYPJssv6kXqiR6paf75icdeROZY2bkFCDKkcIQcPy7o9EKpkL1Mbimeb40JMg9hUsWdmyhDkzVjSHJmC4z2ujpzSDTsjRIQOnxTy1PHZi%2FMwg3uyGLCusDwqbagpO4pcgEJ5ONDy%2BGwO7FmHXU3mFfR56c9HxxiiuLPnBt9ErpqqWKsxH6lUrlHaUp6AzyrgX7PFdksiMfPSk6%2F3%2FWOYr%2FkYuI4fopw7z8%2FLhxC9AiLr9Czz3MngFUGzkmaMVvAhZOSPzg%3D%3D; Hm_lpvt_a6dc86f6e27435039966e994bd7f0792=1637313847", + "__X_HTTP_URL": "www.yumi.com/", + "__X_HTTP_RESULT_INDEX": "1" + }, { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Server": "Tengine", + "Date": "Fri, 19 Nov 2021 09:57:40 GMT", + "Content-Type": "text/html", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "Vary": "Accept-Encoding", + "Set-Cookie": "yumi_sid=V6EhlyovLp46BBzQWLVOBg%2F73RUD5E%2FfaRlkR8RLa8aKhGrPVdVHvHfFWRKKd9wZ%2BfFF4Tb2wnVOOn%2F9iXbBpyHsbxjmUqnbFSoAX7QIJjt%2BEEjAL3M7O7VSpAyMnrFKt7qu46oXV%2B6teyyTUY7Ucy285v6otvZcu8bN%2B5YxKZ1gYh56iJ0bHxnrnQ0vvAx3l%2BLwfw2y0c5IaF2tjrL%2Fn83nrHsPoYYRWAR2zLIXD%2FEMKRtyerwsM5LKhZZteFGWD2w%2B15alKF5T65i0lPvPcAdaqpceL5xz23twQULhs1tIJsOfJZ8JudLlRy6x3DvxQYqRe2xTCex5c77zJqfq%2FdryNbBycIq9gf6C2hXDRwDqRqVgXDMadwGnooKFkv%2ByCbohjHyBCZJypBcYFmglYhin23UC9i%2B%2BOA%2FxhlxcnU8kT8udpTNCktSmF950SQLOmvdvYuXGydKs8v05cxe5fg%3D%3D; expires=Fri, 19-Nov-2021 11:57:38 GMT; Max-Age=7200; path=/; domain=.yumi.com", + "Pragma": "no-cache", + "Cache-Control": "no-store", + "Content-Encoding": "gzip", + "__X_HTTP_RESULT_INDEX": "2" + }] diff --git a/test/test_result_json/http_over_tls.json b/test/test_result_json/http_over_tls.json new file mode 100644 index 0000000..32960f8 --- /dev/null +++ b/test/test_result_json/http_over_tls.json @@ -0,0 +1,2 @@ +[ +] \ No newline at end of file diff --git a/test/test_result_json/http_post_multipart_form_data.json b/test/test_result_json/http_post_multipart_form_data.json new file mode 100644 index 0000000..3e761dd --- /dev/null +++ b/test/test_result_json/http_post_multipart_form_data.json @@ -0,0 +1,101 @@ +[ + { + "__X_HTTP_TUPLE4": "192.168.8.97.11371>192.168.57.14.8080", + "__X_HTTP_RESULT_INDEX": "0" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/fileupload/", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "192.168.57.14:8080", + "Connection": "keep-alive", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "User-Agent": "Mozilla/5.0 (Windows NT 5.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36", + "Accept-Encoding": "gzip,deflate,sdch", + "Accept-Language": "zh-CN,zh;q=0.8", + "Cookie": "JSESSIONID=969AC5FBD069EE6218EB10513726B244; JSESSIONID=400CC78DF5784F303702CC7F02C6122C", + "__X_HTTP_URL": "192.168.57.14:8080/fileupload/", + "__X_HTTP_RESULT_INDEX": "1" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Server": "Apache-Coyote/1.1", + "Content-Type": "text/html;charset=UTF-8", + "Content-Length": "468", + "Date": "Thu, 28 Mar 2019 08:13:33 GMT", + "__X_HTTP_RESULT_INDEX": "2" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/fileupload/", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "192.168.57.14:8080", + "Connection": "keep-alive", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "User-Agent": "Mozilla/5.0 (Windows NT 5.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36", + "Accept-Encoding": "gzip,deflate,sdch", + "Accept-Language": "zh-CN,zh;q=0.8", + "Cookie": "JSESSIONID=969AC5FBD069EE6218EB10513726B244; JSESSIONID=400CC78DF5784F303702CC7F02C6122C", + "__X_HTTP_URL": "192.168.57.14:8080/fileupload/", + "__X_HTTP_RESULT_INDEX": "3" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Server": "Apache-Coyote/1.1", + "Content-Type": "text/html;charset=UTF-8", + "Content-Length": "468", + "Date": "Thu, 28 Mar 2019 08:13:33 GMT", + "__X_HTTP_RESULT_INDEX": "4" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "POST", + "uri": "/fileupload/servlet/UploadHandleServlet", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "192.168.57.14:8080", + "Connection": "keep-alive", + "Content-Length": "449", + "Cache-Control": "max-age=0", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Origin": "http://192.168.57.14:8080", + "User-Agent": "Mozilla/5.0 (Windows NT 5.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36", + "Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryAe47vGj7ybAe6RwO", + "Referer": "http://192.168.57.14:8080/fileupload/", + "Accept-Encoding": "gzip,deflate,sdch", + "Accept-Language": "zh-CN,zh;q=0.8", + "Cookie": "JSESSIONID=969AC5FBD069EE6218EB10513726B244; JSESSIONID=400CC78DF5784F303702CC7F02C6122C", + "__X_HTTP_URL": "192.168.57.14:8080/fileupload/servlet/UploadHandleServlet", + "__X_HTTP_RESULT_INDEX": "5" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Server": "Apache-Coyote/1.1", + "Content-Type": "text/html;charset=UTF-8", + "Content-Length": "144", + "Date": "Thu, 28 Mar 2019 08:13:37 GMT", + "__X_HTTP_RESULT_INDEX": "6" + } +] \ No newline at end of file diff --git a/test/test_result_json/http_req_1byte_sliding_window.json b/test/test_result_json/http_req_1byte_sliding_window.json new file mode 100644 index 0000000..c295389 --- /dev/null +++ b/test/test_result_json/http_req_1byte_sliding_window.json @@ -0,0 +1,34 @@ +[ + { + "__X_HTTP_TUPLE4": "192.168.40.137.46180>192.168.42.40.80", + "__X_HTTP_RESULT_INDEX": "0" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/index.html", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "User-Agent": "Wget/1.14 (linux-gnu)", + "Accept": "*/*", + "Host": "192.168.42.40", + "__X_HTTP_URL": "192.168.42.40/index.html", + "Connection": "Keep-Alive", + "__X_HTTP_RESULT_INDEX": "1" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.0", + "res_status": "OK", + "major_version": 1, + "minor_version": 0, + "status_code": 200, + "Server": "SimpleHTTP/0.6 Python/2.7.5", + "Date": "Fri, 29 Dec 2023 09:11:12 GMT", + "Content-type": "text/html", + "Content-Length": "144", + "Last-Modified": "Fri, 29 Dec 2023 08:50:53 GMT", + "__X_HTTP_RESULT_INDEX": "2" + } +] \ No newline at end of file diff --git a/test/test_result_json/http_res_1byte_sliding_window.json b/test/test_result_json/http_res_1byte_sliding_window.json new file mode 100644 index 0000000..60b756d --- /dev/null +++ b/test/test_result_json/http_res_1byte_sliding_window.json @@ -0,0 +1,34 @@ +[ + { + "__X_HTTP_TUPLE4": "192.168.42.40.36338>192.168.40.137.80", + "__X_HTTP_RESULT_INDEX": "0" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/index.html", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "User-Agent": "Wget/1.14 (linux-gnu)", + "Accept": "*/*", + "Host": "192.168.40.137", + "Connection": "Keep-Alive", + "__X_HTTP_URL": "192.168.40.137/index.html", + "__X_HTTP_RESULT_INDEX": "1" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.0", + "res_status": "OK", + "major_version": 1, + "minor_version": 0, + "status_code": 200, + "Server": "SimpleHTTP/0.6 Python/2.7.5", + "Date": "Fri, 29 Dec 2023 09:32:21 GMT", + "Content-type": "text/html", + "Content-Length": "144", + "Last-Modified": "Fri, 29 Dec 2023 08:50:53 GMT", + "__X_HTTP_RESULT_INDEX": "2" + } +] \ No newline at end of file diff --git a/test/test_result_json/http_res_gzip.json b/test/test_result_json/http_res_gzip.json new file mode 100644 index 0000000..357dbe9 --- /dev/null +++ b/test/test_result_json/http_res_gzip.json @@ -0,0 +1,44 @@ +[ + { + "__X_HTTP_TUPLE4": "192.168.69.2.34059>192.168.69.1.80", + "__X_HTTP_RESULT_INDEX": "0" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/test/ethereal.html", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "cerberus", + "User-Agent": "Mozilla/5.0 (X11; U; Linux ppc; rv:1.7.3) Gecko/20041004 Firefox/0.10.1", + "Accept": "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5", + "Accept-Language": "en-us,en;q=0.5", + "Accept-Encoding": "gzip,deflate", + "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", + "Keep-Alive": "300", + "Connection": "keep-alive", + "Cookie": "FGNCLIID=05c04axp1yaqynldtcdiwis0ag1", + "__X_HTTP_URL": "cerberus/test/ethereal.html", + "__X_HTTP_RESULT_INDEX": "1" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "OK", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Date": "Fri, 29 Oct 2004 05:21:00 GMT", + "Server": "Apache/2.0.50 (Fedora)", + "Last-Modified": "Fri, 29 Oct 2004 05:20:21 GMT", + "ETag": "\"126e1f-6d-371b2f40\"", + "Accept-Ranges": "bytes", + "Vary": "Accept-Encoding", + "Content-Encoding": "gzip", + "Content-Length": "92", + "Connection": "close", + "Content-Type": "text/html; charset=UTF-8", + "__X_HTTP_RESULT_INDEX": "2" + } +] \ No newline at end of file diff --git a/test/test_result_json/http_trans_pipeline.json b/test/test_result_json/http_trans_pipeline.json new file mode 100644 index 0000000..c9b5d37 --- /dev/null +++ b/test/test_result_json/http_trans_pipeline.json @@ -0,0 +1,368 @@ +[ + { + "__X_HTTP_TUPLE4": "223.72.39.14.2545>192.168.182.147.80", + "__X_HTTP_RESULT_INDEX": "0" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/postinfo.html", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "116.181.2.152", + "User-Agent": "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)", + "Connection": "keep-alive", + "__X_HTTP_URL": "116.181.2.152/postinfo.html", + "__X_HTTP_RESULT_INDEX": "1" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/_vti_bin/_vti_aut/author.dll", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "116.181.2.152", + "User-Agent": "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)", + "Connection": "keep-alive", + "__X_HTTP_URL": "116.181.2.152/_vti_bin/_vti_aut/author.dll", + "__X_HTTP_RESULT_INDEX": "2" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/_vti_bin/_vti_aut/author.exe", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "116.181.2.152", + "User-Agent": "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)", + "Connection": "keep-alive", + "__X_HTTP_URL": "116.181.2.152/_vti_bin/_vti_aut/author.exe", + "__X_HTTP_RESULT_INDEX": "3" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/_vti_bin/_vti_aut/dvwssr.dll", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "116.181.2.152", + "User-Agent": "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)", + "Connection": "keep-alive", + "__X_HTTP_URL": "116.181.2.152/_vti_bin/_vti_aut/dvwssr.dll", + "__X_HTTP_RESULT_INDEX": "4" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/_vti_bin/_vti_adm/admin.dll", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "116.181.2.152", + "User-Agent": "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)", + "Connection": "keep-alive", + "__X_HTTP_URL": "116.181.2.152/_vti_bin/_vti_adm/admin.dll", + "__X_HTTP_RESULT_INDEX": "5" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/_vti_bin/_vti_adm/admin.exe", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "116.181.2.152", + "User-Agent": "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)", + "Connection": "keep-alive", + "__X_HTTP_URL": "116.181.2.152/_vti_bin/_vti_adm/admin.exe", + "__X_HTTP_RESULT_INDEX": "6" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/_vti_bin/fpcount.exe?Page=default.asp|Image=3", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "116.181.2.152", + "User-Agent": "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)", + "Connection": "keep-alive", + "__X_HTTP_URL": "116.181.2.152/_vti_bin/fpcount.exe?Page=default.asp|Image=3", + "__X_HTTP_RESULT_INDEX": "7" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "Not Found", + "major_version": 1, + "minor_version": 1, + "status_code": 404, + "Server": "nginx", + "Date": "Thu, 29 Oct 2020 09:59:01 GMT", + "Content-Type": "text/html", + "Content-Length": "146", + "Connection": "keep-alive", + "__X_HTTP_RESULT_INDEX": "8" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "Not Found", + "major_version": 1, + "minor_version": 1, + "status_code": 404, + "Server": "nginx", + "Date": "Thu, 29 Oct 2020 09:59:02 GMT", + "Content-Type": "text/html", + "Content-Length": "146", + "Connection": "keep-alive", + "__X_HTTP_RESULT_INDEX": "9" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "Not Found", + "major_version": 1, + "minor_version": 1, + "status_code": 404, + "Server": "nginx", + "Date": "Thu, 29 Oct 2020 09:59:03 GMT", + "Content-Type": "text/html", + "Content-Length": "146", + "Connection": "keep-alive", + "__X_HTTP_RESULT_INDEX": "10" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "Not Found", + "major_version": 1, + "minor_version": 1, + "status_code": 404, + "Server": "nginx", + "Date": "Thu, 29 Oct 2020 09:59:04 GMT", + "Content-Type": "text/html", + "Content-Length": "146", + "Connection": "keep-alive", + "__X_HTTP_RESULT_INDEX": "11" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "Not Found", + "major_version": 1, + "minor_version": 1, + "status_code": 404, + "Server": "nginx", + "Date": "Thu, 29 Oct 2020 09:59:05 GMT", + "Content-Type": "text/html", + "Content-Length": "146", + "Connection": "keep-alive", + "__X_HTTP_RESULT_INDEX": "12" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/_vti_bin/shtml.dll", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "116.181.2.152", + "User-Agent": "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)", + "__X_HTTP_URL": "116.181.2.152/_vti_bin/shtml.dll", + "Connection": "keep-alive", + "__X_HTTP_RESULT_INDEX": "13" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/_vti_bin/shtml.exe", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "116.181.2.152", + "User-Agent": "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)", + "Connection": "keep-alive", + "__X_HTTP_URL": "116.181.2.152/_vti_bin/shtml.exe", + "__X_HTTP_RESULT_INDEX": "14" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/_vti_pvt/_x_todo.htm", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "116.181.2.152", + "User-Agent": "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)", + "Connection": "keep-alive", + "__X_HTTP_URL": "116.181.2.152/_vti_pvt/_x_todo.htm", + "__X_HTTP_RESULT_INDEX": "15" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/_vti_pvt/_x_todoh.htm", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "116.181.2.152", + "User-Agent": "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)", + "Connection": "keep-alive", + "__X_HTTP_URL": "116.181.2.152/_vti_pvt/_x_todoh.htm", + "__X_HTTP_RESULT_INDEX": "16" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/_vti_pvt/access.cnf", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "116.181.2.152", + "User-Agent": "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)", + "Connection": "keep-alive", + "__X_HTTP_URL": "116.181.2.152/_vti_pvt/access.cnf", + "__X_HTTP_RESULT_INDEX": "17" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/_vti_pvt/administrator.pwd", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "116.181.2.152", + "User-Agent": "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)", + "Connection": "keep-alive", + "__X_HTTP_URL": "116.181.2.152/_vti_pvt/administrator.pwd", + "__X_HTTP_RESULT_INDEX": "18" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/_vti_pvt/administrators.pwd", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "116.181.2.152", + "User-Agent": "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)", + "Connection": "keep-alive", + "__X_HTTP_URL": "116.181.2.152/_vti_pvt/administrators.pwd", + "__X_HTTP_RESULT_INDEX": "19" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/_vti_pvt/authors.pwd", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "116.181.2.152", + "User-Agent": "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)", + "Connection": "keep-alive", + "__X_HTTP_URL": "116.181.2.152/_vti_pvt/authors.pwd", + "__X_HTTP_RESULT_INDEX": "20" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "Not Found", + "major_version": 1, + "minor_version": 1, + "status_code": 404, + "Server": "nginx", + "Date": "Thu, 29 Oct 2020 09:59:06 GMT", + "Content-Type": "text/html", + "Content-Length": "146", + "Connection": "keep-alive", + "__X_HTTP_RESULT_INDEX": "21" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "Not Found", + "major_version": 1, + "minor_version": 1, + "status_code": 404, + "Server": "nginx", + "Date": "Thu, 29 Oct 2020 09:59:07 GMT", + "Content-Type": "text/html", + "Content-Length": "146", + "Connection": "keep-alive", + "__X_HTTP_RESULT_INDEX": "22" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "Not Found", + "major_version": 1, + "minor_version": 1, + "status_code": 404, + "Server": "nginx", + "Date": "Thu, 29 Oct 2020 09:59:08 GMT", + "Content-Type": "text/html", + "Content-Length": "146", + "Connection": "keep-alive", + "__X_HTTP_RESULT_INDEX": "23" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "Not Found", + "major_version": 1, + "minor_version": 1, + "status_code": 404, + "Server": "nginx", + "Date": "Thu, 29 Oct 2020 09:59:09 GMT", + "Content-Type": "text/html", + "Content-Length": "146", + "Connection": "keep-alive", + "__X_HTTP_RESULT_INDEX": "24" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "Not Found", + "major_version": 1, + "minor_version": 1, + "status_code": 404, + "Server": "nginx", + "Date": "Thu, 29 Oct 2020 09:59:10 GMT", + "Content-Type": "text/html", + "Content-Length": "146", + "Connection": "keep-alive", + "__X_HTTP_RESULT_INDEX": "25" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/_vti_pvt/bots.cnf", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "116.181.2.152", + "User-Agent": "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)", + "Connection": "keep-alive", + "__X_HTTP_URL": "116.181.2.152/_vti_pvt/bots.cnf", + "__X_HTTP_RESULT_INDEX": "26" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "Not Found", + "major_version": 1, + "minor_version": 1, + "status_code": 404, + "Server": "nginx", + "Date": "Thu, 29 Oct 2020 09:59:11 GMT", + "Content-Type": "text/html", + "Content-Length": "146", + "Connection": "keep-alive", + "__X_HTTP_RESULT_INDEX": "27" + } +] \ No newline at end of file diff --git a/test/test_result_json/http_tunnel_for_pop3.json b/test/test_result_json/http_tunnel_for_pop3.json new file mode 100644 index 0000000..7956a50 --- /dev/null +++ b/test/test_result_json/http_tunnel_for_pop3.json @@ -0,0 +1,33 @@ +[ + { + "__X_HTTP_TUPLE4": "192.168.10.58.51798>192.168.10.144.808", + "__X_HTTP_RESULT_INDEX": "0" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "CONNECT", + "uri": "pop.163.com:110", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Content-Length": "0", + "Accept": "*/*", + "User-Agent": "Foxmail 7, 1, 3, 48[cn]", + "Accept-Encoding": "gzip, deflate", + "Proxy-Connection": "Keep-Alive", + "Connection": "Keep-Alive", + "Host": "192.168.10.144", + "__X_HTTP_URL": "192.168.10.144pop.163.com:110", + "__X_HTTP_RESULT_INDEX": "1" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "Connection established", + "major_version": 1, + "minor_version": 1, + "status_code": 200, + "Proxy-agent": "CCProxy", + "__X_HTTP_RESULT_INDEX": "2" + } +] \ No newline at end of file diff --git a/test/test_result_json/http_upgrade_http2.json b/test/test_result_json/http_upgrade_http2.json new file mode 100644 index 0000000..3d455a5 --- /dev/null +++ b/test/test_result_json/http_upgrade_http2.json @@ -0,0 +1,42 @@ +[ + { + "__X_HTTP_TUPLE4": "10.9.0.2.58038>139.162.123.134.80", + "__X_HTTP_RESULT_INDEX": "0" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/robots.txt", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "nghttp2.org", + "User-Agent": "curl/7.61.0", + "Accept": "*/*", + "Connection": "Upgrade, HTTP2-Settings", + "Upgrade": "h2c", + "HTTP2-Settings": "AAMAAABkAARAAAAAAAIAAAAA", + "__X_HTTP_URL": "nghttp2.org/robots.txt", + "__X_HTTP_RESULT_INDEX": "1" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "PRI", + "uri": "*", + "req_version": "2.0", + "major_version": 2, + "minor_version": 0, + "__X_HTTP_RESULT_INDEX": "2" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "Switching Protocols", + "major_version": 1, + "minor_version": 1, + "status_code": 101, + "Connection": "Upgrade", + "Upgrade": "h2c", + "__X_HTTP_RESULT_INDEX": "3" + } +] \ No newline at end of file diff --git a/test/test_result_json/http_upgrade_websocket.json b/test/test_result_json/http_upgrade_websocket.json new file mode 100644 index 0000000..33e7336 --- /dev/null +++ b/test/test_result_json/http_upgrade_websocket.json @@ -0,0 +1,42 @@ +[ + { + "__X_HTTP_TUPLE4": "131.179.196.220.59631>131.179.196.46.9696", + "__X_HTTP_RESULT_INDEX": "0" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "spurs.cs.ucla.edu:9696", + "Connection": "Upgrade", + "Pragma": "no-cache", + "Cache-Control": "no-cache", + "Upgrade": "websocket", + "Origin": "null", + "Sec-WebSocket-Version": "13", + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.124 Safari/537.36", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8,lv;q=0.6,ru;q=0.4", + "Cookie": "s_cc=true; s_sq=%5B%5BB%5D%5D; iwe_user_noticecount_urn%3amace%3aucla.edu%3appid%3aperson%3a1223EF7211FC4EC1965579D0B8D85FBA=2; __utma=125574670.1759122974.1407127284.1407127284.1415755402.2; __utmc=125574670; __utma=126236063.2139843507.1390525421.1433785187.1435706244.46; __utmc=126236063; __utmz=126236063.1427934389.33.5.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=(not%20provided); _ucla_sso=2015-07-02T11%3A34%3A30-07%3A00; _ga=GA1.2.1759122974.1407127284", + "Sec-WebSocket-Key": "sgD1adxQ3mk6BbBqab7owA==", + "Sec-WebSocket-Extensions": "permessage-deflate; client_max_window_bits", + "__X_HTTP_URL": "spurs.cs.ucla.edu:9696/", + "__X_HTTP_RESULT_INDEX": "1" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.1", + "res_status": "Switching Protocols", + "major_version": 1, + "minor_version": 1, + "status_code": 101, + "Connection": "upgrade", + "Sec-WebSocket-Accept": "FRh9fmH0UaoLdY5BSFO4hP2Pcjw=", + "Server": "WebSocket++/0.5.1", + "Upgrade": "websocket", + "__X_HTTP_RESULT_INDEX": "2" + } +] \ No newline at end of file diff --git a/test/test_result_json/http_url_test_with_host.json b/test/test_result_json/http_url_test_with_host.json new file mode 100644 index 0000000..a7fe660 --- /dev/null +++ b/test/test_result_json/http_url_test_with_host.json @@ -0,0 +1,35 @@ +[ + { + "__X_HTTP_TUPLE4": "192.168.244.1.52412>192.168.244.128.8080", + "__X_HTTP_RESULT_INDEX": "0" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/urltest/ttt", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "Host": "192.168.244.128:8080", + "Connection": "keep-alive", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", + "Accept-Encoding": "gzip, deflate", + "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", + "__X_HTTP_URL": "192.168.244.128:8080/urltest/ttt", + "__X_HTTP_RESULT_INDEX": "1" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.0", + "res_status": "OK", + "major_version": 1, + "minor_version": 0, + "status_code": 200, + "Server": "BaseHTTP/0.6 Python/3.6.8", + "Date": "Thu, 14 Mar 2024 07:37:43 GMT", + "Content-type": "application/json", + "__X_HTTP_RESULT_INDEX": "2" + } +] \ No newline at end of file diff --git a/test/test_result_json/http_url_test_without_host.json b/test/test_result_json/http_url_test_without_host.json new file mode 100644 index 0000000..9c91498 --- /dev/null +++ b/test/test_result_json/http_url_test_without_host.json @@ -0,0 +1,29 @@ +[ + { + "__X_HTTP_TUPLE4": "192.168.244.128.44868>192.168.244.128.8080", + "__X_HTTP_RESULT_INDEX": "0" + }, + { + "__X_HTTP_TRANSACTION": "request", + "method": "GET", + "uri": "/urltest/ttt", + "req_version": "1.1", + "major_version": 1, + "minor_version": 1, + "User-Agent": "no h", + "__X_HTTP_URL": "192.168.244.128:8080/urltest/ttt", + "__X_HTTP_RESULT_INDEX": "1" + }, + { + "__X_HTTP_TRANSACTION": "response", + "res_version": "1.0", + "res_status": "OK", + "major_version": 1, + "minor_version": 0, + "status_code": 200, + "Server": "BaseHTTP/0.6 Python/3.6.8", + "Date": "Thu, 14 Mar 2024 06:15:20 GMT", + "Content-type": "application/json", + "__X_HTTP_RESULT_INDEX": "2" + } +] \ No newline at end of file diff --git a/test/test_result_json/non_http.json b/test/test_result_json/non_http.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/test/test_result_json/non_http.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/vendor/.gitkeep b/vendor/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt new file mode 100644 index 0000000..1f15068 --- /dev/null +++ b/vendor/CMakeLists.txt @@ -0,0 +1,53 @@ +include(ExternalProject) + +set(VENDOR_BUILD ${CMAKE_BINARY_DIR}/vendor/vbuild) + +# GoogleTest +ExternalProject_Add(googletest PREFIX googletest + URL ${CMAKE_CURRENT_SOURCE_DIR}/googletest-release-1.8.0.tar.gz + URL_MD5 16877098823401d1bf2ed7891d7dce36 + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}) + +ExternalProject_Get_Property(googletest INSTALL_DIR) +file(MAKE_DIRECTORY ${INSTALL_DIR}/include) + +add_library(gtest STATIC IMPORTED GLOBAL) +add_dependencies(gtest googletest) +set_property(TARGET gtest PROPERTY IMPORTED_LOCATION ${INSTALL_DIR}/lib/libgtest.a) +set_property(TARGET gtest PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include) +set_property(TARGET gtest PROPERTY INTERFACE_LINK_LIBRARIES pthread) + +add_library(gmock STATIC IMPORTED GLOBAL) +add_dependencies(gmock googletest) +set_property(TARGET gmock PROPERTY IMPORTED_LOCATION ${INSTALL_DIR}/lib/libgmock.a) +set_property(TARGET gmock PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include) + +#llhttp-9.1.2 +ExternalProject_Add(llhttp PREFIX llhttp + URL ${CMAKE_CURRENT_SOURCE_DIR}/llhttp-release-v9.1.3.tar.gz + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${VENDOR_BUILD} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_C_FLAGS="-fPIC") + +file(MAKE_DIRECTORY ${VENDOR_BUILD}/include) + +add_library(llhttp-static STATIC IMPORTED GLOBAL) +add_dependencies(llhttp-static llhttp) + +set_property(TARGET llhttp-static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${VENDOR_BUILD}/include) +set_property(TARGET llhttp-static PROPERTY IMPORTED_LOCATION ${VENDOR_BUILD}/lib64/libllhttp.a) + +#libcjson-1.7.17 +ExternalProject_Add(cjson PREFIX cjson +URL ${CMAKE_CURRENT_SOURCE_DIR}/libcjson_v1.7.17.tar.gz +URL_MD5 4b2ab12cf065c079004aa44495ade04a +CONFIGURE_COMMAND "" +BUILD_COMMAND make +INSTALL_COMMAND make install +BUILD_IN_SOURCE 1) + +ExternalProject_Get_Property(cjson INSTALL_DIR) +file(MAKE_DIRECTORY ${VENDOR_BUILD}/include/cjson) + +add_library(cjson-static STATIC IMPORTED GLOBAL) +add_dependencies(cjson-static cjson) +set_property(TARGET cjson-static PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/vendor/cjson/src/cjson/libcjson.a) +set_property(TARGET cjson-static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${VENDOR_BUILD}/include/cjson) diff --git a/vendor/googletest-release-1.8.0.tar.gz b/vendor/googletest-release-1.8.0.tar.gz new file mode 100644 index 0000000..a40df33 Binary files /dev/null and b/vendor/googletest-release-1.8.0.tar.gz differ diff --git a/vendor/libcjson_v1.7.17.tar.gz b/vendor/libcjson_v1.7.17.tar.gz new file mode 100644 index 0000000..5d023fc Binary files /dev/null and b/vendor/libcjson_v1.7.17.tar.gz differ diff --git a/vendor/llhttp-release-v9.1.3.tar.gz b/vendor/llhttp-release-v9.1.3.tar.gz new file mode 100644 index 0000000..c83dbd0 Binary files /dev/null and b/vendor/llhttp-release-v9.1.3.tar.gz differ -- cgit v1.2.3