Преглед изворни кода

[dataextract] 提交初版数据提取工程

yanxin пре 7 месеци
родитељ
комит
90899c6d75

+ 7 - 0
FastExtract/.gitignore

@@ -0,0 +1,7 @@
+.idea/
+
+build*/
+cmake-*/
+
+*.so
+*.dll

+ 83 - 0
FastExtract/CMakeLists.txt

@@ -0,0 +1,83 @@
+cmake_minimum_required(VERSION 3.10)
+
+project(
+        FastExtract
+        VERSION 0.0.1
+        LANGUAGES C CXX
+)
+
+set(CMAKE_BUILD_TYPE Release CACHE STRING "build type")
+set(CMAKE_C_STANDARD 99)
+set(CMAKE_CXX_STANDARD 17)
+
+if(NOT MSVC)
+    set(CMAKE_CXX_FLAGS "-Wno-format -g0 -O3")
+    if(NEED_ABI0)
+        add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
+    else()
+        add_definitions(-D_GLIBCXX_USE_CXX11_ABI=1)
+    endif()
+endif(NOT MSVC)
+
+option(ENABLE_GPU "" ON)
+option(BUILD_CLIENT "" OFF)
+option(BUILD_SERVER "" OFF)
+option(BUILD_TUTORIAL "" OFF)
+option(ENABLE_HEADER_FOOTER "" ON)
+option(ENABLE_POSTPROCESS "" ON)
+
+set(Workflow_DIR "" CACHE PATH "WORKFLOW_DIRECTORY")
+set(TRT_DRECTORY "" CACHE PATH "TRT_DRECTORY")
+set(FASTDEPLOY_DRECTORY "" CACHE PATH "FASTDEPLOY_DRECTORY")
+set(RAPIDJSON_DRECTORY "" CACHE PATH "RAPIDJSON_DRECTORY")
+set(Srpc_DIR "" CACHE PATH "Srpc_DIR")
+
+set(THIRDPARTY_PATH ${PROJECT_SOURCE_DIR}/third_party)
+
+set(RUNTIME_OUTPUT_DIR ${PROJECT_SOURCE_DIR}/bin)
+set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/build)
+set(PROJECT_SOURCE_PATH ${PROJECT_SOURCE_DIR}/src)
+set(PROJECT_ROOT_PATH ${PROJECT_SOURCE_DIR})
+
+if (WIN32)
+    set(CMAKE_C_FLAGS   "${CMAKE_C_FLAGS}   /MP /wd4200")
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /wd4200 /std:c++17")
+else ()
+    set(CMAKE_C_FLAGS   "${CMAKE_C_FLAGS}   -Wall -fPIC -pipe -std=gnu99")
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -pipe -std=c++17 -Wno-invalid-offsetof")
+endif ()
+
+if(BUILD_CLIENT OR BUILD_SERVER)
+    find_package(srpc REQUIRED CONFIG HINTS ${Srpc_DIR})
+    find_package(OpenSSL REQUIRED)
+    find_package(Protobuf)
+    if ("x${Protobuf_DIR}" STREQUAL "xProtobuf_DIR-NOTFOUND")
+        if (APPLE)
+            message (FATAL_ERROR ${Protobuf_ERROR_MSG_MACOS})
+        else ()
+            message (FATAL_ERROR ${Protobuf_ERROR_MSG_LINUX})
+        endif ()
+    endif ()
+
+    get_filename_component(Protobuf_LIB_DIR ${Protobuf_LIBRARY} DIRECTORY)
+
+    if (NOT EXISTS "${Srpc_DIR}/third_party/lz4/lib/lz4.h")
+        set(LZ4_LIB lz4)
+    endif ()
+
+    if (NOT EXISTS "${Srpc_DIR}/third_party/snappy/cmake")
+        set(SNAPPY_LIB snappy)
+    endif ()
+
+    find_package(ZLIB REQUIRED)
+
+
+    find_package(Workflow REQUIRED CONFIG HINTS ${Workflow_DIR})
+
+    # Prefer to static link first
+    set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ${CMAKE_FIND_LIBRARY_SUFFIXES})
+    find_library(Workflow_LIB workflow HINTS ${Workflow_DIR}/_lib)
+    find_library(Srpc_LIB srpc HINTS ${Srpc_DIR}/_lib)
+endif(BUILD_CLIENT OR BUILD_SERVER)
+
+add_subdirectory(src)

+ 34 - 0
FastExtract/config/client.conf

@@ -0,0 +1,34 @@
+{
+  "conversion":
+  {
+    "pdf_root_path": "/media/kdan/0d2e102d-a28c-4d29-a512-f1409a936b44/Data/sbp1/",
+    "conversion_resource_path": "/home/kdan/repo/conversion_repos/pdfium_tools/",
+    "conversion_instance": "conversion_fastdeploy",
+    "conversion_type": "2txt",
+    "output_path": "/media/kdan/0d2e102d-a28c-4d29-a512-f1409a936b44/Data/神笔派/output/",
+    "model_path": "model_path/home/kdan/下载/ppyoloe_plus_crn_m_80e_coco_DET_TA_F_20230511",
+    "outer_batch_size": 10000000,
+    "inner_batch_size": 100000
+  },
+
+  "client":
+  {
+    "remote_host": "192.168.10.147",
+    "remote_port": 8080,
+    "redirect_max": 2,
+    "retry_max": 2
+  },
+
+  "global":
+  {
+    "poller_threads": 30,
+    "handler_threads": 60,
+    "endpoint_params":
+    {
+      "max_connections": 20000,
+      "connect_timeout": 1000000,
+      "response_timeout": 1000000
+    }
+  }
+}
+

+ 26 - 0
FastExtract/config/server.conf

@@ -0,0 +1,26 @@
+{
+  "server":
+  {
+    "port": 1412
+  },
+
+  "global":
+  {
+    "poller_threads": 30,
+    "handler_threads": 60,
+    "endpoint_params":
+    {
+      "max_connections": 20000,
+      "connect_timeout": 1000000,
+      "response_timeout": 1000000
+    }
+  },
+
+  "model":
+  {
+    "det_model_path": "/home/kdan/repo/conversion_fastdeploy/ppyoloe_plus_crn_m_80e_coco_DET_TA_F_20230511/model.pdmodel",
+    "det_params_path": "/home/kdan/repo/conversion_fastdeploy/ppyoloe_plus_crn_m_80e_coco_DET_TA_F_20230511/model.pdiparams",
+    "det_cfg_path": "/home/kdan/repo/conversion_fastdeploy/ppyoloe_plus_crn_m_80e_coco_DET_TA_F_20230511/infer_cfg.yml",
+    "model_count": 60
+  }
+}

+ 15 - 0
FastExtract/config/server.proto

@@ -0,0 +1,15 @@
+syntax="proto2";
+
+message EchoRequest {
+	required string path = 1;
+};
+
+message EchoResponse {
+	required string json = 1;
+	optional int32 state = 2;
+	optional int32 error = 3;
+};
+
+service gpu_server {
+	rpc Echo(EchoRequest) returns (EchoResponse);
+};

+ 18 - 0
FastExtract/scripts/build_linux.sh

@@ -0,0 +1,18 @@
+cd ..
+
+if [ -d "build" ]
+then
+    cd build
+else
+    mkdir build
+    cd build
+fi
+
+cmake .. -DWorkflow_DIR=/home/kdan/repo/srpc/workflow \
+            -DTRT_DRECTORY=/usr/local/TensorRT-8.5.3.1   \
+            -DFASTDEPLOY_DRECTORY=/home/kdan/repo/FastDeploy/build_trt/compiled_fastdeploy_sdk    \
+            -DBUILD_CLIENT=ON \
+            -DRAPIDJSON_DRECTORY=/home/kdan/repo/rapidjson    \
+            -DSrpc_DIR=/home/kdan/repo/srpc/
+
+make -j16

+ 11 - 0
FastExtract/src/CMakeLists.txt

@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 3.10)
+
+add_subdirectory(util)
+
+if(BUILD_CLIENT)
+    add_subdirectory(client)
+endif(BUILD_CLIENT)
+
+if(BUILD_SERVER)
+    add_subdirectory(server)
+endif(BUILD_SERVER)

+ 26 - 0
FastExtract/src/client/CMakeLists.txt

@@ -0,0 +1,26 @@
+cmake_minimum_required(VERSION 3.10)
+project(fastextract_client LANGUAGES CXX)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(CMAKE_CXX_FLAGS_RELEASE "-O3")
+set(CMAKE_BUILD_TYPE Release)
+
+# Set all the libraries here
+set(LIB ${Srpc_LIB} ${Workflow_LIB} pthread OpenSSL::SSL OpenSSL::Crypto
+        protobuf z ${SNAPPY_LIB} ${LZ4_LIB} ${THIRDPARTY_PATH}/conversion/lib/libpdfium.so)
+
+# Add header directories and library directories here
+include_directories(${OPENSSL_INCLUDE_DIR} ${WORKFLOW_INCLUDE_DIR})
+link_directories(${OPENSSL_LINK_DIR} ${WORKFLOW_LIB_DIR})
+
+file(GLOB_RECURSE CLIENT_SRC ${CMAKE_CURRENT_LIST_DIR}/*.cc)
+
+include_directories(${RAPIDJSON_DRECTORY}/include)
+
+include_directories(${THIRDPARTY_PATH}/conversion/include)
+include_directories(${PROJECT_SOURCE_PATH}/util)
+
+# Build executable outputs
+add_executable(${PROJECT_NAME} ${CLIENT_SRC} $<TARGET_OBJECTS:util>)
+target_link_libraries(${PROJECT_NAME} ${LIB})
+

+ 260 - 0
FastExtract/src/client/client.cc

@@ -0,0 +1,260 @@
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <thread>
+#include <chrono>
+#include <ctime>
+
+#include "workflow/HttpMessage.h"
+#include "workflow/WFTaskFactory.h"
+#include "workflow/WFFacilities.h"
+#include "workflow/Workflow.h"
+
+#include "client_config/config.h"
+
+static srpc::ClientRPCConfig config;
+
+struct MMInput
+{
+    int id;
+	std::string file_path;
+};
+
+struct MMOutput
+{
+    int error;
+	double time;
+};
+
+using MMFactory = WFThreadTaskFactory<MMInput, MMOutput>;
+using MMTask = WFThreadTask<MMInput, MMOutput>;
+
+void put_callback(WFHttpTask *task)
+{
+    int state = task->get_state();
+    int error = task->get_error();
+
+    if (state == WFT_STATE_SUCCESS) // print server response body
+    {
+        const void *body;
+        size_t body_len;
+
+        task->get_resp()->get_parsed_body(&body, &body_len);
+        std::string res((char *)(body), body_len);
+        if (res.empty())
+        {
+            return;
+        }
+        auto json_res = wfrest::Json::parse(res);
+        config.add_http_put_count();
+    }
+}
+
+void convert_callback(MMTask *task)
+{
+	auto *input = task->get_input();
+	auto *output = task->get_output();
+
+    std::string url = std::string("http://") + config.client_host() +
+                      std::string(":") + std::to_string(config.client_port()) + std::string("/UpdateTaskStatus");
+    std::stringstream query_params;
+    query_params << "?id=" << input->id << "&status=";
+    if (output->error == 0) {
+        query_params << 2;
+    } else {
+        query_params << 3;
+    }
+    
+    url.append(query_params.str());
+	assert(task->get_state() == WFT_STATE_SUCCESS);
+
+    WFHttpTask *http_task = WFTaskFactory::create_http_task(url,
+                                                            config.redirect_max(),
+                                                            config.retry_max(),
+                                                            put_callback);
+    http_task->start();
+}
+
+void convert_pdf(const MMInput *in, MMOutput *out)
+{
+    out->error = -1;
+    if (!in->file_path.empty())
+    {
+        char* new_conv_args[7] {nullptr};
+        for (int i = 0; i < 6; ++i) {
+            switch (i)
+            {
+            case 0:
+            {
+                new_conv_args[i] = new char[strlen(config.get_conversion_instance()) + 1]{0};
+                strcpy(new_conv_args[i], config.get_conversion_instance());
+                break;
+            }
+            case 1:
+            {
+                new_conv_args[i] = new char[strlen(config.get_conversion_type()) + 1]{0};
+                strcpy(new_conv_args[i], config.get_conversion_type());
+                break;
+            }
+            case 2:
+            {
+                new_conv_args[i] = new char[strlen(config.get_conversion_resource_path()) + 1]{0};
+                strcpy(new_conv_args[i], config.get_conversion_resource_path());
+                break;
+            }
+            case 3:
+            {
+                new_conv_args[i] = new char[in->file_path.length() + 1]{0};
+                strcpy(new_conv_args[i], in->file_path.c_str());
+                break;
+            }
+            case 4:
+            {
+                std::string output_path = config.get_output_path();
+                output_path.append(config.get_time_stamp());
+                output_path.append("/");
+
+                std::string tmp_str = in->file_path;
+                tmp_str.erase(0, strlen(config.get_root_path()));
+                auto position = tmp_str.find_last_of('/');
+                tmp_str.erase(position, tmp_str.length() - position);
+
+                output_path.append(tmp_str);
+                output_path.append("/");
+                new_conv_args[i] = new char[output_path.length() + 1]{0};
+                strcpy(new_conv_args[i], output_path.c_str());
+                break;
+            }
+            case 5:
+            {
+                new_conv_args[i] = new char[strlen(config.get_model_path()) + 1]{0};
+                strcpy(new_conv_args[i], config.get_model_path());
+                break;
+            }
+            default:
+                break;
+            }
+        }
+
+        out->error = 0;
+        pid_t pid = fork();
+        if (pid == -1) { // fork失败
+            std::cerr << "Failed to fork()" << std::endl;
+            return;
+        } else if (pid == 0) { // 这里是子进程
+            execvp(new_conv_args[0], new_conv_args);
+            out->error = -1;
+            // 此处不应该被执行,除非execvp失败
+            _exit(EXIT_FAILURE); // 如果execvp失败,退出子进程
+        } else { // 这里是父进程
+            int status;
+            // 等待子进程结束,并获取子进程的退出状态
+            if (waitpid(pid, &status, 0) == -1) {
+                perror("waitpid");
+                out->error = -1;
+                return;
+            }
+
+            // 检查子进程的退出状态
+            if (WIFEXITED(status)) {
+                out->error = WEXITSTATUS(status);
+            } else if (WIFSIGNALED(status)) {
+                std::cerr << "Child process terminated by signal." << std::endl;
+                out->error = -1; // 或者设置一个特定的错误码
+            }
+        }
+
+        for (int i = 0; i < 7; ++i)
+            delete[] new_conv_args[i];
+    }
+}
+
+void sig_handler(int signo)
+{
+    
+}
+
+void init(const char* path)
+{
+    if (config.load(path) == false)
+    {
+        perror("Load config failed");
+        exit(1);
+    }
+
+    signal(SIGINT, sig_handler);
+    signal(SIGTERM, sig_handler);
+}
+
+void callback(WFHttpTask *task)
+{
+    int state = task->get_state();
+    int error = task->get_error();
+
+    if (state == WFT_STATE_SUCCESS) // print server response body
+    {
+        const void *body;
+        size_t body_len;
+
+        task->get_resp()->get_parsed_body(&body, &body_len);
+        std::string res((char *)(body), body_len);
+        if (res.empty())
+        {
+            return;
+        }
+        
+        auto json_res = wfrest::Json::parse(res);
+
+        if (json_res.has("path"))
+        {
+            std::string file_path = config.get_pdf_root_path() + json_res["path"].get<std::string>();
+            MMTask *task = MMFactory::create_thread_task("convert_task", convert_pdf, convert_callback);
+            auto *input = task->get_input();
+            input->file_path = std::move(file_path);
+            input->id = json_res["id"].get<int>();
+           
+            Workflow::start_series_work(task, nullptr);
+            config.add_http_get_count();
+        }
+    }
+}
+
+int main(int argc, char* argv[])
+{
+    if (argc < 2)
+    {
+        std::cout << "please input client config path." << std::endl;
+        return -1;
+    }
+    
+
+    init(argv[1]);
+
+    std::string url = std::string("http://") + config.client_host() +
+                      std::string(":") + std::to_string(config.client_port()) + std::string("/GetNewTask");
+
+    auto outer_size = config.get_outer_batch_size();
+    auto inner_size = config.get_inner_batch_size();
+    for (int i = 0; i < outer_size; i++)
+    {
+        auto begin = std::chrono::high_resolution_clock::now();
+        ParallelWork *pwork = Workflow::create_parallel_work(nullptr);
+        for (int j = 0; j < inner_size; j++)
+        {
+            WFHttpTask *task = WFTaskFactory::create_http_task(url,
+                                                            config.redirect_max(),
+                                                            config.retry_max(),
+                                                            callback);
+            SeriesWork *series = Workflow::create_series_work(task, nullptr);
+            pwork->add_series(series);
+        }
+        
+        Workflow::start_series_work(pwork, nullptr);
+        auto end = std::chrono::high_resolution_clock::now();
+        auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin);
+        std::cout << "转换任务数: " << config.get_http_get_count() << ", 上传任务数: " << config.get_http_put_count() << std::endl;
+    }
+    
+    return 0;
+}

+ 44 - 0
FastExtract/src/server/CMakeLists.txt

@@ -0,0 +1,44 @@
+cmake_minimum_required(VERSION 3.10)
+project(fastextract_server LANGUAGES CXX)
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(CMAKE_CXX_FLAGS_RELEASE "-O3")
+set(CMAKE_BUILD_TYPE Release)
+
+# Generate idl code: xx.srpc.h xx.pb.h xx.pb.cc xx.thrift.h
+message("PROJECT_ROOT_PATH: ${PROJECT_ROOT_PATH}")
+set(IDL_FILE ${PROJECT_ROOT_PATH}/config/server.proto)
+set(SRPC_GEN_PROGRAM ${SRPC_BIN_DIR}/srpc_generator) 
+find_program(PROTOC "protoc")
+if(${PROTOC} STREQUAL "PROTOC-NOTFOUND")
+    message(FATAL_ERROR "Protobuf compiler is missing!")
+endif ()
+protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${IDL_FILE})
+
+add_custom_target(SRPC_GEN ALL
+    COMMAND ${SRPC_GEN_PROGRAM} ${IDL_FILE} ${PROJECT_SOURCE_DIR} -s
+    COMMENT "srpc generator..."
+)
+
+# Set all the libraries here
+include(${FASTDEPLOY_DRECTORY}/FastDeploy.cmake)
+set(LIB ${Srpc_LIB} ${Workflow_LIB} pthread OpenSSL::SSL OpenSSL::Crypto
+    protobuf z ${SNAPPY_LIB} ${LZ4_LIB})
+
+# Add all the common code here
+set(COMMON_CODE ${PROTO_SRCS})
+
+# Add header directories and library directories here
+include_directories(${OPENSSL_INCLUDE_DIR} ${Protobuf_INCLUDE_DIR}
+                    ${WORKFLOW_INCLUDE_DIR} ${SRPC_INCLUDE_DIR}
+                    ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
+link_directories(${OPENSSL_LINK_DIR} ${Protobuf_LIB_DIR}
+                 ${WORKFLOW_LIB_DIR} ${SRPC_LIB_DIR})
+include_directories(${PROJECT_SOURCE_PATH}/util)
+include_directories(${RAPIDJSON_DRECTORY}/include)
+include_directories(${FASTDEPLOY_INCS})
+
+# Build executable outputs
+add_executable(${PROJECT_NAME} server.cc ${COMMON_CODE} $<TARGET_OBJECTS:util>)
+target_link_libraries(${PROJECT_NAME} ${LIB} ${FASTDEPLOY_LIBS})

+ 211 - 0
FastExtract/src/server/server.cc

@@ -0,0 +1,211 @@
+#include <stdio.h>
+#include <signal.h>
+#include <mutex>
+#include <thread>
+
+#include "workflow/WFFacilities.h"
+#include "srpc/rpc_types.h"
+#include "fastdeploy/vision.h"
+#include "rapidjson/document.h"
+#include "rapidjson/writer.h"
+#include "rapidjson/stringbuffer.h"
+
+#include "server_config/config.h"
+#include "server.srpc.h"
+
+using namespace srpc;
+
+static int gpu_id = 0;
+
+static std::vector< fastdeploy::vision::detection::PPYOLOER* > det_models;
+static std::vector< char > model_used;
+static std::mutex model_mtx;
+
+static WFFacilities::WaitGroup wait_group(100);
+static srpc::ServerRPCConfig config;
+
+std::string DetectionResultToJson(const fastdeploy::vision::DetectionResult& res)
+{
+    rapidjson::StringBuffer ss;
+    rapidjson::Writer<rapidjson::StringBuffer> writer(ss);
+
+    auto label_map = [](int label) -> std::string {
+        switch (label)
+        {
+            case 3:
+                return "Figure";
+
+            case 6:
+                return "Table_0";
+
+            case 7:
+                return "Table_std";
+
+            default:
+                return "";
+        }
+    };
+
+    writer.StartObject();
+    writer.Key("objects");
+    writer.StartArray();
+    for (int i = 0; i < res.scores.size(); i++) {
+        auto label_str = label_map(res.label_ids[i]);
+        if (label_str.empty() || res.scores[i] < 0.5)
+            continue;
+
+        writer.StartObject();
+        writer.Key("text");
+        writer.String(label_str.c_str());
+
+        writer.Key("score");
+        writer.Double(res.scores[i]);
+
+        writer.Key("position");
+        writer.StartArray();
+        writer.Int(res.boxes[i][0]);
+        writer.Int(res.boxes[i][1]);
+        writer.Int(res.boxes[i][2]);
+        writer.Int(res.boxes[i][1]);
+        writer.Int(res.boxes[i][2]);
+        writer.Int(res.boxes[i][3]);
+        writer.Int(res.boxes[i][0]);
+        writer.Int(res.boxes[i][3]);
+        writer.EndArray();
+        writer.EndObject();
+    }
+    writer.EndArray();
+    writer.EndObject();
+    return ss.GetString();
+}
+
+
+void sig_handler(int signo)
+{
+    wait_group.done();
+}
+
+void init(const char* path)
+{
+    if (config.load(path) == false)
+    {
+        perror("Load config failed");
+        exit(1);
+    }
+
+    fastdeploy::SetLogger(false, false);
+
+    auto option = fastdeploy::RuntimeOption();
+    option.UseGpu(gpu_id);
+
+    int model_count = config.get_model_count();
+    for(size_t i = 0; i < model_count; ++i) {
+        if (i > model_count / 2 && gpu_id == 0)
+        {
+            gpu_id = 1;
+            option.UseGpu(gpu_id);
+            auto* model =  new fastdeploy::vision::detection::PPYOLOER(config.get_det_model_path(), config.get_det_params_path(), config.get_det_cfg_path(), option);
+            model->Initialized();
+            det_models.emplace_back(model);
+            continue;
+        }
+        
+        if (det_models.empty()) {
+            auto* model =  new fastdeploy::vision::detection::PPYOLOER(config.get_det_model_path(), config.get_det_params_path(), config.get_det_cfg_path(), option);
+            model->Initialized();
+            det_models.emplace_back(model);
+        } else {
+            auto tmodel = std::move(det_models.back()->Clone());
+            det_models.emplace_back(reinterpret_cast<fastdeploy::vision::detection::PPYOLOER*>(tmodel.release()));
+        }
+    }
+    std::cout << "成功初始化 " << model_count << " 个模型!" << std::endl;
+    model_used.resize(model_count, 0);
+
+    signal(SIGINT, sig_handler);
+    signal(SIGTERM, sig_handler);
+}
+
+class ServiceImpl : public gpu_server::Service
+{
+public:
+    void Echo(EchoRequest *req, EchoResponse *resp, RPCContext *ctx) override
+    {
+        try {
+            auto get_valid_model = []() -> int {
+                while (true) {
+                    for (int i = 0; i < det_models.size(); ++i) {
+                        if (!model_used[i]) {
+                            std::unique_lock<std::mutex> lk(model_mtx);
+                            if (!model_used[i])
+                            {
+                                model_used[i] = 1;
+                                std::cout << "将使用第 " << i + 1 << " 个模型进行推理!" << std::endl;
+                                return i;
+                            }                            
+                        }
+                    }
+                }
+            };
+
+            const cv::Mat im = cv::imread(req->path());
+            if (!im.empty())
+            {
+                auto begin = std::chrono::high_resolution_clock::now();
+                auto model_index = get_valid_model();
+
+                fastdeploy::vision::DetectionResult res;
+                if (!det_models[model_index]->Predict(im, &res)) {
+                    std::cerr << "Failed to predict." << std::endl;
+                    return;
+                }
+                // 4. delete the following codes and fill your logic
+                {
+                    std::lock_guard<std::mutex> gd(model_mtx);
+                    model_used[model_index] = 0;
+                }
+                auto json_str = DetectionResultToJson(res);
+                res.Free();
+                resp->set_json(json_str.c_str());
+                auto end = std::chrono::high_resolution_clock::now();
+                auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin);
+                // std::cout << "推理耗时: << " << elapsed.count() * 1e-9 << " seconds.\n";
+            } else {
+                resp->set_json("{}");
+            }
+        } catch (const std::exception& e){
+            std::cout << "failed: " << e.what() << std::endl;
+        }
+    }
+};
+
+int main(int argc, char* argv[])
+{
+    if (argc < 2)
+    {
+        std::cout << "please input client config path." << std::endl;
+        return -1;
+    }
+
+    // 1. load config
+    init(argv[1]);
+
+    // 2. start server
+    SRPCServer server;
+    ServiceImpl impl;
+    server.add_service(&impl);
+
+    config.load_filter(server);
+
+    if (server.start(config.server_port()) == 0)
+    {
+        // 3. success and wait
+        fprintf(stderr, "gpu_server SRPC server started, port %u\n", config.server_port());
+        wait_group.wait();
+        server.stop();
+    }
+    else
+        perror("server start");
+
+    return 0;
+}

+ 426 - 0
FastExtract/src/server/server.srpc.h

@@ -0,0 +1,426 @@
+#pragma once
+#include <stdio.h>
+#include <string>
+#include "srpc/rpc_define.h"
+#include "server.pb.h"
+
+namespace gpu_server
+{
+
+/*
+ * Server codes
+ * Generated by SRPC
+ */
+
+class Service : public srpc::RPCService
+{
+public:
+	// please implement these methods in server.cc
+
+	virtual void Echo(EchoRequest *request, EchoResponse *response,
+					srpc::RPCContext *ctx) = 0;
+
+public:
+	Service();
+};
+
+/*
+ * Client codes
+ * Generated by SRPC
+ */
+
+using EchoDone = std::function<void (EchoResponse *, srpc::RPCContext *)>;
+
+class SRPCClient : public srpc::SRPCClient
+{
+public:
+	void Echo(const EchoRequest *req, EchoDone done);
+	void Echo(const EchoRequest *req, EchoResponse *resp, srpc::RPCSyncContext *sync_ctx);
+	WFFuture<std::pair<EchoResponse, srpc::RPCSyncContext>> async_Echo(const EchoRequest *req);
+
+public:
+	SRPCClient(const char *host, unsigned short port);
+	SRPCClient(const struct srpc::RPCClientParams *params);
+
+public:
+	srpc::SRPCClientTask *create_Echo_task(EchoDone done);
+};
+
+class SRPCHttpClient : public srpc::SRPCHttpClient
+{
+public:
+	void Echo(const EchoRequest *req, EchoDone done);
+	void Echo(const EchoRequest *req, EchoResponse *resp, srpc::RPCSyncContext *sync_ctx);
+	WFFuture<std::pair<EchoResponse, srpc::RPCSyncContext>> async_Echo(const EchoRequest *req);
+
+public:
+	SRPCHttpClient(const char *host, unsigned short port);
+	SRPCHttpClient(const struct srpc::RPCClientParams *params);
+
+public:
+	srpc::SRPCHttpClientTask *create_Echo_task(EchoDone done);
+};
+
+class BRPCClient : public srpc::BRPCClient
+{
+public:
+	void Echo(const EchoRequest *req, EchoDone done);
+	void Echo(const EchoRequest *req, EchoResponse *resp, srpc::RPCSyncContext *sync_ctx);
+	WFFuture<std::pair<EchoResponse, srpc::RPCSyncContext>> async_Echo(const EchoRequest *req);
+
+public:
+	BRPCClient(const char *host, unsigned short port);
+	BRPCClient(const struct srpc::RPCClientParams *params);
+
+public:
+	srpc::BRPCClientTask *create_Echo_task(EchoDone done);
+};
+
+class TRPCClient : public srpc::TRPCClient
+{
+public:
+	void Echo(const EchoRequest *req, EchoDone done);
+	void Echo(const EchoRequest *req, EchoResponse *resp, srpc::RPCSyncContext *sync_ctx);
+	WFFuture<std::pair<EchoResponse, srpc::RPCSyncContext>> async_Echo(const EchoRequest *req);
+
+public:
+	TRPCClient(const char *host, unsigned short port);
+	TRPCClient(const struct srpc::RPCClientParams *params);
+
+public:
+	srpc::TRPCClientTask *create_Echo_task(EchoDone done);
+};
+
+class TRPCHttpClient : public srpc::TRPCHttpClient
+{
+public:
+	void Echo(const EchoRequest *req, EchoDone done);
+	void Echo(const EchoRequest *req, EchoResponse *resp, srpc::RPCSyncContext *sync_ctx);
+	WFFuture<std::pair<EchoResponse, srpc::RPCSyncContext>> async_Echo(const EchoRequest *req);
+
+public:
+	TRPCHttpClient(const char *host, unsigned short port);
+	TRPCHttpClient(const struct srpc::RPCClientParams *params);
+
+public:
+	srpc::TRPCHttpClientTask *create_Echo_task(EchoDone done);
+};
+
+///// implements detials /////
+
+inline Service::Service(): srpc::RPCService("gpu_server")
+{
+	this->srpc::RPCService::add_method("Echo",
+		[this](srpc::RPCWorker& worker) ->int {
+			return ServiceRPCCallImpl(this, worker, &Service::Echo);
+		});
+}
+
+inline SRPCClient::SRPCClient(const char *host, unsigned short port):
+	srpc::SRPCClient("gpu_server")
+{
+	struct srpc::RPCClientParams params = srpc::RPC_CLIENT_PARAMS_DEFAULT;
+	
+	params.host = host;
+	params.port = port;
+	this->srpc::SRPCClient::init(&params);
+}
+
+inline SRPCClient::SRPCClient(const struct srpc::RPCClientParams *params):
+	srpc::SRPCClient("gpu_server")
+{
+	const struct srpc::RPCClientParams *temp = params;
+	struct srpc::RPCClientParams temp_params;
+	
+	this->srpc::SRPCClient::init(temp);
+}
+
+inline void SRPCClient::Echo(const EchoRequest *req, EchoDone done)
+{
+	auto *task = this->create_rpc_client_task("Echo", std::move(done));
+
+	task->serialize_input(req);
+	task->start();
+}
+
+inline void SRPCClient::Echo(const EchoRequest *req, EchoResponse *resp, srpc::RPCSyncContext *sync_ctx)
+{
+	auto res = this->async_Echo(req).get();
+
+	if (resp && res.second.success)
+		*resp = std::move(res.first);
+
+	if (sync_ctx)
+		*sync_ctx = std::move(res.second);
+}
+
+inline WFFuture<std::pair<EchoResponse, srpc::RPCSyncContext>> SRPCClient::async_Echo(const EchoRequest *req)
+{
+	using RESULT = std::pair<EchoResponse, srpc::RPCSyncContext>;
+	auto *pr = new WFPromise<RESULT>();
+	auto fr = pr->get_future();
+	auto *task = this->create_rpc_client_task<EchoResponse>("Echo", srpc::RPCAsyncFutureCallback<EchoResponse>);
+
+	task->serialize_input(req);
+	task->user_data = pr;
+	task->start();
+	return fr;
+}
+
+inline srpc::SRPCClientTask *SRPCClient::create_Echo_task(EchoDone done)
+{
+	return this->create_rpc_client_task("Echo", std::move(done));
+}
+
+inline SRPCHttpClient::SRPCHttpClient(const char *host, unsigned short port):
+	srpc::SRPCHttpClient("gpu_server")
+{
+	struct srpc::RPCClientParams params = srpc::RPC_CLIENT_PARAMS_DEFAULT;
+	
+	params.host = host;
+	params.port = port;
+	this->srpc::SRPCHttpClient::init(&params);
+}
+
+inline SRPCHttpClient::SRPCHttpClient(const struct srpc::RPCClientParams *params):
+	srpc::SRPCHttpClient("gpu_server")
+{
+	const struct srpc::RPCClientParams *temp = params;
+	struct srpc::RPCClientParams temp_params;
+	
+	this->srpc::SRPCHttpClient::init(temp);
+}
+
+inline void SRPCHttpClient::Echo(const EchoRequest *req, EchoDone done)
+{
+	auto *task = this->create_rpc_client_task("Echo", std::move(done));
+
+	task->serialize_input(req);
+	task->start();
+}
+
+inline void SRPCHttpClient::Echo(const EchoRequest *req, EchoResponse *resp, srpc::RPCSyncContext *sync_ctx)
+{
+	auto res = this->async_Echo(req).get();
+
+	if (resp && res.second.success)
+		*resp = std::move(res.first);
+
+	if (sync_ctx)
+		*sync_ctx = std::move(res.second);
+}
+
+inline WFFuture<std::pair<EchoResponse, srpc::RPCSyncContext>> SRPCHttpClient::async_Echo(const EchoRequest *req)
+{
+	using RESULT = std::pair<EchoResponse, srpc::RPCSyncContext>;
+	auto *pr = new WFPromise<RESULT>();
+	auto fr = pr->get_future();
+	auto *task = this->create_rpc_client_task<EchoResponse>("Echo", srpc::RPCAsyncFutureCallback<EchoResponse>);
+
+	task->serialize_input(req);
+	task->user_data = pr;
+	task->start();
+	return fr;
+}
+
+inline srpc::SRPCHttpClientTask *SRPCHttpClient::create_Echo_task(EchoDone done)
+{
+	return this->create_rpc_client_task("Echo", std::move(done));
+}
+
+inline BRPCClient::BRPCClient(const char *host, unsigned short port):
+	srpc::BRPCClient("gpu_server")
+{
+	struct srpc::RPCClientParams params = srpc::RPC_CLIENT_PARAMS_DEFAULT;
+	
+	params.host = host;
+	params.port = port;
+	this->srpc::BRPCClient::init(&params);
+}
+
+inline BRPCClient::BRPCClient(const struct srpc::RPCClientParams *params):
+	srpc::BRPCClient("gpu_server")
+{
+	const struct srpc::RPCClientParams *temp = params;
+	struct srpc::RPCClientParams temp_params;
+	
+	this->srpc::BRPCClient::init(temp);
+}
+
+inline void BRPCClient::Echo(const EchoRequest *req, EchoDone done)
+{
+	auto *task = this->create_rpc_client_task("Echo", std::move(done));
+
+	task->serialize_input(req);
+	task->start();
+}
+
+inline void BRPCClient::Echo(const EchoRequest *req, EchoResponse *resp, srpc::RPCSyncContext *sync_ctx)
+{
+	auto res = this->async_Echo(req).get();
+
+	if (resp && res.second.success)
+		*resp = std::move(res.first);
+
+	if (sync_ctx)
+		*sync_ctx = std::move(res.second);
+}
+
+inline WFFuture<std::pair<EchoResponse, srpc::RPCSyncContext>> BRPCClient::async_Echo(const EchoRequest *req)
+{
+	using RESULT = std::pair<EchoResponse, srpc::RPCSyncContext>;
+	auto *pr = new WFPromise<RESULT>();
+	auto fr = pr->get_future();
+	auto *task = this->create_rpc_client_task<EchoResponse>("Echo", srpc::RPCAsyncFutureCallback<EchoResponse>);
+
+	task->serialize_input(req);
+	task->user_data = pr;
+	task->start();
+	return fr;
+}
+
+inline srpc::BRPCClientTask *BRPCClient::create_Echo_task(EchoDone done)
+{
+	return this->create_rpc_client_task("Echo", std::move(done));
+}
+
+inline TRPCClient::TRPCClient(const char *host, unsigned short port):
+	srpc::TRPCClient("gpu_server")
+{
+	struct srpc::RPCClientParams params = srpc::RPC_CLIENT_PARAMS_DEFAULT;
+	
+	params.host = host;
+	params.port = port;
+	this->srpc::TRPCClient::init(&params);
+}
+
+inline TRPCClient::TRPCClient(const struct srpc::RPCClientParams *params):
+	srpc::TRPCClient("gpu_server")
+{
+	const struct srpc::RPCClientParams *temp = params;
+	struct srpc::RPCClientParams temp_params;
+	
+	this->srpc::TRPCClient::init(temp);
+}
+
+inline void TRPCClient::Echo(const EchoRequest *req, EchoDone done)
+{
+	auto *task = this->create_rpc_client_task("/gpu_server/Echo", std::move(done));
+
+	if (this->params.callee_timeout >= 0)
+		task->get_req()->set_callee_timeout(this->params.callee_timeout);
+	if (!this->params.caller.empty())
+		task->get_req()->set_caller_name(this->params.caller);
+	task->serialize_input(req);
+	task->start();
+}
+
+inline void TRPCClient::Echo(const EchoRequest *req, EchoResponse *resp, srpc::RPCSyncContext *sync_ctx)
+{
+	auto res = this->async_Echo(req).get();
+
+	if (resp && res.second.success)
+		*resp = std::move(res.first);
+
+	if (sync_ctx)
+		*sync_ctx = std::move(res.second);
+}
+
+inline WFFuture<std::pair<EchoResponse, srpc::RPCSyncContext>> TRPCClient::async_Echo(const EchoRequest *req)
+{
+	using RESULT = std::pair<EchoResponse, srpc::RPCSyncContext>;
+	auto *pr = new WFPromise<RESULT>();
+	auto fr = pr->get_future();
+	auto *task = this->create_rpc_client_task<EchoResponse>("/gpu_server/Echo", srpc::RPCAsyncFutureCallback<EchoResponse>);
+
+	if (this->params.callee_timeout >= 0)
+		task->get_req()->set_callee_timeout(this->params.callee_timeout);
+	if (!this->params.caller.empty())
+		task->get_req()->set_caller_name(this->params.caller);
+	task->serialize_input(req);
+	task->user_data = pr;
+	task->start();
+	return fr;
+}
+
+inline srpc::TRPCClientTask *TRPCClient::create_Echo_task(EchoDone done)
+{
+	auto *task = this->create_rpc_client_task("/gpu_server/Echo", std::move(done));
+
+	if (!this->params.caller.empty())
+		task->get_req()->set_caller_name(this->params.caller);
+
+	return task;
+}
+
+inline TRPCHttpClient::TRPCHttpClient(const char *host, unsigned short port):
+	srpc::TRPCHttpClient("gpu_server")
+{
+	struct srpc::RPCClientParams params = srpc::RPC_CLIENT_PARAMS_DEFAULT;
+	
+	params.host = host;
+	params.port = port;
+	this->srpc::TRPCHttpClient::init(&params);
+}
+
+inline TRPCHttpClient::TRPCHttpClient(const struct srpc::RPCClientParams *params):
+	srpc::TRPCHttpClient("gpu_server")
+{
+	const struct srpc::RPCClientParams *temp = params;
+	struct srpc::RPCClientParams temp_params;
+	
+	this->srpc::TRPCHttpClient::init(temp);
+}
+
+inline void TRPCHttpClient::Echo(const EchoRequest *req, EchoDone done)
+{
+	auto *task = this->create_rpc_client_task("Echo", std::move(done));
+
+	if (this->params.callee_timeout >= 0)
+		task->get_req()->set_callee_timeout(this->params.callee_timeout);
+	if (!this->params.caller.empty())
+		task->get_req()->set_caller_name(this->params.caller);
+	task->serialize_input(req);
+	task->start();
+}
+
+inline void TRPCHttpClient::Echo(const EchoRequest *req, EchoResponse *resp, srpc::RPCSyncContext *sync_ctx)
+{
+	auto res = this->async_Echo(req).get();
+
+	if (resp && res.second.success)
+		*resp = std::move(res.first);
+
+	if (sync_ctx)
+		*sync_ctx = std::move(res.second);
+}
+
+inline WFFuture<std::pair<EchoResponse, srpc::RPCSyncContext>> TRPCHttpClient::async_Echo(const EchoRequest *req)
+{
+	using RESULT = std::pair<EchoResponse, srpc::RPCSyncContext>;
+	auto *pr = new WFPromise<RESULT>();
+	auto fr = pr->get_future();
+	auto *task = this->create_rpc_client_task<EchoResponse>("Echo", srpc::RPCAsyncFutureCallback<EchoResponse>);
+
+	if (this->params.callee_timeout >= 0)
+		task->get_req()->set_callee_timeout(this->params.callee_timeout);
+	if (!this->params.caller.empty())
+		task->get_req()->set_caller_name(this->params.caller);
+	task->serialize_input(req);
+	task->user_data = pr;
+	task->start();
+	return fr;
+}
+
+inline srpc::TRPCHttpClientTask *TRPCHttpClient::create_Echo_task(EchoDone done)
+{
+	auto *task = this->create_rpc_client_task("Echo", std::move(done));
+
+	if (!this->params.caller.empty())
+		task->get_req()->set_caller_name(this->params.caller);
+
+	return task;
+}
+
+} // end namespace gpu_server
+

+ 18 - 0
FastExtract/src/util/CMakeLists.txt

@@ -0,0 +1,18 @@
+cmake_minimum_required(VERSION 3.10)
+project(util)
+
+set(SRC
+	json/Json.cc
+	server_config/config.cc
+	client_config/config.cc
+)
+
+set(LIB ${Srpc_LIB} ${Workflow_LIB} pthread OpenSSL::SSL OpenSSL::Crypto
+    protobuf z ${SNAPPY_LIB} ${LZ4_LIB})
+
+include_directories(${OPENSSL_INCLUDE_DIR} ${Protobuf_INCLUDE_DIR}
+                    ${WORKFLOW_INCLUDE_DIR} ${SRPC_INCLUDE_DIR}
+                    ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
+
+add_library(${PROJECT_NAME} OBJECT ${SRC})
+target_link_libraries(${PROJECT_NAME} ${LIB})

+ 342 - 0
FastExtract/src/util/client_config/config.cc

@@ -0,0 +1,342 @@
+#include <fstream>
+#include "config.h"
+#include "workflow/WFGlobal.h"
+#include "workflow/UpstreamManager.h"
+#include "workflow/UpstreamPolicies.h"
+
+using namespace srpc;
+
+// default upstream_route_t
+
+static unsigned int default_consistent_hash(const char *path, const char *query,
+                                              const char *fragment)
+{
+    return 0;
+}
+
+static unsigned int default_select_route(const char *path, const char *query,
+                                         const char *fragment)
+{
+    return 0;
+}
+
+static void set_endpoint_params(const wfrest::Json& data,
+                                struct EndpointParams *params)
+{
+    *params = ENDPOINT_PARAMS_DEFAULT;
+
+    for (const auto& it : data)
+    {
+        if (it.key() == "max_connections")
+            params->max_connections = data["max_connections"];
+        else if (it.key() == "connect_timeout")
+            params->connect_timeout = data["connect_timeout"];
+        else if (it.key() == "response_timeout")
+            params->response_timeout = data["response_timeout"];
+        else if (it.key() == "ssl_connect_timeout")
+            params->ssl_connect_timeout = data["ssl_connect_timeout"];
+        else if (it.key() == "use_tls_sni")
+            params->use_tls_sni = data["use_tls_sni"];
+        else
+        {
+            printf("[INFO][set_endpoint_params] Unknown key : %s\n",
+                   it.key().c_str());
+        }
+    }
+}
+
+static void load_global(const wfrest::Json& data)
+{
+    struct WFGlobalSettings settings = GLOBAL_SETTINGS_DEFAULT;
+    std::string resolv_conf_path;
+    std::string hosts_path;
+
+    for (const auto& it : data)
+    {
+        if (it.key() == "endpoint_params")
+        {
+            set_endpoint_params(data["endpoint_params"],
+                                &settings.endpoint_params);
+        }
+        else if (it.key() == "dns_server_params")
+        {
+            set_endpoint_params(data["dns_server_params"],
+                                &settings.dns_server_params);
+        }
+        else if (it.key() == "dns_ttl_default")
+            settings.dns_ttl_default = data["dns_ttl_default"];
+        else if (it.key() == "dns_ttl_min")
+            settings.dns_ttl_min = data["dns_ttl_min"];
+        else if (it.key() == "dns_threads")
+            settings.dns_threads = data["dns_threads"];
+        else if (it.key() == "poller_threads")
+            settings.poller_threads = data["poller_threads"];
+        else if (it.key() == "handler_threads")
+            settings.handler_threads = data["handler_threads"];
+        else if (it.key() == "compute_threads")
+            settings.compute_threads = data["compute_threads"];
+        else if (it.key() == "resolv_conf_path")
+        {
+            resolv_conf_path = data["resolv_conf_path"].get<std::string>();
+            settings.resolv_conf_path = resolv_conf_path.c_str();
+        }
+        else if (it.key() == "hosts_path")
+        {
+            hosts_path = data["hosts_path"].get<std::string>();
+            settings.hosts_path = hosts_path.c_str();
+        }
+        else
+            printf("[INFO][load_global] Unknown key : %s\n", it.key().c_str());
+    }
+
+    WORKFLOW_library_init(&settings);
+}
+
+static bool load_upstream_server(const wfrest::Json& data,
+                                 std::vector<std::string>& hosts,
+                                 std::vector<AddressParams>& params)
+{
+    AddressParams param;
+
+    hosts.clear();
+    params.clear();
+
+    for (const auto& server : data)
+    {
+        if (server.has("host") == false)
+        {
+            printf("[ERROR][load_upstream] Invalid upstream server\n");
+            continue;
+        }
+
+        param = ADDRESS_PARAMS_DEFAULT;
+        if (server.has("params"))
+        {
+            for (const auto& p : server["params"])
+            {
+                if (p.key() == "endpoint_params")
+                    set_endpoint_params(p.value(), &param.endpoint_params);
+                else if (p.key() == "weight")
+                    param.weight = p.value().get<unsigned short>();
+                else if (p.key() == "max_fails")
+                    param.max_fails = p.value().get<unsigned int>();
+                else if (p.key() == "dns_ttl_default")
+                    param.dns_ttl_default = p.value().get<unsigned int>();
+                else if (p.key() == "dns_ttl_min")
+                    param.dns_ttl_min = p.value().get<unsigned int>();
+                else if (p.key() == "server_type")
+                    param.server_type = p.value().get<int>();
+                else if (p.key() == "group_id")
+                    param.group_id = p.value().get<int>();
+                else
+                    printf("[ERROR][load_upstream] Invalid params: %s\n",
+                           p.key().c_str());
+            }
+        }
+
+        hosts.push_back(server["host"]);
+        params.push_back(param);
+    }
+    
+    if (hosts.size() == 0)
+        return false;
+    else
+        return true;
+}
+
+static void load_upstream(const wfrest::Json& data)
+{
+    std::string name;
+    std::string type;
+    bool try_another;
+    std::vector<std::string> hosts;
+    std::vector<AddressParams> params;
+
+    for (const auto& it : data)
+    {
+        if (it.has("name") == false ||
+            it.has("type") == false ||
+            it.has("server") == false || 
+            load_upstream_server(it["server"], hosts, params) == false)
+        {
+            printf("[ERROR][load_upstream] Invalid upstream\n");
+            continue;
+        }
+
+        name = it["name"].get<std::string>();
+        type = it["type"].get<std::string>();
+
+        if (it.has("try_another"))
+            try_another = it["try_another"];
+        else
+            try_another = false;
+
+        if (type == "weighted_random")
+        {
+            UpstreamManager::upstream_create_weighted_random(name, try_another);
+        }
+        else if (type == "consistent_hash")
+        {
+            UpstreamManager::upstream_create_consistent_hash(name,
+                                                    default_consistent_hash);
+        }
+        else if (type == "round_robin")
+        {
+            UpstreamManager::upstream_create_round_robin(name, try_another);
+        }
+        else if (type == "manual")
+        {
+            UpstreamManager::upstream_create_manual(name,
+                                                    default_select_route,
+                                                    try_another,
+                                                    default_consistent_hash);
+        }
+        else if (type == "vnswrr")
+        {
+            UpstreamManager::upstream_create_vnswrr(name);
+        }
+        else
+        {
+            printf("[INFO][load_upstream] Unknown type : %s\n", type.c_str());
+            continue;
+        }
+
+        for (size_t i = 0; i < hosts.size(); i++)
+            UpstreamManager::upstream_add_server(name, hosts[i], &params[i]);
+    }
+}
+
+void ClientRPCConfig::load_server()
+{
+    if (this->data["server"].has("port"))
+        this->s_port = this->data["server"]["port"];
+
+    if (this->data["server"].has("root"))
+        this->root_path = this->data["server"]["root"].get<std::string>();
+
+    if (this->data["server"].has("cert_file"))
+        this->s_cert_file = this->data["server"]["cert_file"].get<std::string>();
+
+    if (this->data["server"].has("file_key"))
+        this->s_file_key = this->data["server"]["file_key"].get<std::string>();
+
+    if (this->data["server"].has("error_page"))
+    {
+        for (const auto& it : this->data["server"]["error_page"])
+        {
+            std::string page;
+
+            if (it.has("error") == true && it.has("error") == true)
+            {
+                page = it["page"].get<std::string>();
+                for (const auto& e : it["error"])
+                    this->error_page.insert(std::make_pair(e.get<int>(), page));
+            }
+            else
+            {
+                printf("[ERROR][load_file_service] Invalid error_page\n");
+                continue;
+            }
+        }
+    }
+}
+
+void ClientRPCConfig::load_client()
+{
+    if (this->data["client"].has("remote_host"))
+        this->c_host = this->data["client"]["remote_host"].get<std::string>();
+
+    if (this->data["client"].has("remote_port"))
+        this->c_port = this->data["client"]["remote_port"];
+
+    if (this->data["client"].has("redirect_max"))
+        this->c_redirect_max = this->data["client"]["redirect_max"];
+
+    if (this->data["client"].has("retry_max"))
+        this->c_retry_max = this->data["client"]["retry_max"];
+
+    if (this->data["client"].has("user_name"))
+        this->c_user_name = this->data["client"]["user_name"].get<std::string>();
+
+    if (this->data["client"].has("password"))
+        this->c_password = this->data["client"]["password"].get<std::string>();
+}
+
+void ClientRPCConfig::load_conversion()
+{
+    if (this->data["conversion"].has("pdf_root_path"))
+        this->pdf_root_path = this->data["conversion"]["pdf_root_path"].get<std::string>();
+
+    if (this->data["conversion"].has("conversion_resource_path"))
+        this->conversion_resource_path = this->data["conversion"]["conversion_resource_path"].get<std::string>();
+
+    if (this->data["conversion"].has("conversion_instance"))
+        this->conversion_instance = this->data["conversion"]["conversion_instance"].get<std::string>();
+
+    if (this->data["conversion"].has("conversion_type"))
+        this->conversion_type = this->data["conversion"]["conversion_type"].get<std::string>();
+
+    if (this->data["conversion"].has("output_path"))
+        this->output_path = this->data["conversion"]["output_path"].get<std::string>();
+
+    if (this->data["conversion"].has("model_path"))
+        this->model_path = this->data["conversion"]["model_path"].get<std::string>();
+
+    if (this->data["conversion"].has("outer_batch_size"))
+        this->outer_batch_size = this->data["conversion"]["outer_batch_size"];
+
+    if (this->data["conversion"].has("inner_batch_size"))
+        this->inner_batch_size = this->data["conversion"]["inner_batch_size"];
+}
+
+bool ClientRPCConfig::load(const char *file)
+{
+    FILE *fp = fopen(file, "r");
+    if (!fp)
+        return false;
+
+    this->data = wfrest::Json::parse(fp);
+    fclose(fp);
+    
+    if (this->data.is_valid() == false)
+        return false;
+
+    for (const auto& it : this->data)
+    {
+        if (it.key() == "server")
+            this->load_server();
+        else if (it.key() == "client")
+            this->load_client();
+        else if (it.key() == "conversion")
+            this->load_conversion();
+        else if (it.key() == "global")
+            load_global(it.value());
+        else if (it.key() == "upstream")
+            load_upstream(it.value());
+        else
+            printf("[INFO][ClientRPCConfig::load] Unknown key: %s\n", it.key().c_str());
+    }
+
+    // 获取当前时间点
+    auto now = std::chrono::system_clock::now();
+    // 转换为时间戳
+    auto now_c = std::chrono::system_clock::to_time_t(now);
+    
+    // 创建tm结构体实例
+    std::tm local_tm = *std::localtime(&now_c);
+
+    // 缓冲区用于存储格式化后的字符串
+    char buffer[20];
+    // 使用asctime_r函数按特定格式填充缓冲区,但这个函数不支持我们所需的格式,需要进一步处理
+    std::strftime(buffer, sizeof(buffer), "%Y%m%d_%H_%M_%S", &local_tm);
+    time_stamp = buffer;
+    time_stamp.erase(0, 2);
+    time_stamp.push_back('c');
+
+    return true;
+};
+
+ClientRPCConfig::~ClientRPCConfig()
+{
+}
+

+ 83 - 0
FastExtract/src/util/client_config/config.h

@@ -0,0 +1,83 @@
+#ifndef _RPC_CONFIG_H_
+#define _RPC_CONFIG_H_
+
+#include <string>
+#include <vector>
+#include <unordered_map>
+#include "json/Json.h"
+
+namespace srpc
+{
+
+class ClientRPCConfig
+{
+public:
+    using ErrorPageMap = std::unordered_map<int, std::string>;
+
+    bool load(const char *file);
+
+    unsigned short server_port() const { return this->s_port; }
+    const char *server_cert_file() const { return this->s_cert_file.c_str(); }
+    const char *server_file_key() const { return this->s_file_key.c_str(); }
+    unsigned short client_port() const { return this->c_port; }
+    const char *client_host() const { return this->c_host.c_str(); }
+    int redirect_max() const { return this->c_redirect_max; }
+    int retry_max() const { return this->c_retry_max; }
+    const char *client_user_name() const { return this->c_user_name.c_str(); }
+    const char *client_password() const { return this->c_password.c_str(); }
+    const char *get_root_path() const { return this->root_path.c_str(); }
+    const ErrorPageMap& get_error_page() const { return this->error_page; }
+
+    const char* get_conversion_instance() { return conversion_instance.c_str(); }
+    const char* get_conversion_type() { return conversion_type.c_str(); }
+    const char* get_conversion_resource_path() { return conversion_resource_path.c_str(); }
+    const char* get_output_path() { return output_path.c_str(); }
+    const char* get_model_path() { return model_path.c_str(); }
+    const char* get_pdf_root_path() { return pdf_root_path.c_str(); }
+    const char* get_time_stamp() { return time_stamp.c_str(); }
+
+    void add_http_put_count() { http_put_count++; }
+    void add_http_get_count() { http_get_count++; }
+    int get_http_put_count() { return http_put_count; }
+    int get_http_get_count() { return http_get_count; }
+    int get_outer_batch_size() { return outer_batch_size; }
+    int get_inner_batch_size() { return inner_batch_size; }
+
+public:
+    ClientRPCConfig() : s_port(0), c_port(0), c_redirect_max(0), c_retry_max(0) { }
+    ~ClientRPCConfig();
+
+private:
+    void load_server();
+    void load_client();
+    void load_conversion();
+
+    wfrest::Json data;
+    unsigned short s_port;
+    std::string s_cert_file;
+    std::string s_file_key;
+    std::string c_host;
+    unsigned short c_port;
+    int c_redirect_max;
+    int c_retry_max;
+    std::string c_user_name;
+    std::string c_password;
+    std::string root_path;
+    ErrorPageMap error_page;
+
+    int http_get_count = 0;
+    int http_put_count = 0;
+    int outer_batch_size;
+    int inner_batch_size;
+    std::string pdf_root_path;
+    std::string conversion_resource_path;
+    std::string conversion_instance;
+    std::string conversion_type;
+    std::string output_path;
+    std::string model_path;
+    std::string time_stamp;
+};
+
+}
+
+#endif

+ 35 - 0
FastExtract/src/util/client_config/util.h

@@ -0,0 +1,35 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "workflow/WFTaskFactory.h"
+
+template <class TASK>
+void print_peer_address(TASK *server_task)
+{
+    char addrstr[128];
+    struct sockaddr_storage addr;
+    socklen_t l = sizeof addr;
+    unsigned short port = 0;
+    long long seq = server_task->get_task_seq();
+
+    server_task->get_peer_addr((struct sockaddr *)&addr, &l);
+    if (addr.ss_family == AF_INET)
+    {
+        struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
+        inet_ntop(AF_INET, &sin->sin_addr, addrstr, 128);
+        port = ntohs(sin->sin_port);
+    }
+    else if (addr.ss_family == AF_INET6)
+    {
+        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr;
+        inet_ntop(AF_INET6, &sin6->sin6_addr, addrstr, 128);
+        port = ntohs(sin6->sin6_port);
+    }
+    else
+        strcpy(addrstr, "Unknown");
+
+    fprintf(stderr, "peer address: %s:%d, seq: %lld.\n", addrstr, port, seq);
+}
+

Разлика између датотеке није приказан због своје велике величине
+ 1056 - 0
FastExtract/src/util/json/Json.cc


+ 898 - 0
FastExtract/src/util/json/Json.h

@@ -0,0 +1,898 @@
+#ifndef WFREST_JSON_H_
+#define WFREST_JSON_H_
+
+#include "workflow/json_parser.h"
+#include <algorithm>
+#include <cassert>
+#include <cstdio>
+#include <fstream>
+#include <functional>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <initializer_list>
+
+namespace wfrest
+{
+
+namespace detail
+{
+
+template <typename T>
+struct is_string
+{
+    static constexpr bool value = false;
+};
+
+template <class T, class Traits, class Alloc>
+struct is_string<std::basic_string<T, Traits, Alloc>>
+{
+    static constexpr bool value = true;
+};
+
+template <typename C>
+struct is_char
+    : std::integral_constant<bool, std::is_same<C, char>::value ||
+                                       std::is_same<C, char16_t>::value ||
+                                       std::is_same<C, char32_t>::value ||
+                                       std::is_same<C, wchar_t>::value>
+{
+};
+
+template <typename T>
+struct is_number
+    : std::integral_constant<bool, std::is_arithmetic<T>::value &&
+                                       !std::is_same<T, bool>::value &&
+                                       !detail::is_char<T>::value>
+{
+};
+
+} // namespace detail
+
+class Object_S;
+class Array_S;
+
+class Json
+{
+public:
+    using Object = Object_S;
+    using Array = Array_S;
+
+public:
+    static Json parse(const std::string& str);
+    static Json parse(const std::ifstream& stream);
+    static Json parse(FILE* fp);
+
+    std::string dump() const;
+    std::string dump(int spaces) const;
+
+    // for correctness in clang
+    Json operator[](const char* key);
+
+    Json operator[](const char* key) const;
+
+    Json operator[](const std::string& key);
+
+    Json operator[](const std::string& key) const;
+
+    Json operator[](int index);
+
+    Json operator[](int index) const;
+
+    template <typename T>
+    void operator=(const T& val)
+    {
+        if (parent_ == nullptr)
+            return;
+        if (json_value_type(parent_) == JSON_VALUE_ARRAY)
+        {
+            update_arr(val);
+        }
+        else if (json_value_type(parent_) == JSON_VALUE_OBJECT)
+        {
+            push_back_obj(parent_key_, val);
+        }
+    }
+
+    bool has(const std::string& key) const;
+
+    template <typename T>
+    typename std::enable_if<std::is_same<T, bool>::value, T>::type get() const
+    {
+        return json_value_type(node_) == JSON_VALUE_TRUE ? true : false;
+    }
+
+    template <typename T>
+    typename std::enable_if<detail::is_number<T>::value, T>::type get() const
+    {
+        return static_cast<T>(json_value_number(node_));
+    }
+
+    template <typename T>
+    typename std::enable_if<detail::is_string<T>::value, T>::type get() const
+    {
+        return std::string(json_value_string(node_));
+    }
+
+    template <typename T>
+    typename std::enable_if<std::is_same<T, Object>::value, T>::type
+    get() const;
+
+    template <typename T>
+    typename std::enable_if<std::is_same<T, Array>::value, T>::type get() const;
+
+    template <typename T>
+    typename std::enable_if<std::is_same<T, std::nullptr_t>::value, T>::type
+    get() const
+    {
+        return nullptr;
+    }
+
+    template <typename T>
+    operator T()
+    {
+        return get<T>();
+    }
+
+public:
+    int type() const
+    {
+        return json_value_type(node_);
+    }
+
+    std::string type_str() const;
+
+    bool is_null() const
+    {
+        return type() == JSON_VALUE_NULL;
+    }
+
+    bool is_number() const
+    {
+        return type() == JSON_VALUE_NUMBER;
+    }
+
+    bool is_boolean() const
+    {
+        int type = this->type();
+        return type == JSON_VALUE_TRUE || type == JSON_VALUE_FALSE;
+    }
+
+    bool is_object() const
+    {
+        return type() == JSON_VALUE_OBJECT;
+    }
+
+    bool is_array() const
+    {
+        return type() == JSON_VALUE_ARRAY;
+    }
+
+    bool is_string() const
+    {
+        return type() == JSON_VALUE_STRING;
+    }
+
+    bool is_valid() const
+    {
+        return node_ != nullptr;
+    }
+
+    int size() const;
+
+    bool empty() const;
+
+    void clear();
+
+    Json copy() const;
+
+    std::string key() const
+    {
+        return parent_key_;
+    }
+
+    const Json& value() const
+    {
+        return *this;
+    }
+
+public:
+    // for object
+    template <typename T, typename std::enable_if<detail::is_number<T>::value,
+                                                  bool>::type = true>
+    void push_back(const std::string& key, const T& val)
+    {
+        if (is_placeholder())
+        {
+            *this = Json::Object{{key, val}};
+            return;
+        }
+        if (!can_obj_push_back())
+        {
+            return;
+        }
+        json_object_t* obj = json_value_object(node_);
+        json_object_append(obj, key.c_str(), JSON_VALUE_NUMBER,
+                           static_cast<double>(val));
+    }
+
+    template <typename T,
+              typename std::enable_if<std::is_same<T, Object>::value ||
+                                          std::is_same<T, Array>::value,
+                                      bool>::type = true>
+    void push_back(const std::string& key, const T& val)
+    {
+        if (is_placeholder())
+        {
+            *this = Json::Object{{key, val}};
+            return;
+        }
+        if (!can_obj_push_back())
+        {
+            return;
+        }
+        json_object_t* obj = json_value_object(node_);
+        Json copy_json = val.copy();
+        json_object_append(obj, key.c_str(), 0, copy_json.node_);
+        copy_json.reset();
+    }
+
+    void push_back(const std::string& key, bool val);
+    void push_back(const std::string& key, std::nullptr_t val);
+    void push_back(const std::string& key, const std::string& val);
+    void push_back(const std::string& key, const char* val);
+    void push_back(const std::string& key, const std::vector<std::string>& val);
+    void push_back(const std::string& key, const std::initializer_list<std::string>& val)
+    {
+        push_back(key, std::vector<std::string>(val));
+    }
+    void push_back(const std::string& key, const Json& val);
+
+private:
+    // for object
+    template <typename T>
+    void push_back_obj(const std::string& key, const T& val)
+    {
+        if (!can_obj_push_back())
+        {
+            return;
+        }
+        if (is_placeholder())
+        {
+            placeholder_push_back(key, val);
+        }
+        else
+        {
+            normal_push_back(key, val);
+        }
+    }
+
+    template <typename T, typename std::enable_if<detail::is_number<T>::value,
+                                                  bool>::type = true>
+    void placeholder_push_back(const std::string& key, const T& val)
+    {
+        json_object_t* obj = json_value_object(parent_);
+        destroy_node(&node_);
+        node_ = const_cast<json_value_t*>(json_object_append(
+            obj, key.c_str(), JSON_VALUE_NUMBER, static_cast<double>(val)));
+    }
+
+    template <typename T,
+              typename std::enable_if<std::is_same<T, Object>::value ||
+                                          std::is_same<T, Array>::value,
+                                      bool>::type = true>
+    void placeholder_push_back(const std::string& key, const T& val)
+    {
+        json_object_t* obj = json_value_object(parent_);
+        destroy_node(&node_);
+        Json copy_json = val.copy();
+        node_ = const_cast<json_value_t*>(
+            json_object_append(obj, key.c_str(), 0, copy_json.node_));
+        copy_json.reset();
+    }
+
+    void placeholder_push_back(const std::string& key, bool val);
+    void placeholder_push_back(const std::string& key, std::nullptr_t val);
+    void placeholder_push_back(const std::string& key, const std::string& val);
+    void placeholder_push_back(const std::string& key, const char* val);
+    void placeholder_push_back(const std::string& key,
+                               const std::vector<std::string>& val);
+    void placeholder_push_back(const std::string& key, const Json& val);
+
+    template <typename T, typename std::enable_if<detail::is_number<T>::value,
+                                                  bool>::type = true>
+    void normal_push_back(const std::string& key, const T& val)
+    {
+        json_object_t* obj = json_value_object(parent_);
+        const json_value_t* find = json_object_find(key.c_str(), obj);
+        if (find == nullptr)
+        {
+            json_object_append(obj, key.c_str(), JSON_VALUE_NUMBER,
+                               static_cast<double>(val));
+            return;
+        }
+        json_object_insert_before(find, obj, key.c_str(), JSON_VALUE_NUMBER,
+                                  static_cast<double>(val));
+        json_value_t* remove_val = json_object_remove(find, obj);
+        json_value_destroy(remove_val);
+    }
+
+    template <typename T,
+              typename std::enable_if<std::is_same<T, Object>::value ||
+                                          std::is_same<T, Array>::value,
+                                      bool>::type = true>
+    void normal_push_back(const std::string& key, const T& val)
+    {
+        json_object_t* obj = json_value_object(parent_);
+        const json_value_t* find = json_object_find(key.c_str(), obj);
+        Json copy_json = val.copy();
+        if (find == nullptr)
+        {
+            json_object_append(obj, key.c_str(), 0, copy_json.node_);
+            copy_json.reset();
+            return;
+        }
+        json_object_insert_before(find, obj, key.c_str(), 0, copy_json.node_);
+        copy_json.reset();
+        json_value_t* remove_val = json_object_remove(find, obj);
+        json_value_destroy(remove_val);
+    }
+
+    void normal_push_back(const std::string& key, bool val);
+    void normal_push_back(const std::string& key, std::nullptr_t val);
+    void normal_push_back(const std::string& key, const std::string& val);
+    void normal_push_back(const std::string& key, const char* val);
+    void normal_push_back(const std::string& key,
+                          const std::vector<std::string>& val);
+    void normal_push_back(const std::string& key, const Json& val);
+
+public:
+    void erase(const std::string& key);
+
+    template <typename T, typename std::enable_if<detail::is_number<T>::value,
+                                                  bool>::type = true>
+    void push_back(T val)
+    {
+        if (is_placeholder())
+        {
+            *this = Json::Array{{val}};
+            return;
+        }
+        if (!can_arr_push_back())
+        {
+            return;
+        }
+        json_array_t* arr = json_value_array(node_);
+        json_array_append(arr, JSON_VALUE_NUMBER, static_cast<double>(val));
+    }
+
+    template <typename T,
+              typename std::enable_if<std::is_same<T, Json::Object>::value ||
+                                          std::is_same<T, Json::Array>::value,
+                                      bool>::type = true>
+    void push_back(const T& val)
+    {
+        if (is_placeholder())
+        {
+            *this = Json::Array{{val}};
+            return;
+        }
+        if (!can_arr_push_back())
+        {
+            return;
+        }
+        json_array_t* arr = json_value_array(node_);
+        Json copy_json = val.copy();
+        json_array_append(arr, 0, copy_json.node_);
+        copy_json.reset();
+    }
+
+    void push_back(bool val);
+    void push_back(std::nullptr_t val);
+    void push_back(const std::string& val);
+    void push_back(const char* val);
+    void push_back(const std::vector<std::string>& val);
+    void push_back(const std::initializer_list<std::string>& val)
+    {
+        push_back(std::vector<std::string>(val));
+    }
+    void push_back(const Json& val);
+
+    void erase(int index);
+
+private:
+    template <typename T, typename std::enable_if<detail::is_number<T>::value,
+                                                  bool>::type = true>
+    void update_arr(T val)
+    {
+        json_array_t* arr = json_value_array(parent_);
+        json_array_insert_before(node_, arr, JSON_VALUE_NUMBER,
+                                 static_cast<double>(val));
+        json_value_t* remove_val = json_array_remove(node_, arr);
+        json_value_destroy(remove_val);
+    }
+
+    template <typename T,
+              typename std::enable_if<std::is_same<T, Json::Object>::value ||
+                                          std::is_same<T, Json::Array>::value,
+                                      bool>::type = true>
+    void update_arr(const T& val)
+    {
+        json_array_t* arr = json_value_array(parent_);
+        Json copy_json = val.copy();
+        json_array_insert_before(node_, arr, 0, copy_json.node_);
+        copy_json.reset();
+        json_value_t* remove_val = json_array_remove(node_, arr);
+        json_value_destroy(remove_val);
+    }
+
+    void update_arr(bool val);
+    void update_arr(std::nullptr_t val);
+    void update_arr(const std::string& val);
+    void update_arr(const char* val);
+    void update_arr(const std::vector<std::string>& val);
+    void update_arr(const Json& val);
+
+public:
+    class IteratorBase
+    {
+    public:
+        friend class Json;
+        explicit IteratorBase(const json_value_t* val)
+            : val_(val), json_(new Json(nullptr, nullptr, ""))
+        {
+        }
+
+        ~IteratorBase()
+        {
+            if (json_ != nullptr)
+            {
+                delete json_;
+            }
+        }
+
+        IteratorBase(const IteratorBase& iter)
+        {
+            val_ = iter.val_;
+            key_ = iter.key_;
+            json_ = new Json(iter.json_->node_, iter.json_->parent_,
+                             iter.json_->parent_key_);
+        }
+
+        IteratorBase& operator=(const IteratorBase& iter)
+        {
+            if (this == &iter)
+            {
+                return *this;
+            }
+            val_ = iter.val_;
+            key_ = iter.key_;
+            json_->reset(iter.json_->node_, iter.json_->parent_, false,
+                         iter.json_->parent_key_);
+            return *this;
+        }
+
+        IteratorBase(IteratorBase&& iter)
+            : val_(iter.val_), key_(iter.key_), json_(iter.json_)
+        {
+            iter.val_ = nullptr;
+            iter.key_ = nullptr;
+            iter.json_ = nullptr;
+        }
+
+        IteratorBase& operator=(IteratorBase&& iter)
+        {
+            if (this == &iter)
+            {
+                return *this;
+            }
+            val_ = iter.val_;
+            iter.val_ = nullptr;
+            key_ = iter.key_;
+            iter.key_ = nullptr;
+            delete json_;
+            json_ = iter.json_;
+            iter.json_ = nullptr;
+            return *this;
+        }
+
+        Json& operator*() const
+        {
+            if (key_ != nullptr)
+            {
+                json_->parent_key_ = std::string(key_);
+            }
+            else
+            {
+                json_->parent_key_ = "";
+            }
+            return *json_;
+        }
+
+        Json* operator->() const
+        {
+            if (key_ != nullptr)
+            {
+                json_->parent_key_ = std::string(key_);
+            }
+            else
+            {
+                json_->parent_key_ = "";
+            }
+
+            return json_;
+        }
+
+        bool operator==(const IteratorBase& other) const
+        {
+            return json_->node_ == other.json_->node_;
+        }
+
+        bool operator!=(IteratorBase const& other) const
+        {
+            return !(*this == other);
+        }
+
+    private:
+        const json_value_t* val_ = nullptr;
+        const char* key_ = nullptr;
+        Json* json_; // cursor
+    };
+
+    class iterator : public IteratorBase
+    {
+    public:
+        friend class Json;
+        explicit iterator(const json_value_t* val) : IteratorBase(val)
+        {
+        }
+
+        iterator& operator++()
+        {
+            if (is_end())
+            {
+                return *this;
+            }
+            forward();
+            return *this;
+        }
+
+        iterator operator++(int)
+        {
+            iterator old = (*this);
+            if (!is_end())
+            {
+                ++(*this);
+            }
+            return old;
+        }
+
+    private:
+        void set_begin()
+        {
+            json_->node_ = nullptr;
+            key_ = nullptr;
+            forward();
+        }
+
+        void set_end()
+        {
+            json_->node_ = nullptr;
+            key_ = nullptr;
+        }
+
+        bool is_end()
+        {
+            return json_->node_ == nullptr && key_ == nullptr;
+        }
+
+        void forward()
+        {
+            if (json_value_type(val_) == JSON_VALUE_OBJECT)
+            {
+                json_object_t* obj = json_value_object(val_);
+                key_ = json_object_next_name(key_, obj);
+                json_->node_ = const_cast<json_value_t*>(
+                    json_object_next_value(json_->node_, obj));
+            }
+            else if (json_value_type(val_) == JSON_VALUE_ARRAY)
+            {
+                json_array_t* arr = json_value_array(val_);
+                json_->node_ = const_cast<json_value_t*>(
+                    json_array_next_value(json_->node_, arr));
+            }
+        }
+    };
+
+    class reverse_iterator : public IteratorBase
+    {
+    public:
+        friend class Json;
+        explicit reverse_iterator(const json_value_t* val) : IteratorBase(val)
+        {
+        }
+
+        reverse_iterator& operator++()
+        {
+            if (is_rend())
+            {
+                return *this;
+            }
+            backward();
+            return *this;
+        }
+
+        reverse_iterator operator++(int)
+        {
+            reverse_iterator old = (*this);
+            if (!is_rend())
+            {
+                ++(*this);
+            }
+            return old;
+        }
+
+    private:
+        void set_rbegin()
+        {
+            json_->node_ = nullptr;
+            key_ = nullptr;
+            backward();
+        }
+
+        void set_rend()
+        {
+            json_->node_ = nullptr;
+            key_ = nullptr;
+        }
+
+        bool is_rend()
+        {
+            return json_->node_ == nullptr && key_ == nullptr;
+        }
+
+        void backward()
+        {
+            if (json_value_type(val_) == JSON_VALUE_OBJECT)
+            {
+                json_object_t* obj = json_value_object(val_);
+                key_ = json_object_prev_name(key_, obj);
+                json_->node_ = const_cast<json_value_t*>(
+                    json_object_prev_value(json_->node_, obj));
+            }
+            else if (json_value_type(val_) == JSON_VALUE_ARRAY)
+            {
+                json_array_t* arr = json_value_array(val_);
+                json_->node_ = const_cast<json_value_t*>(
+                    json_array_prev_value(json_->node_, arr));
+            }
+        }
+    };
+
+    iterator begin() const
+    {
+        iterator iter(node_);
+        iter.set_begin();
+        return iter;
+    }
+
+    iterator end() const
+    {
+        iterator iter(node_);
+        iter.set_end();
+        return iter;
+    }
+
+    reverse_iterator rbegin() const
+    {
+        reverse_iterator iter(node_);
+        iter.set_rbegin();
+        return iter;
+    }
+
+    reverse_iterator rend() const
+    {
+        reverse_iterator iter(node_);
+        iter.set_rend();
+        return iter;
+    }
+
+private:
+    bool can_obj_push_back();
+
+    bool can_arr_push_back();
+
+    // can convert to any type, just like a placeholder
+    bool is_placeholder() const
+    {
+        return is_null() && parent_ != nullptr;
+    }
+
+    void destroy_node(json_value_t** node)
+    {
+        if (allocated_)
+        {
+            json_value_destroy(*node);
+            *node = nullptr;
+            allocated_ = false;
+        }
+    }
+
+    void copy(const Json& other);
+
+public:
+    // Constructors for the various types of JSON value.
+    // Basic json type Constructors
+    Json();
+    Json(const std::string& str);
+    Json(const char* str);
+    Json(std::nullptr_t null);
+    template <typename T, typename std::enable_if<detail::is_number<T>::value,
+                                                  bool>::type = true>
+    Json(T val)
+        : node_(json_value_create(JSON_VALUE_NUMBER, static_cast<double>(val))),
+          parent_(nullptr), allocated_(true)
+    {
+    }
+    Json(bool val);
+    Json(const std::vector<std::string>& val);
+
+    // For parse
+    Json(const std::string& str, bool parse_flag);
+
+    Json(const Json& json);
+    Json& operator=(const Json& json);
+    Json(Json&& other);
+    Json& operator=(Json&& other);
+
+    ~Json();
+
+protected:
+    // watcher
+    Json(const json_value_t* node, const json_value_t* parent,
+         std::string&& key);
+    Json(const json_value_t* node, const json_value_t* parent,
+         const std::string& key);
+
+    enum class JsonType
+    {
+        Object,
+        Array,
+    };
+    // For Object && Array Construct
+    Json(JsonType type);
+
+    bool is_root() const
+    {
+        return parent_ == nullptr;
+    }
+    bool to_object();
+
+    bool to_array();
+
+    void reset()
+    {
+        node_ = nullptr;
+        parent_ = nullptr;
+        allocated_ = false;
+        parent_key_.clear();
+    }
+
+    void reset(json_value_t* node, const json_value_t* parent, bool allocated,
+               const std::string& parent_key)
+    {
+        node_ = node;
+        parent_ = parent;
+        allocated_ = allocated;
+        parent_key_ = parent_key;
+    }
+
+    void reset(json_value_t* node, const json_value_t* parent, bool allocated,
+               std::string&& parent_key)
+    {
+        node_ = node;
+        parent_ = parent;
+        allocated_ = allocated;
+        parent_key_ = std::move(parent_key);
+    }
+
+    void set_parent(const json_value_t* parent, std::string&& parent_key)
+    {
+        parent_ = parent;
+        parent_key_ = std::move(parent_key);
+    }
+
+private:
+    json_value_t* node_ = nullptr;
+    const json_value_t* parent_ = nullptr;
+    bool allocated_ = false;
+    std::string parent_key_;
+
+private:
+    // for parse
+    static void value_convert(const json_value_t* val, int spaces, int depth,
+                              std::string* out_str);
+
+    static void string_convert(const char* raw_str, std::string* out_str);
+
+    static void number_convert(double number, std::string* out_str);
+
+    static void array_convert(const json_array_t* arr, int spaces, int depth,
+                              std::string* out_str);
+
+    static void array_convert_not_format(const json_array_t* arr,
+                                         std::string* out_str);
+
+    static void object_convert(const json_object_t* obj, int spaces, int depth,
+                               std::string* out_str);
+
+    static void object_convert_not_format(const json_object_t* obj,
+                                          std::string* out_str);
+
+    friend inline std::ostream& operator<<(std::ostream& os, const Json& json)
+    {
+        return (os << json.dump());
+    }
+};
+
+class Object_S : public Json
+{
+public:
+    Object_S() : Json(JsonType::Object)
+    {
+    }
+
+    Object_S(const json_value_t* node, const json_value_t* parent)
+        : Json(node, parent, "")
+    {
+    }
+    using string_type = std::basic_string<char, std::char_traits<char>,
+                                                std::allocator<char>>;
+    using pair_type = std::pair<string_type, Json>;
+
+    Object_S(std::initializer_list<pair_type> list)
+    {
+        std::for_each(list.begin(), list.end(),
+                      [this](const pair_type& pair)
+                      { this->push_back(pair.first, pair.second); });
+    }
+};
+
+class Array_S : public Json
+{
+public:
+    Array_S() : Json(JsonType::Array)
+    {
+    }
+
+    Array_S(const json_value_t* node, const json_value_t* parent)
+        : Json(node, parent, "")
+    {
+    }
+
+    Array_S(std::initializer_list<Json> list)
+    {
+        std::for_each(list.begin(), list.end(),
+                      [this](const Json& js) { this->push_back(js); });
+    }
+};
+
+template <typename T>
+typename std::enable_if<std::is_same<T, Json::Object>::value, T>::type
+Json::get() const
+{
+    return Json::Object(node_, parent_);
+}
+
+template <typename T>
+typename std::enable_if<std::is_same<T, Json::Array>::value, T>::type
+Json::get() const
+{
+    return Json::Array(node_, parent_);
+}
+
+} // namespace wfrest
+
+#endif // WFREST_JSON_H_

+ 557 - 0
FastExtract/src/util/server_config/config.cc

@@ -0,0 +1,557 @@
+#include <fstream>
+#include "config.h"
+#include "workflow/WFGlobal.h"
+#include "workflow/UpstreamManager.h"
+#include "workflow/UpstreamPolicies.h"
+#include "srpc/rpc_metrics_filter.h"
+#include "srpc/rpc_trace_filter.h"
+
+using namespace srpc;
+
+// default upstream_route_t
+
+static unsigned int default_consistent_hash(const char *path, const char *query,
+                                              const char *fragment)
+{
+    return 0;
+}
+
+static unsigned int default_select_route(const char *path, const char *query,
+                                         const char *fragment)
+{
+    return 0;
+}
+
+static void set_endpoint_params(const wfrest::Json& data,
+                                struct EndpointParams *params)
+{
+    *params = ENDPOINT_PARAMS_DEFAULT;
+
+    for (const auto& it : data)
+    {
+        if (it.key() == "max_connections")
+            params->max_connections = data["max_connections"];
+        else if (it.key() == "connect_timeout")
+            params->connect_timeout = data["connect_timeout"];
+        else if (it.key() == "response_timeout")
+            params->response_timeout = data["response_timeout"];
+        else if (it.key() == "ssl_connect_timeout")
+            params->ssl_connect_timeout = data["ssl_connect_timeout"];
+        else if (it.key() == "use_tls_sni")
+            params->use_tls_sni = data["use_tls_sni"];
+        else
+        {
+            printf("[INFO][set_endpoint_params] Unknown key : %s\n",
+                   it.key().c_str());
+        }
+    }
+}
+
+static void load_global(const wfrest::Json& data)
+{
+    struct WFGlobalSettings settings = GLOBAL_SETTINGS_DEFAULT;
+    std::string resolv_conf_path;
+    std::string hosts_path;
+
+    for (const auto& it : data)
+    {
+        if (it.key() == "endpoint_params")
+        {
+            set_endpoint_params(data["endpoint_params"],
+                                &settings.endpoint_params);
+        }
+        else if (it.key() == "dns_server_params")
+        {
+            set_endpoint_params(data["dns_server_params"],
+                                &settings.dns_server_params);
+        }
+        else if (it.key() == "dns_ttl_default")
+            settings.dns_ttl_default = data["dns_ttl_default"];
+        else if (it.key() == "dns_ttl_min")
+            settings.dns_ttl_min = data["dns_ttl_min"];
+        else if (it.key() == "dns_threads")
+            settings.dns_threads = data["dns_threads"];
+        else if (it.key() == "poller_threads")
+            settings.poller_threads = data["poller_threads"];
+        else if (it.key() == "handler_threads")
+            settings.handler_threads = data["handler_threads"];
+        else if (it.key() == "compute_threads")
+            settings.compute_threads = data["compute_threads"];
+        else if (it.key() == "resolv_conf_path")
+        {
+            resolv_conf_path = data["resolv_conf_path"].get<std::string>();
+            settings.resolv_conf_path = resolv_conf_path.c_str();
+        }
+        else if (it.key() == "hosts_path")
+        {
+            hosts_path = data["hosts_path"].get<std::string>();
+            settings.hosts_path = hosts_path.c_str();
+        }
+        else
+            printf("[INFO][load_global] Unknown key : %s\n", it.key().c_str());
+    }
+
+    WORKFLOW_library_init(&settings);
+}
+
+static bool load_upstream_server(const wfrest::Json& data,
+                                 std::vector<std::string>& hosts,
+                                 std::vector<AddressParams>& params)
+{
+    AddressParams param;
+
+    hosts.clear();
+    params.clear();
+
+    for (const auto& server : data)
+    {
+        if (server.has("host") == false)
+        {
+            printf("[ERROR][load_upstream] Invalid upstream server\n");
+            continue;
+        }
+
+        param = ADDRESS_PARAMS_DEFAULT;
+        if (server.has("params"))
+        {
+            for (const auto& p : server["params"])
+            {
+                if (p.key() == "endpoint_params")
+                    set_endpoint_params(p.value(), &param.endpoint_params);
+                else if (p.key() == "weight")
+                    param.weight = p.value().get<unsigned short>();
+                else if (p.key() == "max_fails")
+                    param.max_fails = p.value().get<unsigned int>();
+                else if (p.key() == "dns_ttl_default")
+                    param.dns_ttl_default = p.value().get<unsigned int>();
+                else if (p.key() == "dns_ttl_min")
+                    param.dns_ttl_min = p.value().get<unsigned int>();
+                else if (p.key() == "server_type")
+                    param.server_type = p.value().get<int>();
+                else if (p.key() == "group_id")
+                    param.group_id = p.value().get<int>();
+                else
+                    printf("[ERROR][load_upstream] Invalid params: %s\n",
+                           p.key().c_str());
+            }
+        }
+
+        hosts.push_back(server["host"]);
+        params.push_back(param);
+    }
+    
+    if (hosts.size() == 0)
+        return false;
+    else
+        return true;
+}
+
+static void load_upstream(const wfrest::Json& data)
+{
+    std::string name;
+    std::string type;
+    bool try_another;
+    std::vector<std::string> hosts;
+    std::vector<AddressParams> params;
+
+    for (const auto& it : data)
+    {
+        if (it.has("name") == false ||
+            it.has("type") == false ||
+            it.has("server") == false || 
+            load_upstream_server(it["server"], hosts, params) == false)
+        {
+            printf("[ERROR][load_upstream] Invalid upstream\n");
+            continue;
+        }
+
+        name = it["name"].get<std::string>();
+        type = it["type"].get<std::string>();
+
+        if (it.has("try_another"))
+            try_another = it["try_another"];
+        else
+            try_another = false;
+
+        if (type == "weighted_random")
+        {
+            UpstreamManager::upstream_create_weighted_random(name, try_another);
+        }
+        else if (type == "consistent_hash")
+        {
+            UpstreamManager::upstream_create_consistent_hash(name,
+                                                    default_consistent_hash);
+        }
+        else if (type == "round_robin")
+        {
+            UpstreamManager::upstream_create_round_robin(name, try_another);
+        }
+        else if (type == "manual")
+        {
+            UpstreamManager::upstream_create_manual(name,
+                                                    default_select_route,
+                                                    try_another,
+                                                    default_consistent_hash);
+        }
+        else if (type == "vnswrr")
+        {
+            UpstreamManager::upstream_create_vnswrr(name);
+        }
+        else
+        {
+            printf("[INFO][load_upstream] Unknown type : %s\n", type.c_str());
+            continue;
+        }
+
+        for (size_t i = 0; i < hosts.size(); i++)
+            UpstreamManager::upstream_add_server(name, hosts[i], &params[i]);
+    }
+}
+
+void ServerRPCConfig::load_server()
+{
+    if (this->data["server"].has("port"))
+        this->s_port = this->data["server"]["port"];
+
+    if (this->data["server"].has("root"))
+        this->root_path = this->data["server"]["root"].get<std::string>();
+
+    if (this->data["server"].has("cert_file"))
+        this->s_cert_file = this->data["server"]["cert_file"].get<std::string>();
+
+    if (this->data["server"].has("file_key"))
+        this->s_file_key = this->data["server"]["file_key"].get<std::string>();
+
+    if (this->data["server"].has("error_page"))
+    {
+        for (const auto& it : this->data["server"]["error_page"])
+        {
+            std::string page;
+
+            if (it.has("error") == true && it.has("error") == true)
+            {
+                page = it["page"].get<std::string>();
+                for (const auto& e : it["error"])
+                    this->error_page.insert(std::make_pair(e.get<int>(), page));
+            }
+            else
+            {
+                printf("[ERROR][load_file_service] Invalid error_page\n");
+                continue;
+            }
+        }
+    }
+}
+
+void ServerRPCConfig::load_client()
+{
+    if (this->data["client"].has("transport_type"))
+    {
+        std::string type = this->data["client"]["transport_type"].get<std::string>();
+        if (type == "TT_SCTP")
+            this->c_transport_type = TT_SCTP;
+        else if (type == "TT_UDP")
+            this->c_transport_type = TT_UDP;
+    }
+
+    if (this->data["client"].has("remote_host"))
+        this->c_host = this->data["client"]["remote_host"].get<std::string>();
+
+    if (this->data["client"].has("remote_port"))
+        this->c_port = this->data["client"]["remote_port"];
+
+    if (this->data["client"].has("is_ssl"))
+        this->c_is_ssl = this->data["client"]["is_ssl"];
+
+    if (this->data["client"].has("callee_timeout"))
+        this->c_callee_timeout = this->data["client"]["callee_timeout"];
+
+    if (this->data["client"].has("redirect_max"))
+        this->c_redirect_max = this->data["client"]["redirect_max"];
+
+    if (this->data["client"].has("retry_max"))
+        this->c_retry_max = this->data["client"]["retry_max"];
+
+    if (this->data["client"].has("user_name"))
+        this->c_user_name = this->data["client"]["user_name"].get<std::string>();
+
+    if (this->data["client"].has("password"))
+        this->c_password = this->data["client"]["password"].get<std::string>();
+}
+
+bool ServerRPCConfig::load(const char *file)
+{
+    FILE *fp = fopen(file, "r");
+    if (!fp)
+        return false;
+
+    this->data = wfrest::Json::parse(fp);
+    fclose(fp);
+    
+    if (this->data.is_valid() == false)
+        return false;
+
+    for (const auto& it : this->data)
+    {
+        if (it.key() == "server")
+            this->load_server();
+        else if (it.key() == "client")
+            this->load_client();
+        else if (it.key() == "global")
+            load_global(it.value());
+        else if (it.key() == "upstream")
+            load_upstream(it.value());
+        else if (it.key() == "metrics")
+            this->load_metrics();
+        else if (it.key() == "trace")
+            this->load_trace();
+        else if (it.key() == "model")
+            this->load_model();
+        else
+            printf("[INFO][ServerRPCConfig::load] Unknown key: %s\n", it.key().c_str());
+    }
+
+    return true;
+};
+
+void ServerRPCConfig::load_metrics()
+{
+    for (const auto& it : this->data["metrics"])
+    {
+        if (it.has("filter") == false)
+            continue;
+
+        std::string filter_name = it["filter"];
+
+        if (filter_name.compare("prometheus") == 0)
+        {
+            if (it.has("port") == false)
+                continue;
+
+            RPCMetricsPull *filter = new RPCMetricsPull();
+            unsigned short port = it["port"];
+            filter->init(port);
+            this->filters.push_back(filter);
+        }
+        else if (filter_name.compare("opentelemetry") == 0)
+        {
+            if (it.has("address") == false)
+                continue;
+
+            std::string name = it["filter_name"];
+            std::string url = it["address"];
+
+            unsigned int redirect_max = OTLP_HTTP_REDIRECT_MAX;
+            unsigned int retry_max = OTLP_HTTP_RETRY_MAX;
+            size_t report_threshold = RPC_REPORT_THREHOLD_DEFAULT;
+            size_t report_interval = RPC_REPORT_INTERVAL_DEFAULT;
+
+            if (it.has("redirect_max"))
+                redirect_max = it["redirect_max"];
+            if (it.has("retry_max"))
+                retry_max = it["retry_max"];
+            if (it.has("report_threshold"))
+                report_threshold = it["report_threshold"];
+            if (it.has("report_interval_ms"))
+                report_interval = it["report_interval_ms"];
+
+            RPCMetricsOTel *filter = new RPCMetricsOTel(name,
+                                                        url,
+                                                        redirect_max,
+                                                        retry_max,
+                                                        report_threshold,
+                                                        report_interval);
+
+            if (it.has("attributes"))
+            {
+                for (const auto& kv : it["attributes"])
+                {
+                    if (kv.has("key") == false || kv.has("value") == false)
+                        continue;
+
+                    filter->add_attributes(kv["key"], kv["value"]);
+                }
+            }
+
+            this->filters.push_back(filter);
+        }
+        else
+        {
+            printf("[ERROR][ServerRPCConfig::load_metrics] Unknown metrics: %s\n",
+                   filter_name.c_str());
+        }
+    }
+}
+
+void ServerRPCConfig::load_trace()
+{
+    for (const auto& it : this->data["trace"])
+    {
+        if (it.has("filter") == false)
+            continue;
+
+        std::string filter_name = it["filter"];
+        size_t spans_per_second = SPANS_PER_SECOND_DEFAULT;
+
+        if (filter_name.compare("default") == 0)
+        {
+            if (it.has("spans_per_second"))
+                spans_per_second = it["spans_per_second"];
+
+            auto *filter = new RPCTraceDefault(spans_per_second);
+            this->filters.push_back(filter);
+        }
+        else if (filter_name.compare("opentelemetry") == 0)
+        {
+            if (it.has("address") == false)
+                continue;
+
+            std::string url = it["address"];
+            unsigned int redirect_max = OTLP_HTTP_REDIRECT_MAX;
+            unsigned int retry_max = OTLP_HTTP_RETRY_MAX;
+            size_t report_threshold = RPC_REPORT_THREHOLD_DEFAULT;
+            size_t report_interval = RPC_REPORT_INTERVAL_DEFAULT;
+
+            if (it.has("redirect_max"))
+                redirect_max = it["redirect_max"];
+            if (it.has("retry_max"))
+                retry_max = it["retry_max"];
+            if (it.has("report_threshold"))
+                report_threshold = it["report_threshold"];
+            if (it.has("report_interval_ms"))
+                report_interval = it["report_interval_ms"];
+
+            auto *filter = new RPCTraceOpenTelemetry(url,
+                                                     OTLP_TRACES_PATH,
+                                                     redirect_max,
+                                                     retry_max,
+                                                     spans_per_second,
+                                                     report_threshold,
+                                                     report_interval);
+
+            if (it.has("attributes"))
+            {
+                for (const auto& kv : it["attributes"])
+                {
+                    if (kv.has("key") == false || kv.has("value") == false)
+                        continue;
+
+                    filter->add_attributes(kv["key"], kv["value"]);
+                }
+            }
+
+            this->filters.push_back(filter);
+        }
+        else
+        {
+            printf("[ERROR][ServerRPCConfig::load_metrics] Unknown metrics: %s\n",
+                   filter_name.c_str());
+        }
+    }
+}
+
+void ServerRPCConfig::load_model()
+{
+    if (this->data["model"].has("model_count"))
+        this->model_count = this->data["model"]["model_count"];
+
+    if (this->data["model"].has("det_model_path"))
+        this->det_model_path = this->data["model"]["det_model_path"].get<std::string>();
+
+    if (this->data["model"].has("det_params_path"))
+        this->det_params_path = this->data["model"]["det_params_path"].get<std::string>();
+
+    if (this->data["model"].has("det_cfg_path"))
+        this->det_cfg_path = this->data["model"]["det_cfg_path"].get<std::string>();
+}
+
+void ServerRPCConfig::load_filter(SRPCServer& server)
+{
+    for (auto *filter : this->filters)
+        server.add_filter(filter);
+}
+
+void ServerRPCConfig::load_filter(SRPCClient& client)
+{
+    for (auto *filter : this->filters)
+        client.add_filter(filter);
+}
+
+void ServerRPCConfig::load_filter(SRPCHttpServer& server)
+{
+    for (auto *filter : this->filters)
+        server.add_filter(filter);
+}
+
+void ServerRPCConfig::load_filter(SRPCHttpClient& client)
+{
+    for (auto *filter : this->filters)
+        client.add_filter(filter);
+}
+
+void ServerRPCConfig::load_filter(BRPCServer& server)
+{
+    for (auto *filter : this->filters)
+        server.add_filter(filter);
+}
+
+void ServerRPCConfig::load_filter(BRPCClient& client)
+{
+    for (auto *filter : this->filters)
+        client.add_filter(filter);
+}
+
+void ServerRPCConfig::load_filter(ThriftServer& server)
+{
+    for (auto *filter : this->filters)
+        server.add_filter(filter);
+}
+
+void ServerRPCConfig::load_filter(ThriftClient& client)
+{
+    for (auto *filter : this->filters)
+        client.add_filter(filter);
+}
+
+void ServerRPCConfig::load_filter(ThriftHttpServer& server)
+{
+    for (auto *filter : this->filters)
+        server.add_filter(filter);
+}
+
+void ServerRPCConfig::load_filter(ThriftHttpClient& client)
+{
+    for (auto *filter : this->filters)
+        client.add_filter(filter);
+}
+
+void ServerRPCConfig::load_filter(TRPCServer& server)
+{
+    for (auto *filter : this->filters)
+        server.add_filter(filter);
+}
+
+void ServerRPCConfig::load_filter(TRPCClient& client)
+{
+    for (auto *filter : this->filters)
+        client.add_filter(filter);
+}
+
+void ServerRPCConfig::load_filter(TRPCHttpServer& server)
+{
+    for (auto *filter : this->filters)
+        server.add_filter(filter);
+}
+
+void ServerRPCConfig::load_filter(TRPCHttpClient& client)
+{
+    for (auto *filter : this->filters)
+        client.add_filter(filter);
+}
+
+ServerRPCConfig::~ServerRPCConfig()
+{
+    for (size_t i = 0; i < this->filters.size(); i++)
+        delete this->filters[i];
+}
+

+ 104 - 0
FastExtract/src/util/server_config/config.h

@@ -0,0 +1,104 @@
+#ifndef _RPC_CONFIG_H_
+#define _RPC_CONFIG_H_
+
+#include <string>
+#include <vector>
+#include <unordered_map>
+#include "json/Json.h"
+#include "srpc/rpc_types.h"
+#include "srpc/rpc_define.h"
+#include "srpc/rpc_filter.h"
+
+namespace srpc
+{
+
+class ServerRPCConfig
+{
+public:
+    using ErrorPageMap = std::unordered_map<int, std::string>;
+
+    bool load(const char *file);
+
+    void load_filter(SRPCServer& server);
+    void load_filter(SRPCClient& client);
+
+    void load_filter(SRPCHttpServer& server);
+    void load_filter(SRPCHttpClient& client);
+
+    void load_filter(BRPCServer& server);
+    void load_filter(BRPCClient& client);
+
+    void load_filter(ThriftServer& server);
+    void load_filter(ThriftClient& client);
+
+    void load_filter(ThriftHttpServer& server);
+    void load_filter(ThriftHttpClient& client);
+
+    void load_filter(TRPCServer& server);
+    void load_filter(TRPCClient& client);
+
+    void load_filter(TRPCHttpServer& server);
+    void load_filter(TRPCHttpClient& client);
+
+    unsigned short server_port() const { return this->s_port; }
+    const char *server_cert_file() const { return this->s_cert_file.c_str(); }
+    const char *server_file_key() const { return this->s_file_key.c_str(); }
+    enum TransportType client_transport_type() const { return this->c_transport_type; }
+    const char *client_host() const { return this->c_host.c_str(); }
+    unsigned short client_port() const { return this->c_port; }
+    bool client_is_ssl() const { return this->c_is_ssl; }
+    const char *client_url() const { return this->c_url.c_str(); }
+    int client_callee_timeout() const { return this->c_callee_timeout; }
+    const char *client_caller() const { return this->c_caller.c_str(); }
+    int redirect_max() const { return this->c_redirect_max; }
+    int retry_max() const { return this->c_retry_max; }
+    const char *client_user_name() const { return this->c_user_name.c_str(); }
+    const char *client_password() const { return this->c_password.c_str(); }
+    const char *get_root_path() const { return this->root_path.c_str(); }
+    const ErrorPageMap& get_error_page() const { return this->error_page; }
+
+    int get_model_count() { return model_count; }
+    const char* get_det_model_path() { return det_model_path.c_str(); }
+    const char* get_det_params_path() { return det_params_path.c_str(); }
+    const char* get_det_cfg_path() { return det_cfg_path.c_str(); }
+public:
+    ServerRPCConfig() :
+        s_port(0), c_port(0), c_is_ssl(false), c_redirect_max(0), c_retry_max(0), model_count(30)
+    { }
+    ~ServerRPCConfig();
+
+private:
+    void load_server();
+    void load_client();
+    void load_metrics();
+    void load_trace();
+    void load_model();
+
+    wfrest::Json data;
+    std::vector<RPCFilter *> filters;
+    unsigned short s_port;
+    std::string s_cert_file;
+    std::string s_file_key;
+    enum TransportType c_transport_type;
+    std::string c_host;
+    unsigned short c_port;
+    bool c_is_ssl;
+    std::string c_url;
+    int c_callee_timeout;
+    std::string c_caller;
+    int c_redirect_max;
+    int c_retry_max;
+    std::string c_user_name;
+    std::string c_password;
+    std::string root_path;
+    ErrorPageMap error_page;
+
+    int model_count;
+    std::string det_model_path;
+    std::string det_params_path;
+    std::string det_cfg_path;
+};
+
+}
+
+#endif

+ 384 - 0
FastExtract/third_party/conversion/include/fpdf_convert.h

@@ -0,0 +1,384 @@
+#ifndef PUBLIC_FPDFCONVERT_H_
+#define PUBLIC_FPDFCONVERT_H_
+
+#ifdef LINUX_SERVER
+#define FPDFCONVERT_EXPORT __attribute__((visibility("default")))
+#else
+#define FPDFCONVERT_EXPORT
+#endif
+
+#include <vector>
+
+class CFP_PdfToWord;
+class CFP_PdfToPPT;
+class CFP_PdfToExcel;
+class CFP_PdfToTable;
+class CFP_PdfToTxt;
+class CFP_PdfToImg;
+class CFP_PdfToRtf;
+class CFP_PdfToHtml;
+class CFP_PDFExtraceText;
+class CFP_PDFExtraceTable;
+class CFP_PDFExtracePDF;
+#ifdef USEOCR
+class CFP_PdfToEditable;
+#endif // USEOCR
+
+
+// #define ERR_SUCCESS_ 1000// 成功
+// #define ERR_UNKNOWN_ 1001// 未知错误
+// #define ERR_VERIFY_ 1002// 认证失败
+// #define ERR_INVALID_ 1003// license无效
+// #define ERR_OVERDUE_ 1004// license过期
+// #define ERR_DATA_ 1005// license数据错误
+// #define ERR_PLATFORM_ 1006// platform错误
+// #define ERR_IDENTIFIER_ 1007// identifier错误
+
+typedef struct fpdf_to_word_t__ *FPDF_TO_WORD;
+typedef struct fpdf_to_ppt_t__ *FPDF_TO_PPT;
+typedef struct fpdf_to_excel_t__ *FPDF_TO_EXCEL;
+typedef struct fpdf_to_excel_table_t__ *FPDF_TO_TABLE;
+typedef struct fpdf_to_txt_t__ *FPDF_TO_TXT;
+typedef struct fpdf_to_csv_t__ *FPDF_TO_CSV;
+typedef struct fpdf_to_img_t__ *FPDF_TO_IMG;
+typedef struct fpdf_to_rtf_t__ *FPDF_TO_RTF;
+typedef struct fpdf_to_html_t__ *FPDF_TO_HTML;
+typedef struct fpdf_handle_ *FPDF_HANDLE;
+typedef struct fpdf_to_table_json_t__ *FPDF_TO_TableJson;
+
+typedef bool (*PSO_StartOCR)(const char *img_path,size_t size); //number is not 0,img_path is memmorydata
+
+enum ConvertPlatForm {
+    P_MAC, P_IOS, P_ANDROID, P_WIN, P_LINUX, P_OTHERS
+};
+
+enum ConvertType {
+    T_PPT, T_WORD, T_TXT, T_EXCEL, T_TABLE, T_CSV, T_IMG, T_RTF, T_HTML, T_ExtractText, T_ExtractTable, T_EDITABLE, T_ExtractPDF
+};
+
+#ifndef CONVERT_HTML_PARAMS_
+#define CONVERT_HTML_PARAMS_
+//转换出的文档类型
+enum PageAndNavigationPaneOptions : int {
+    SinglePage = 0, //单页
+    //SinglepageNavigationByTitle,//导览框架 基于标题 未实现 需要文件标题数据,预留接口
+    SinglePageNavigationByBookmarks,//导览框架 基于书签
+    MultiplePages,//多页拆分
+    //MultiplePagesSplitByTitle,//多页按标题拆分 未实现 需要文件标题数据,预留接口
+    MultiplePagesSplitByBookmarks//多页按书签拆分
+};
+#endif //CONVERT_HTML_PARAMS_
+
+#ifndef CONVERTPARAMS_
+#define CONVERTPARAMS_
+enum ImgType {
+    JPEG, PNG //, BMP, GIF, TIFF, TGA, JPEG2000
+};
+
+enum ExcelMode{
+    OneTableOneSheet,               //一个表格一个Sheet
+    PageTablesOneSheet,             //一页所有表格一个Sheet
+    AllPagesTablesSingleSheet,      //文档所有表格一个Sheet
+    PageTablesTextsOneSheet,        //一页所有文字与表格一个Sheet
+    PageTextsOnSheet,               //一页所有文本一个Sheet
+    AllPagesTablesTextsSingleSheet  //所有文字和表格一个Sheet
+};
+
+enum WordLayoutMode {
+    FlowMode =                  1,  //流排模式
+    FlowTableMode =             3,  //流排支持表格
+    BoxMode =                   4   //框排模式
+};
+
+enum OCRLanguage {
+    UNKNOWN =                 0,            ///< Unknown
+    CHINESE =                 1,           ///< Simplified Chinese
+    CHINESE_TRA =             2,           ///< Traditional Chinese
+    ENGLISH =                 3,           ///< English
+    KOREAN =                  4,           ///< Korean
+    JAPANESE =                5,           ///< Japanese
+    LATIN =                   6,           ///< Latin
+};
+
+//1.10.x 新增
+//数据提取参数
+//提取文本
+//开启 OCR 之后目前无法按照段落提取
+enum ExtractTextMode {
+    e_ExtractByLines, //按照行提取
+    e_ExtractByParagraphs, //按照段落提取
+};
+
+typedef struct ConverterParams {
+    bool isOnlyAiTable;
+    bool isContainImg;//是否包含图片
+    bool isImgsMerge;//是否图片合并
+    unsigned int imgDpi;//图片分辨率
+
+    ImgType imgType;//图片类型
+    bool isContainAnnot;//是否包含注释
+    bool isAnnotToImg;//注释是否转成图片
+    bool isPathToImg;//路径是否转成图片
+
+    bool isContainEmptyChar;//是否包含空字符
+
+    WordLayoutMode wordLayoutMode;//Word排版模式
+
+    ExcelMode excelMode;
+    bool isSingleSheetOfExcel;//转Excel是否采用单Sheet
+    bool isAllowOcr;//是否允许开启OCR
+    bool isContainOcrBg;//是否包含OCR底图
+    OCRLanguage ocrLanguage;//OCR语言
+
+    PageAndNavigationPaneOptions htmlOptions;
+
+    bool isCsvMerge;//是否合并CSV
+
+    ExtractTextMode extractTextMode;
+
+    ConverterParams() {
+        isContainImg = true;
+        isImgsMerge = true;
+        imgDpi = 144;
+        imgType = ImgType::PNG;
+        isContainAnnot = true;
+
+        isAnnotToImg = true;
+        isPathToImg = true;
+        isContainEmptyChar = true;
+
+        wordLayoutMode = WordLayoutMode::BoxMode;
+
+        excelMode = OneTableOneSheet;
+        isSingleSheetOfExcel = true;
+        isAllowOcr = false;
+        isContainOcrBg = true;
+        htmlOptions = PageAndNavigationPaneOptions::SinglePage;
+        isCsvMerge = false;
+        isOnlyAiTable = false;
+        extractTextMode = e_ExtractByLines;
+    }
+} ConverterParams;
+
+enum ConvertResult {
+    /// Success, and no error occurs.
+    e_ErrSuccess = 0,
+    /// 取消转换
+    e_ErrCancel = 1,
+    /// File cannot be found or could not be opened.
+    e_ErrFile = 2,
+    /// Invalid password. Usually, this error may occur when loading a PDF document with password. When meet this, user should load document again with correct password.
+    e_ErrPDFPassword = 3,
+    /// PDF页面加载失败
+    e_ErrPDFPage = 4,
+    /// Format is invalid. For files, this may also mean that file is corrupted.
+    e_ErrPDFFormat = 5,
+    /// PDF document is encrypted by some unsupported security handler.
+    e_ErrPDFSecurity = 6,
+    /// Out-of-memory error occurs.
+    e_ErrOutOfMemory = 7,
+    /// 系统IO错误
+    e_ErrIO = 8,
+    /// 文件夹压缩失败
+    e_ErrCompress = 9,
+
+    /// The license is invalid
+    e_ErrLicenseInvalid = 20,
+    /// The license has expired
+    e_ErrLicenseExpire = 21,
+    /// The license does not support the current platform
+    e_ErrLicenseUnsupportedPlatform = 22,
+    /// The license does not support the application id
+    e_ErrLicenseUnsupportedID = 23,
+    /// The license does not support the device id
+    e_ErrLicenseUnsupportedDevice = 24,
+    /// The license does not have the function permission
+    e_ErrLicensePermissionDeny = 25,
+    /// License has not been initialized
+    e_ErrLicenseUninitialized = 26,
+    /// Illegal access to the API interface
+    e_ErrLicenseIllegalAccess = 27,
+    /// Failed to read license file
+    e_ErrLicenseFileReadFailed = 28,
+
+    /// PDF文档中没有表格
+    e_ErrNoTable = 40,
+    /// 调用OCR识别失败
+    e_ErrOCRFailure = 41,
+
+    /// 未知错误
+    e_ErrUnknown = 100
+};
+#endif //CONVERTPARAMS_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+// Conversions to/from underlying types.
+FPDFCONVERT_EXPORT CFP_PdfToWord *CFPPdfToWordFromFPDFToWord(FPDF_TO_WORD word);
+FPDFCONVERT_EXPORT FPDF_TO_WORD FPDFToWordFromCFPPdfToWord(CFP_PdfToWord *word);
+
+FPDFCONVERT_EXPORT CFP_PdfToPPT *CFPPdfToPPTFromFPDFTOPPT(FPDF_TO_PPT ppt);
+FPDFCONVERT_EXPORT FPDF_TO_PPT FPDFTOPPTFromCFPPdfToPPT(CFP_PdfToPPT *ppt);
+
+FPDFCONVERT_EXPORT CFP_PdfToExcel *CFPPdfToExcelFromFPDFTOEXCEL(FPDF_TO_EXCEL excel);
+FPDFCONVERT_EXPORT FPDF_TO_EXCEL FPDFTOEXCELFromCFPPdfToExcel(CFP_PdfToExcel *excel);
+
+FPDFCONVERT_EXPORT CFP_PdfToTable *CFPPdfToTableFromFPDFTOTABLE(FPDF_TO_TABLE table);
+FPDFCONVERT_EXPORT FPDF_TO_TABLE FPDFTOTABLEFromCFPPdfToTable(CFP_PdfToTable *table);
+
+FPDFCONVERT_EXPORT CFP_PdfToTxt *CFPPdfToTxtFromFPDFToTxt(FPDF_TO_TXT txt);
+FPDFCONVERT_EXPORT FPDF_TO_TXT FPDFToTxtFromCFPPdfToTxt(CFP_PdfToTxt *txt);
+
+FPDFCONVERT_EXPORT CFP_PdfToTable *CFPPdfToCsvFromFPDFTOCSV(FPDF_TO_CSV csv);
+FPDFCONVERT_EXPORT FPDF_TO_CSV FPDFTOCSVFromCFPPdfToCsv(CFP_PdfToTable *csv);
+
+FPDFCONVERT_EXPORT CFP_PdfToImg *CFPPdfToImgFromFPDFToImg(FPDF_TO_IMG img);
+FPDFCONVERT_EXPORT FPDF_TO_IMG FPDFToImgFromCFPPdfToImg(CFP_PdfToImg *img);
+
+FPDFCONVERT_EXPORT CFP_PdfToRtf *CFPPdfToRtfFromFPDFToRtf(FPDF_TO_RTF rtf);
+FPDFCONVERT_EXPORT FPDF_TO_RTF FPDFToRtfFromCFPPdfToRtf(CFP_PdfToRtf *rtf);
+
+FPDFCONVERT_EXPORT CFP_PdfToHtml *CFPPdfToHtmlFromFPDFToHtml(FPDF_TO_HTML html);
+FPDFCONVERT_EXPORT FPDF_TO_HTML FPDFToHtmlFromCFPPdfToHtml(CFP_PdfToHtml *html);
+
+FPDFCONVERT_EXPORT FPDF_HANDLE FPDFExtractTextFromCFPPdfExtractText(CFP_PDFExtraceText* exctarct_text);
+FPDFCONVERT_EXPORT CFP_PDFExtraceText* FPDFExtractTextToCFPPdfExtractText(FPDF_HANDLE exctarct_text);
+
+FPDFCONVERT_EXPORT FPDF_HANDLE FPDFExtractTableFromCFPPdfExtractTable(CFP_PDFExtraceTable* exctarct_table);
+FPDFCONVERT_EXPORT CFP_PDFExtraceTable* FPDFExtractTableToCFPPdfExtractTable(FPDF_HANDLE exctarct_table);
+
+FPDFCONVERT_EXPORT FPDF_HANDLE FPDFExtractPDFFromCFPPdfExtractPDF(CFP_PDFExtracePDF* exctarct_pdf);
+FPDFCONVERT_EXPORT CFP_PDFExtracePDF* FPDFExtractPDFToCFPPdfExtractPDF(FPDF_HANDLE exctarct_pdf);
+
+#ifdef USEOCR
+FPDFCONVERT_EXPORT FPDF_HANDLE FPDFToEditableFromCFPPdfToEditable(CFP_PdfToEditable* pdf_editable);
+FPDFCONVERT_EXPORT CFP_PdfToEditable* FPDFToEditableToCFPPdfToEditable(FPDF_HANDLE pdf_editable);
+#endif
+
+//旧接口废弃
+//FPDFCONVERT_EXPORT int CPDF_initPermission(const char *key, const char *secret, const char *packageName, const int current_time, const ConvertPlatForm &platForm, int *type, bool id_verify);
+FPDFCONVERT_EXPORT int CPDF_initPermission(const char* license_file, bool isFile, const char* deviceId, const char* appId, bool id_verify);
+FPDFCONVERT_EXPORT void CPDF_initRes(const char *path);
+FPDFCONVERT_EXPORT void CPDF_ReleaseRes();
+FPDFCONVERT_EXPORT int CPDF_getPagesByFd(const int fd, const char *user_pwd);
+FPDFCONVERT_EXPORT int CPDF_getPagesByPath(const char *strPdfFile, const char *strUserPwd);
+FPDFCONVERT_EXPORT char *getFileSizeStr(const char *filename);
+
+FPDFCONVERT_EXPORT bool FileIsImage(const char *filename);
+
+FPDFCONVERT_EXPORT bool isAllowToWord();
+FPDFCONVERT_EXPORT bool isAllowToPPT();
+FPDFCONVERT_EXPORT bool isAllowToTxt();
+FPDFCONVERT_EXPORT bool isAllowToExcel();
+FPDFCONVERT_EXPORT bool isAllowToTable();
+FPDFCONVERT_EXPORT bool isAllowToCsv();
+FPDFCONVERT_EXPORT bool isAllowToImg();
+FPDFCONVERT_EXPORT bool isAllowToRtf();
+FPDFCONVERT_EXPORT bool isAllowToHtml();
+FPDFCONVERT_EXPORT bool isAllowOcr();
+FPDFCONVERT_EXPORT bool isAllowExtract();
+FPDFCONVERT_EXPORT bool isAllowToSearchablePDF();
+FPDFCONVERT_EXPORT void CPDF_ConvertPages(void *fpdf_to_handle, int pages, ConvertType type);
+FPDFCONVERT_EXPORT int CPDF_getPages(void *fpdf_to_handle, ConvertType type);
+FPDFCONVERT_EXPORT void CPDF_Cancel(void *fpdf_to_handle, ConvertType type);
+FPDFCONVERT_EXPORT bool CPDF_IsCancel(void *fpdf_to_handle, ConvertType type);
+FPDFCONVERT_EXPORT void CPDF_Release(void *fpdf_to_handle, ConvertType type);
+
+FPDFCONVERT_EXPORT void CPDF_setStartOcr(PSO_StartOCR pfunc);
+FPDFCONVERT_EXPORT void CPDF_setOcrResult(const char *result);
+FPDFCONVERT_EXPORT const char *CPDF_getOcrKey();
+FPDFCONVERT_EXPORT int CPDF_verifyOcrKey(const char *secret, char *result);
+
+FPDFCONVERT_EXPORT FPDF_TO_WORD CPDF_InitPDFToWordByFd(const int fd, const char *name, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT FPDF_TO_WORD CPDF_InitIMGToWord(const char *imgPath, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, ConvertResult *result);
+FPDFCONVERT_EXPORT FPDF_TO_WORD CPDF_InitPDFToWordByPath(const char *strPdfFile, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT int CPDF_StartPDFToWord(FPDF_TO_WORD word, int nPageNo, int nPageid);
+FPDFCONVERT_EXPORT void CPDF_EndPDFToWord(FPDF_TO_WORD word, char *pOutPath);
+
+FPDFCONVERT_EXPORT FPDF_TO_PPT CPDF_InitIMGToPPT(const char *imgPath, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, ConvertResult *result);
+FPDFCONVERT_EXPORT FPDF_TO_PPT CPDF_InitPDFToPPTByFd(const int fd, const char *name, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT FPDF_TO_PPT CPDF_InitPDFToPPTByPath(const char *strPdfFile, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT int CPDF_StartPDFToPPT(FPDF_TO_PPT ppt, int nPageNo, int nPageid);
+FPDFCONVERT_EXPORT void CPDF_EndPDFToPPT(FPDF_TO_PPT ppt, char *pOutPath);
+
+FPDFCONVERT_EXPORT FPDF_TO_EXCEL CPDF_InitIMGToExcel(const char *imgPath, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, ConvertResult *result);
+FPDFCONVERT_EXPORT FPDF_TO_EXCEL CPDF_InitPDFToExcelByFd(const int fd, const char *name, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT FPDF_TO_EXCEL CPDF_InitPDFToExcelByPath(const char *strPdfFile, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT bool CPDF_InitExcelXML(FPDF_TO_EXCEL excel, const int convertPages);
+FPDFCONVERT_EXPORT int CPDF_StartPDFToExcel(FPDF_TO_EXCEL excel, int nPageNo, int nPageid);
+FPDFCONVERT_EXPORT void CPDF_EndPDFToExcel(FPDF_TO_EXCEL excel, char *pOutPath);
+
+FPDFCONVERT_EXPORT FPDF_TO_TABLE CPDF_InitIMGToTable(const char *imgPath, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, ConvertResult *result);
+FPDFCONVERT_EXPORT FPDF_TO_TABLE CPDF_InitPDFToTableByFd(const int fd, const char *name, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT FPDF_TO_TABLE CPDF_InitPDFToTableByPath(const char *strPdfFile, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT bool CPDF_InitTableXML(FPDF_TO_TABLE table, const int convertPages);
+FPDFCONVERT_EXPORT int CPDF_StartPDFToTable(FPDF_TO_TABLE table, int nPageNo, int nPageid);
+FPDFCONVERT_EXPORT void CPDF_EndPDFToTable(FPDF_TO_TABLE table, char *pOutPath);
+
+FPDFCONVERT_EXPORT FPDF_TO_CSV CPDF_InitIMGToCSv(const char *imgPath, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, ConvertResult *result);
+FPDFCONVERT_EXPORT FPDF_TO_CSV CPDF_InitPDFToCsvByFd(const int fd, const char *name, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT FPDF_TO_CSV CPDF_InitPDFToCsvByPath(const char *strPdfFile, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT bool CPDF_InitCsvXML(FPDF_TO_CSV csv, const int convertPages);
+FPDFCONVERT_EXPORT int CPDF_StartPDFToCsv(FPDF_TO_CSV csv, int nPageNo, int nPageid);
+FPDFCONVERT_EXPORT void CPDF_EndPDFToCsv(FPDF_TO_CSV csv, char *pOutPath);
+
+FPDFCONVERT_EXPORT FPDF_TO_TXT CPDF_InitIMGToTxt(const char *imgPath, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, ConvertResult *result);
+FPDFCONVERT_EXPORT FPDF_TO_TXT CPDF_InitPDFToTxtByFd(const int fd, const char *name, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT FPDF_TO_TXT CPDF_InitPDFToTxtByPath(const char *strPdfFile, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT int CPDF_StartPDFToTxt(FPDF_TO_TXT txt, int nPageNo, int nPageid);
+FPDFCONVERT_EXPORT void CPDF_EndPDFToTxt(FPDF_TO_TXT txt, char *pOutPath);
+
+FPDFCONVERT_EXPORT FPDF_TO_IMG CPDF_InitPDFToImgByFd(const int fd, const char *name, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT FPDF_TO_IMG CPDF_InitPDFToImgByPath(const char *strPdfFile, ConverterParams &params, const char *strOutputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT int CPDF_StartPDFToImg(FPDF_TO_IMG img, int nPageNo, int nPageid);
+FPDFCONVERT_EXPORT void CPDF_EndPDFToImg(FPDF_TO_IMG img, char *pOutPath);
+
+FPDFCONVERT_EXPORT FPDF_TO_RTF CPDF_InitIMGToRTF(const char *imgPath, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, ConvertResult *result);
+FPDFCONVERT_EXPORT FPDF_TO_RTF CPDF_InitPDFToRTFByFd(const int fd, const char *name, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT FPDF_TO_RTF CPDF_InitPDFToRTFByPath(const char *strPdfFile, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT void CPDF_InitPDFToRTFPages(FPDF_TO_RTF rtf, int *pages, int len);
+FPDFCONVERT_EXPORT int CPDF_StartPDFToRTF(FPDF_TO_RTF rtf, int nPageNo, int nPageid);
+FPDFCONVERT_EXPORT void CPDF_EndPDFToRTF(FPDF_TO_RTF rtf, char *pOutPath);
+
+FPDFCONVERT_EXPORT FPDF_TO_HTML CPDF_InitIMGToHTML(const char *imgPath, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, ConvertResult *result);
+FPDFCONVERT_EXPORT FPDF_TO_HTML CPDF_InitPDFToHTMLByFd(const int fd, const char *name, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT FPDF_TO_HTML CPDF_InitPDFToHTMLByPath(const char *strPdfFile, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT int CPDF_StartPDFToHTML(FPDF_TO_HTML html, int nPageNo, int nPageid);
+FPDFCONVERT_EXPORT void CPDF_EndPDFToHTML(FPDF_TO_HTML html, char *pOutPath);
+
+FPDFCONVERT_EXPORT FPDF_HANDLE CPDF_InitPDFExtractTextByFd(const int fd, const char *name, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT FPDF_HANDLE CPDF_InitPDFExtractTextByPath(const char *strPdfFile, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT int CPDF_StartPDFExtractText(FPDF_HANDLE handle, int nPageNo, int nPageid);
+FPDFCONVERT_EXPORT void CPDF_EndPDFExtractText(FPDF_HANDLE handle, char *pOutPath);
+
+FPDFCONVERT_EXPORT FPDF_HANDLE CPDF_InitPDFExtractTableByFd(const int fd, const char *name, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT FPDF_HANDLE CPDF_InitPDFExtractTableByPath(const char *strPdfFile, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT int CPDF_StartPDFExtractTable(FPDF_HANDLE handle, int nPageNo, int nPageid);
+FPDFCONVERT_EXPORT void CPDF_EndPDFExtractTable(FPDF_HANDLE handle, char *pOutPath);
+
+FPDFCONVERT_EXPORT FPDF_HANDLE CPDF_InitPDFExtractPDFByFd(const int fd, const char *name, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT FPDF_HANDLE CPDF_InitPDFExtractPDFByPath(const char *strPdfFile, ConverterParams &params, const char *outputDir, const char *outputFilenameNoSuffix, const char *strUserPwd, ConvertResult *result);
+FPDFCONVERT_EXPORT int CPDF_StartPDFExtractPDF(FPDF_HANDLE handle, int nPageNo, int nPageid);
+FPDFCONVERT_EXPORT void CPDF_EndPDFExtractPDF(FPDF_HANDLE handle, char *pOutPath);
+
+#ifdef USEOCR
+FPDFCONVERT_EXPORT FPDF_HANDLE CPDF_InitPDFToSearchableByFd(const int fd, const char* name, ConverterParams& params, const char* outputDir, const char* outputFilenameNoSuffix, const char* strUserPwd, ConvertResult* result);
+FPDFCONVERT_EXPORT FPDF_HANDLE CPDF_InitPDFToSearchableByPath(const char* strPdfFile, ConverterParams& params, const char* outputDir, const char* outputFilenameNoSuffix, const char* strUserPwd, ConvertResult* result);
+FPDFCONVERT_EXPORT int CPDF_StartPDFToSearchable(FPDF_HANDLE handle, int nPageNo, int nPageid);
+FPDFCONVERT_EXPORT void CPDF_EndPDFToSearchable(FPDF_HANDLE handle, char *pOutPath);
+#endif
+
+FPDFCONVERT_EXPORT int CPDF_GetMajorNumber();
+FPDFCONVERT_EXPORT int CPDF_GetMinorNumber();
+FPDFCONVERT_EXPORT int CPDF_GetRevisionNumber();
+FPDFCONVERT_EXPORT const char* CPDF_GetVersion();
+FPDFCONVERT_EXPORT const char * CPDF_GetBuildTag();
+
+FPDFCONVERT_EXPORT void CPDF_EnableLog(bool is_enable, char* filename, bool is_standOutput);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // PUBLIC_FPDFCONVERT_H_