mirror of
https://gitee.com/sogou/srpc.git
synced 2024-12-21 16:14:54 +08:00
commit
f84d95711a
@ -1602,7 +1602,9 @@ public:
|
||||
|
||||
std::string server_impl_method_format = R"(
|
||||
void %s(%s *request, %s *response, srpc::RPCContext *ctx) override
|
||||
{}
|
||||
{
|
||||
// TODO: fill server logic here
|
||||
}
|
||||
)";
|
||||
|
||||
std::string server_main_begin_format = R"(
|
||||
|
30
tools/CMakeLists.txt
Normal file
30
tools/CMakeLists.txt
Normal file
@ -0,0 +1,30 @@
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
|
||||
project(srpc-ctl
|
||||
VERSION 0.9.8
|
||||
LANGUAGES C CXX
|
||||
)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_BUILD_TYPE RelWithDebInfo)
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR})
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR})
|
||||
|
||||
set(generator_code
|
||||
"../src/generator/generator.cc"
|
||||
"../src/generator/parser.cc")
|
||||
|
||||
set(srpc_ctl_code
|
||||
"srpc_ctl.cc"
|
||||
"srpc_config.cc"
|
||||
"srpc_controller.cc"
|
||||
"srpc_basic_controller.cc"
|
||||
"srpc_rpc_controller.cc"
|
||||
"srpc_proxy_controller.cc")
|
||||
|
||||
include_directories("../src/")
|
||||
add_executable(srpc ${srpc_ctl_code} ${generator_code})
|
||||
target_link_libraries(srpc ${LIBRARY_NAME})
|
||||
|
17
tools/GNUmakefile
Normal file
17
tools/GNUmakefile
Normal file
@ -0,0 +1,17 @@
|
||||
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
BUILD_DIR := build.cmake
|
||||
OUTPUT := srpc-ctl
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: base
|
||||
make -C $(BUILD_DIR) -f Makefile
|
||||
|
||||
base:
|
||||
mkdir -p $(BUILD_DIR)
|
||||
cd $(BUILD_DIR) && cmake -D CMAKE_BUILD_TYPE=Debug $(ROOT_DIR)
|
||||
|
||||
clean:
|
||||
make -C $(BUILD_DIR) clean
|
||||
rm -rf $(BUILD_DIR)
|
||||
|
405
tools/README.md
Normal file
405
tools/README.md
Normal file
@ -0,0 +1,405 @@
|
||||
[English version](README_en.md)
|
||||
|
||||
# srpc小工具
|
||||
|
||||
### 一个帮你生成Workflow和SRPC项目/脚手架的小工具。
|
||||
|
||||
#### 1. 编译
|
||||
|
||||
先从github上把srpc项目拷贝下来,小工具代码在srpc/tools/目录下,执行`make`即可编译。
|
||||
|
||||
```
|
||||
git clone --recursive https://github.com/sogou/srpc.git
|
||||
cd srpc/tools
|
||||
make
|
||||
```
|
||||
|
||||
#### 2. 用法
|
||||
|
||||
执行`./srpc`即可看到小工具的用法介绍:
|
||||
|
||||
```
|
||||
./srpc
|
||||
```
|
||||
|
||||
```
|
||||
Description:
|
||||
A handy generator for Workflow and SRPC project.
|
||||
|
||||
Usage:
|
||||
./srpc <COMMAND> <PROJECT_NAME> [FLAGS]
|
||||
|
||||
Available Commands:
|
||||
"http" - create project with both client and server
|
||||
"redis" - create project with both client and server
|
||||
"rpc" - create project with both client and server
|
||||
"proxy" - create proxy for some client and server protocol
|
||||
"file" - create project with file service
|
||||
```
|
||||
|
||||
#### 3. 入门
|
||||
|
||||
|
||||
我们先从最简单的命令开始入门:
|
||||
|
||||
```sh
|
||||
./srpc http project_name1
|
||||
```
|
||||
|
||||
然后就可以看到屏幕上显示,项目建立在了新目录`./project_name1/`中,并且带有编译和执行的提示命令。
|
||||
|
||||
```
|
||||
Success:
|
||||
make project path " ./project_name1/ " done.
|
||||
|
||||
Commands:
|
||||
cd ./project_name1/
|
||||
make -j
|
||||
|
||||
Execute:
|
||||
./server
|
||||
./client
|
||||
```
|
||||
|
||||
打开目录,我们查看一下有什么文件:
|
||||
|
||||
```
|
||||
cd ./project_name1/ && tree
|
||||
```
|
||||
|
||||
```
|
||||
.
|
||||
├── CMakeLists.txt
|
||||
├── GNUmakefile
|
||||
├── client.conf
|
||||
├── client_main.cc
|
||||
├── config
|
||||
│ ├── Json.cc
|
||||
│ ├── Json.h
|
||||
│ ├── config.cc
|
||||
│ ├── config.h
|
||||
│ └── util.h
|
||||
├── example.conf
|
||||
├── server.conf
|
||||
└── server_main.cc
|
||||
|
||||
2 directories, 12 files
|
||||
```
|
||||
|
||||
然后我们就可以根据上面执行`srpc`命令时所看到的指引,编译和执行这个项目。
|
||||
|
||||
#### 4. HTTP
|
||||
|
||||
创建HTTP项目的用法如下,可以创建http协议的server和client。其中server和client里的示例代码都可以自行改动,配置文件`server.conf`和`client.conf`里也可以指定基本的配置项,cmake编译文件都已经生成好了,整个项目可以直接拿走使用。
|
||||
|
||||
```
|
||||
./srpc http
|
||||
```
|
||||
|
||||
```
|
||||
Missing: PROJECT_NAME
|
||||
|
||||
Usage:
|
||||
./srpc http <PROJECT_NAME> [FLAGS]
|
||||
|
||||
Available Flags:
|
||||
-o : project output path (default: CURRENT_PATH)
|
||||
-d : path of dependencies (default: COMPILE_PATH)
|
||||
```
|
||||
|
||||
#### 5. RPC
|
||||
|
||||
创建RPC项目的用法如下,包括了多种rpc协议,protobuf或thrift的文件:
|
||||
|
||||
```
|
||||
./srpc rpc
|
||||
```
|
||||
|
||||
```
|
||||
Missing: PROJECT_NAME
|
||||
|
||||
Usage:
|
||||
./srpc rpc <PROJECT_NAME> [FLAGS]
|
||||
|
||||
Available Flags:
|
||||
-r : rpc type [ SRPC | SRPCHttp | BRPC | Thrift | ThriftHttp | TRPC | TRPCHttp ] (default: SRPC)
|
||||
-o : project output path (default: CURRENT_PATH)
|
||||
-s : service name (default: PROJECT_NAME)
|
||||
-i : idl type [ protobuf | thrift ] (default: protobuf)
|
||||
-x : data type [ protobuf | thrift | json ] (default: idl type. json for http)
|
||||
-c : compress type [ gzip | zlib | snappy | lz4 ] (default: no compression)
|
||||
-d : path of dependencies (default: COMPILE_PATH)
|
||||
-f : specify the idl_file to generate codes (default: template/rpc/IDL_FILE)
|
||||
-p : specify the path for idl_file to depend (default: template/rpc/)
|
||||
```
|
||||
|
||||
我们通过以下命令,试一下指定要创建的RPC项目所依赖的proto文件:
|
||||
|
||||
```
|
||||
./srpc rpc rpc_example -f test_proto/test.proto
|
||||
```
|
||||
|
||||
然后就可以看到一些生成代码多信息,这和原先使用`srpc_genrator`时所看到的是类似的。
|
||||
|
||||
```
|
||||
Info: srpc generator begin.
|
||||
proto file: [/root/srpc/tools/rpc_example/test.proto]
|
||||
Successfully parse service block [message] : EchoRequest
|
||||
Successfully parse service block [message] : EchoResponse
|
||||
Successfully parse service block [service] : new_test
|
||||
Successfully parse method:Echo req:EchoRequest resp:EchoResponse
|
||||
finish parsing proto file: [/root/srpc/tools/rpc_example/test.proto]
|
||||
[Generator] generate srpc files: /root/srpc/tools/rpc_example/test.srpc.h
|
||||
[Generator Done]
|
||||
[Generator] generate server files: /root/srpc/tools/rpc_example/server_main.cc, client files: /root/srpc/tools/rpc_example/client_main.cc
|
||||
Info: srpc generator done.
|
||||
|
||||
Success:
|
||||
make project path " /root/srpc/tools/rpc_example/ " done.
|
||||
|
||||
Commands:
|
||||
cd /root/srpc/tools/rpc_example/
|
||||
make -j
|
||||
|
||||
Execute:
|
||||
./server
|
||||
./client
|
||||
```
|
||||
|
||||
#### 6. REDIS
|
||||
|
||||
创建REDIS协议的client和server,命令如下:
|
||||
|
||||
```
|
||||
./srpc redis
|
||||
```
|
||||
|
||||
will get the following instructions:
|
||||
|
||||
```
|
||||
Missing: PROJECT_NAME
|
||||
|
||||
Usage:
|
||||
./srpc redis <PROJECT_NAME> [FLAGS]
|
||||
|
||||
Available Flags:
|
||||
-o : project output path (default: CURRENT_PATH)
|
||||
-d : path of dependencies (default: COMPILE_PATH)
|
||||
```
|
||||
|
||||
根据以上指引我们创建了一个项目后,就可以得到最简单的redis server和client。client就简单地实现了发送`SET k1 v1`命令,而server无论收到什么都会简单地回复一个`OK`。我们可以用这简单的示例,改造一个可以请求任何redis协议服务的client,也可以构造一个简单的redis服务器。
|
||||
|
||||
```
|
||||
./server
|
||||
|
||||
Redis server start, port 6379
|
||||
redis server get cmd: [SET] from peer address: 127.0.0.1:60665, seq: 0.
|
||||
```
|
||||
|
||||
```
|
||||
./client
|
||||
|
||||
Redis client state = 0 error = 0
|
||||
response: OK
|
||||
```
|
||||
|
||||
如果client有填写用户名和密码的需求,可以填到`client.conf`中。我们打开这个配置文件看看:
|
||||
|
||||
```
|
||||
1 {
|
||||
2 "client":
|
||||
3 {
|
||||
4 "remote_host": "127.0.0.1",
|
||||
5 "remote_port": 6379,
|
||||
6 "retry_max": 2,
|
||||
7 "user_name": "root",
|
||||
8 "password": ""
|
||||
9 }
|
||||
10 }
|
||||
```
|
||||
|
||||
#### 7. PROXY
|
||||
|
||||
这个命令用于构建一个转发服务器,并且还有与其协议相关的server和client。
|
||||
|
||||
```
|
||||
./srpc proxy
|
||||
```
|
||||
|
||||
执行上述命令,我们可以看到proxy命令的指引:
|
||||
|
||||
```
|
||||
Missing: PROJECT_NAME
|
||||
|
||||
Usage:
|
||||
./srpc proxy <PROJECT_NAME> [FLAGS]
|
||||
|
||||
Available Flags:
|
||||
-c : client type for proxy [ Http | Redis | SRPC | SRPCHttp | BRPC | Thrift | ThriftHttp | TRPC | TRPCHttp ] (default: Http)
|
||||
-s : server type for proxy [ Http | Redis | SRPC | SRPCHttp | BRPC | Thrift | ThriftHttp | TRPC | TRPCHttp ] (default: Http)
|
||||
-o : project output path (default: CURRENT_PATH)
|
||||
-d : path of dependencies (default: COMPILE_PATH)
|
||||
```
|
||||
|
||||
让我们来试一下,创建一个转发不同协议的项目:
|
||||
|
||||
```
|
||||
./srpc proxy srpc_trpc_proxy_example -c SRPC -s TRPC
|
||||
```
|
||||
```
|
||||
Success:
|
||||
make project path " ./srpc_trpc_proxy_example/ " done.
|
||||
|
||||
Commands:
|
||||
cd ./srpc_trpc_proxy_example/
|
||||
make -j
|
||||
|
||||
Execute:
|
||||
./server
|
||||
./proxy
|
||||
./client
|
||||
```
|
||||
查看新创建的项目中有什么文件:
|
||||
|
||||
```
|
||||
cd srpc_trpc_proxy_example && tree
|
||||
```
|
||||
|
||||
```
|
||||
.
|
||||
├── CMakeLists.txt
|
||||
├── GNUmakefile
|
||||
├── client.conf
|
||||
├── client_main.cc
|
||||
├── config
|
||||
│ ├── Json.cc
|
||||
│ ├── Json.h
|
||||
│ ├── config.cc
|
||||
│ └── config.h
|
||||
├── proxy.conf
|
||||
├── proxy_main.cc
|
||||
├── server.conf
|
||||
├── server_main.cc
|
||||
└── srpc_trpc_proxy_example.proto
|
||||
|
||||
2 directories, 13 files
|
||||
```
|
||||
|
||||
分别在三个终端执行`./server` `./proxy` 和 `./client`,我们可以看到,client发送了一个trpc协议的请求"Hello, srpc!"给proxy,而proxy收到之后把请求用srpc协议发给了server。SRPC server填了回复"Hi back"并通过刚才的proxy路线转回给了client,期间转发纯异步,不会阻塞任何线程。
|
||||
|
||||
```
|
||||
./server
|
||||
|
||||
srpc_trpc_proxy_example TRPC server start, port 1412
|
||||
get req: message: "Hello, srpc!"
|
||||
```
|
||||
|
||||
```
|
||||
./proxy
|
||||
|
||||
srpc_trpc_proxy_example [SRPC]-[TRPC] proxy start, port 1411
|
||||
srpc_trpc_proxy_example proxy get request from client. ip : 127.0.0.1
|
||||
message: "Hello, srpc!"
|
||||
```
|
||||
|
||||
```
|
||||
./client
|
||||
message: "Hi back"
|
||||
```
|
||||
|
||||
#### 8. FILE COMMAND
|
||||
|
||||
这是一个简单的文件服务器:
|
||||
|
||||
```
|
||||
./srpc file file_project
|
||||
```
|
||||
|
||||
```
|
||||
Success:
|
||||
make project path " ./file_project/ " done.
|
||||
|
||||
Commands:
|
||||
cd ./file_project/
|
||||
make -j
|
||||
|
||||
Execute:
|
||||
./server
|
||||
|
||||
Try file service:
|
||||
curl localhost:8080/index.html
|
||||
curl -i localhost:8080/a/b/
|
||||
```
|
||||
我们通过上述命令创建之后,可以看看文件服务器的目录结构如下:
|
||||
|
||||
```
|
||||
.
|
||||
├── CMakeLists.txt
|
||||
├── GNUmakefile
|
||||
├── config
|
||||
│ ├── Json.cc
|
||||
│ ├── Json.h
|
||||
│ ├── config.cc
|
||||
│ └── config.h
|
||||
├── file_service.cc
|
||||
├── file_service.h
|
||||
├── html
|
||||
│ ├── 404.html
|
||||
│ ├── 50x.html
|
||||
│ └── index.html
|
||||
├── server.conf
|
||||
└── server_main.cc
|
||||
|
||||
3 directories, 13 files
|
||||
```
|
||||
打开`server.conf`,就可以看到我们为文件服务器添加的具体配置项:`root`和`error_page`。我们可以通过root去指定打开文件的根目录,以及通过error_page去关联具体的错误码和它们所要返回作body的页面名称。
|
||||
|
||||
```
|
||||
1 {
|
||||
2 "server":
|
||||
3 {
|
||||
4 "port": 8080,
|
||||
5 "root": "./html/",
|
||||
6 "error_page" : [
|
||||
7 {
|
||||
8 "error" : [ 404 ],
|
||||
9 "page" : "404.html"
|
||||
10 },
|
||||
11 {
|
||||
12 "error" : [ 500, 502, 503, 504],
|
||||
13 "page" : "50x.html"
|
||||
14 }
|
||||
15 ]
|
||||
16 }
|
||||
17 }
|
||||
```
|
||||
|
||||
我们执行`make`进行编译,然后执行`./server`把文件服务器跑起来,然后用`curl`进行测试:
|
||||
|
||||
示例1:在根目录`./html/`下读取文件`index.html`(即使请求localhost:8080,默认也是读index.html)。
|
||||
```
|
||||
curl localhost:8080/index.html
|
||||
|
||||
<html>Hello from workflow and srpc file server!</html>
|
||||
```
|
||||
|
||||
示例2:读文件`/a/b/`,这个文件不存在,所以我们根据上面配置文件`server.conf`中所指定的,填入`404`错误码会返回页面`404.html`的内容。
|
||||
|
||||
```
|
||||
curl -i localhost:8080/a/b/
|
||||
HTTP/1.1 404 Not Found
|
||||
Server: SRPC HTTP File Server
|
||||
Content-Length: 59
|
||||
Connection: Keep-Alive
|
||||
|
||||
<html>This is not the web page you are looking for.</html>
|
||||
```
|
||||
|
||||
以下信息在server端可以看到:
|
||||
|
||||
```
|
||||
./server
|
||||
http file service start, port 8080
|
||||
file service get request: /a/b/
|
||||
```
|
||||
|
410
tools/README_en.md
Normal file
410
tools/README_en.md
Normal file
@ -0,0 +1,410 @@
|
||||
[中文版入口](README.md)
|
||||
|
||||
# srpc tools
|
||||
|
||||
### An easy tool to generate Workflow and SRPC project
|
||||
|
||||
#### 1. COMPILE
|
||||
|
||||
```
|
||||
git clone --recursive https://github.com/sogou/srpc.git
|
||||
cd srpc/tools
|
||||
make
|
||||
```
|
||||
|
||||
#### 2. USAGE
|
||||
|
||||
```
|
||||
./srpc
|
||||
```
|
||||
|
||||
```
|
||||
Description:
|
||||
A handy generator for Workflow and SRPC project.
|
||||
|
||||
Usage:
|
||||
./srpc <COMMAND> <PROJECT_NAME> [FLAGS]
|
||||
|
||||
Available Commands:
|
||||
"http" - create project with both client and server
|
||||
"redis" - create project with both client and server
|
||||
"rpc" - create project with both client and server
|
||||
"proxy" - create proxy for some client and server protocol
|
||||
"file" - create project with file service
|
||||
```
|
||||
|
||||
#### 3. START
|
||||
|
||||
Execute this simple example
|
||||
|
||||
```sh
|
||||
./srpc http project_name1
|
||||
```
|
||||
|
||||
And we will get this on the screen, new project is in `./project_name1/`.
|
||||
|
||||
```
|
||||
Success:
|
||||
make project path " ./project_name1/ " done.
|
||||
|
||||
Commands:
|
||||
cd ./project_name1/
|
||||
make -j
|
||||
|
||||
Execute:
|
||||
./server
|
||||
./client
|
||||
```
|
||||
|
||||
Let's take a look at the project:
|
||||
|
||||
```
|
||||
cd ./project_name1/ && tree
|
||||
```
|
||||
|
||||
These files are generated.
|
||||
|
||||
```
|
||||
.
|
||||
├── CMakeLists.txt
|
||||
├── GNUmakefile
|
||||
├── client.conf
|
||||
├── client_main.cc
|
||||
├── config
|
||||
│ ├── Json.cc
|
||||
│ ├── Json.h
|
||||
│ ├── config.cc
|
||||
│ ├── config.h
|
||||
│ └── util.h
|
||||
├── example.conf
|
||||
├── server.conf
|
||||
└── server_main.cc
|
||||
|
||||
2 directories, 12 files
|
||||
```
|
||||
|
||||
And we can try to make the project accorrding to the suggestions above.
|
||||
|
||||
#### 4. HTTP COMMAND
|
||||
|
||||
commands for HTTP:
|
||||
|
||||
```
|
||||
./srpc http
|
||||
```
|
||||
|
||||
will get the following instructions:
|
||||
|
||||
```
|
||||
Missing: PROJECT_NAME
|
||||
|
||||
Usage:
|
||||
./srpc http <PROJECT_NAME> [FLAGS]
|
||||
|
||||
Available Flags:
|
||||
-o : project output path (default: CURRENT_PATH)
|
||||
-d : path of dependencies (default: COMPILE_PATH)
|
||||
```
|
||||
|
||||
#### 5. RPC COMMAND
|
||||
|
||||
commands for RPCs:
|
||||
|
||||
```
|
||||
./srpc rpc
|
||||
```
|
||||
|
||||
will get the following instructions:
|
||||
|
||||
```
|
||||
Missing: PROJECT_NAME
|
||||
|
||||
Usage:
|
||||
./srpc rpc <PROJECT_NAME> [FLAGS]
|
||||
|
||||
Available Flags:
|
||||
-r : rpc type [ SRPC | SRPCHttp | BRPC | Thrift | ThriftHttp | TRPC | TRPCHttp ] (default: SRPC)
|
||||
-o : project output path (default: CURRENT_PATH)
|
||||
-s : service name (default: PROJECT_NAME)
|
||||
-i : idl type [ protobuf | thrift ] (default: protobuf)
|
||||
-x : data type [ protobuf | thrift | json ] (default: idl type. json for http)
|
||||
-c : compress type [ gzip | zlib | snappy | lz4 ] (default: no compression)
|
||||
-d : path of dependencies (default: COMPILE_PATH)
|
||||
-f : specify the idl_file to generate codes (default: template/rpc/IDL_FILE)
|
||||
-p : specify the path for idl_file to depend (default: template/rpc/)
|
||||
```
|
||||
|
||||
We can specified our IDL files with the following command:
|
||||
|
||||
```
|
||||
./srpc rpc rpc_example -f test_proto/test.proto
|
||||
```
|
||||
|
||||
And we can see the infomation when generating files, which is similar to srpc_genrator.
|
||||
|
||||
```
|
||||
Info: srpc generator begin.
|
||||
proto file: [/root/srpc/tools/rpc_example/test.proto]
|
||||
Successfully parse service block [message] : EchoRequest
|
||||
Successfully parse service block [message] : EchoResponse
|
||||
Successfully parse service block [service] : new_test
|
||||
Successfully parse method:Echo req:EchoRequest resp:EchoResponse
|
||||
finish parsing proto file: [/root/srpc/tools/rpc_example/test.proto]
|
||||
[Generator] generate srpc files: /root/srpc/tools/rpc_example/test.srpc.h
|
||||
[Generator Done]
|
||||
[Generator] generate server files: /root/srpc/tools/rpc_example/server_main.cc, client files: /root/srpc/tools/rpc_example/client_main.cc
|
||||
Info: srpc generator done.
|
||||
|
||||
Success:
|
||||
make project path " /root/srpc/tools/rpc_example/ " done.
|
||||
|
||||
Commands:
|
||||
cd /root/srpc/tools/rpc_example/
|
||||
make -j
|
||||
|
||||
Execute:
|
||||
./server
|
||||
./client
|
||||
```
|
||||
|
||||
#### 6. REDIS COMMAND
|
||||
|
||||
commands for REDIS:
|
||||
|
||||
```
|
||||
./srpc redis
|
||||
```
|
||||
|
||||
will get the following instructions:
|
||||
|
||||
```
|
||||
Missing: PROJECT_NAME
|
||||
|
||||
Usage:
|
||||
./srpc redis <PROJECT_NAME> [FLAGS]
|
||||
|
||||
Available Flags:
|
||||
-o : project output path (default: CURRENT_PATH)
|
||||
-d : path of dependencies (default: COMPILE_PATH)
|
||||
```
|
||||
|
||||
Make a project with the instructions, we can get the simple redis server and client. The client will send a basic command `SET k1 v1`, and the server will reply `OK` for every request.
|
||||
|
||||
```
|
||||
./server
|
||||
|
||||
Redis server start, port 6379
|
||||
redis server get cmd: [SET] from peer address: 127.0.0.1:60665, seq: 0.
|
||||
```
|
||||
|
||||
```
|
||||
./client
|
||||
|
||||
Redis client state = 0 error = 0
|
||||
response: OK
|
||||
```
|
||||
|
||||
If there is user name and password for redis server, client may fill them into client.conf:
|
||||
|
||||
```
|
||||
1 {
|
||||
2 "client":
|
||||
3 {
|
||||
4 "remote_host": "127.0.0.1",
|
||||
5 "remote_port": 6379,
|
||||
6 "retry_max": 2,
|
||||
7 "user_name": "root",
|
||||
8 "password": ""
|
||||
9 }
|
||||
10 }
|
||||
```
|
||||
|
||||
#### 7. PROXY COMMAND
|
||||
|
||||
commands for PROXY:
|
||||
|
||||
```
|
||||
./srpc proxy
|
||||
```
|
||||
|
||||
will get the following instructions:
|
||||
|
||||
```
|
||||
Missing: PROJECT_NAME
|
||||
|
||||
Usage:
|
||||
./srpc proxy <PROJECT_NAME> [FLAGS]
|
||||
|
||||
Available Flags:
|
||||
-c : client type for proxy [ Http | Redis | SRPC | SRPCHttp | BRPC | Thrift | ThriftHttp | TRPC | TRPCHttp ] (default: Http)
|
||||
-s : server type for proxy [ Http | Redis | SRPC | SRPCHttp | BRPC | Thrift | ThriftHttp | TRPC | TRPCHttp ] (default: Http)
|
||||
-o : project output path (default: CURRENT_PATH)
|
||||
-d : path of dependencies (default: COMPILE_PATH)
|
||||
```
|
||||
|
||||
Let's make a project with diffrent protocol:
|
||||
|
||||
```
|
||||
./srpc proxy srpc_trpc_proxy_example -c SRPC -s TRPC
|
||||
```
|
||||
```
|
||||
Success:
|
||||
make project path " ./srpc_trpc_proxy_example/ " done.
|
||||
|
||||
Commands:
|
||||
cd ./srpc_trpc_proxy_example/
|
||||
make -j
|
||||
|
||||
Execute:
|
||||
./server
|
||||
./proxy
|
||||
./client
|
||||
```
|
||||
|
||||
Check the files in directory:
|
||||
|
||||
```
|
||||
cd srpc_trpc_proxy_example && tree
|
||||
```
|
||||
|
||||
```
|
||||
.
|
||||
├── CMakeLists.txt
|
||||
├── GNUmakefile
|
||||
├── client.conf
|
||||
├── client_main.cc
|
||||
├── config
|
||||
│ ├── Json.cc
|
||||
│ ├── Json.h
|
||||
│ ├── config.cc
|
||||
│ └── config.h
|
||||
├── proxy.conf
|
||||
├── proxy_main.cc
|
||||
├── server.conf
|
||||
├── server_main.cc
|
||||
└── srpc_trpc_proxy_example.proto
|
||||
|
||||
2 directories, 13 files
|
||||
```
|
||||
|
||||
Execute `./server` `./proxy` and `./client` on the three sessions respectively, and we will see that the client sends a trpc protocol request "Hello, srpc!" to the proxy, and the proxy receives the request and send to server as srpc protocol. SRPC server fill the response "Hi back" and will finnally transfer back to client by proxy.
|
||||
|
||||
```
|
||||
./server
|
||||
|
||||
srpc_trpc_proxy_example TRPC server start, port 1412
|
||||
get req: message: "Hello, srpc!"
|
||||
```
|
||||
|
||||
```
|
||||
./proxy
|
||||
|
||||
srpc_trpc_proxy_example [SRPC]-[TRPC] proxy start, port 1411
|
||||
srpc_trpc_proxy_example proxy get request from client. ip : 127.0.0.1
|
||||
message: "Hello, srpc!"
|
||||
```
|
||||
|
||||
```
|
||||
./client
|
||||
message: "Hi back"
|
||||
```
|
||||
|
||||
#### 8. FILE COMMAND
|
||||
|
||||
This is an example to make a file service. We try the command like the following:
|
||||
```
|
||||
./srpc file file_project
|
||||
```
|
||||
|
||||
will get the following infomation:
|
||||
|
||||
```
|
||||
Success:
|
||||
make project path " ./file_project/ " done.
|
||||
|
||||
Commands:
|
||||
cd ./file_project/
|
||||
make -j
|
||||
|
||||
Execute:
|
||||
./server
|
||||
|
||||
Try file service:
|
||||
curl localhost:8080/index.html
|
||||
curl -i localhost:8080/a/b/
|
||||
```
|
||||
|
||||
Check the files in directories:
|
||||
|
||||
```
|
||||
.
|
||||
├── CMakeLists.txt
|
||||
├── GNUmakefile
|
||||
├── config
|
||||
│ ├── Json.cc
|
||||
│ ├── Json.h
|
||||
│ ├── config.cc
|
||||
│ └── config.h
|
||||
├── file_service.cc
|
||||
├── file_service.h
|
||||
├── html
|
||||
│ ├── 404.html
|
||||
│ ├── 50x.html
|
||||
│ └── index.html
|
||||
├── server.conf
|
||||
└── server_main.cc
|
||||
|
||||
3 directories, 13 files
|
||||
```
|
||||
|
||||
Also check the `server.conf`, we can see the `root` and `error_page` are added into file service. We can specify the root to find files for this service, and fill the page coresponding to the error codes.
|
||||
|
||||
```
|
||||
1 {
|
||||
2 "server":
|
||||
3 {
|
||||
4 "port": 8080,
|
||||
5 "root": "./html/",
|
||||
6 "error_page" : [
|
||||
7 {
|
||||
8 "error" : [ 404 ],
|
||||
9 "page" : "404.html"
|
||||
10 },
|
||||
11 {
|
||||
12 "error" : [ 500, 502, 503, 504],
|
||||
13 "page" : "50x.html"
|
||||
14 }
|
||||
15 ]
|
||||
16 }
|
||||
17 }
|
||||
```
|
||||
|
||||
After we execute `make` and run the server with `./server`, we can make requests with `curl`:
|
||||
|
||||
example 1: read the file `index.html` in root path `./html/`.
|
||||
```
|
||||
curl localhost:8080/index.html
|
||||
|
||||
<html>Hello from workflow and srpc file server!</html>
|
||||
```
|
||||
|
||||
example 2: read the file `/a/b/`. This does't exist, so the error page `404.html` for `404` will be return as we fill them into `server.conf` above.
|
||||
|
||||
```
|
||||
curl -i localhost:8080/a/b/
|
||||
HTTP/1.1 404 Not Found
|
||||
Server: SRPC HTTP File Server
|
||||
Content-Length: 59
|
||||
Connection: Keep-Alive
|
||||
|
||||
<html>This is not the web page you are looking for.</html>
|
||||
```
|
||||
|
||||
The log printed by server:
|
||||
|
||||
```
|
||||
./server
|
||||
http file service start, port 8080
|
||||
file service get request: /a/b/
|
||||
```
|
||||
|
421
tools/srpc_basic_controller.cc
Normal file
421
tools/srpc_basic_controller.cc
Normal file
@ -0,0 +1,421 @@
|
||||
/*
|
||||
Copyright (c) 2022 Sogou, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "srpc_controller.h"
|
||||
|
||||
using DEFAULT_FILES = std::vector<struct CommandController::file_info>;
|
||||
|
||||
static std::string server_process_codes(uint8_t type)
|
||||
{
|
||||
if (type == PROTOCOL_TYPE_HTTP)
|
||||
return std::string(R"(
|
||||
fprintf(stderr, "http server get request_uri: %s\n",
|
||||
task->get_req()->get_request_uri());
|
||||
print_peer_address<WFHttpTask>(task);
|
||||
|
||||
task->get_resp()->append_output_body("<html>Hello from server!</html>");
|
||||
)");
|
||||
|
||||
if (type == PROTOCOL_TYPE_REDIS)
|
||||
return std::string(R"(
|
||||
protocol::RedisRequest *req = task->get_req();
|
||||
protocol::RedisResponse *resp = task->get_resp();
|
||||
protocol::RedisValue val;
|
||||
std::string cmd;
|
||||
|
||||
if (req->parse_success() == false || req->get_command(cmd) == false)
|
||||
return;
|
||||
|
||||
fprintf(stderr, "redis server get cmd: [%s] from ", cmd.c_str());
|
||||
print_peer_address<WFRedisTask>(task);
|
||||
|
||||
val.set_status("OK"); // example: return OK to every requests
|
||||
resp->set_result(val);
|
||||
)");
|
||||
|
||||
return std::string("Unknown type");
|
||||
}
|
||||
|
||||
static std::string client_redirect_codes(uint8_t type)
|
||||
{
|
||||
if (type == PROTOCOL_TYPE_HTTP)
|
||||
return std::string(R"(
|
||||
config.redirect_max(),)");
|
||||
|
||||
return std::string("");
|
||||
}
|
||||
|
||||
static std::string client_task_callback_codes(uint8_t type)
|
||||
{
|
||||
if (type == PROTOCOL_TYPE_HTTP)
|
||||
return std::string(R"(
|
||||
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);
|
||||
fwrite(body, 1, body_len, stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
)");
|
||||
|
||||
if (type == PROTOCOL_TYPE_REDIS)
|
||||
return std::string(R"(
|
||||
protocol::RedisResponse *resp = task->get_resp();
|
||||
protocol::RedisValue val;
|
||||
|
||||
if (state == WFT_STATE_SUCCESS && resp->parse_success() == true)
|
||||
{
|
||||
resp->get_result(val);
|
||||
fprintf(stderr, "response: %s\n", val.string_value().c_str());
|
||||
}
|
||||
)");
|
||||
|
||||
return std::string("Unknown type");
|
||||
}
|
||||
|
||||
static std::string client_set_request_codes(uint8_t type)
|
||||
{
|
||||
if (type == PROTOCOL_TYPE_HTTP)
|
||||
return std::string(R"(
|
||||
protocol::HttpRequest *req = task->get_req();
|
||||
req->set_request_uri("/client_request"); // will send to server by proxy
|
||||
)");
|
||||
|
||||
if (type == PROTOCOL_TYPE_REDIS)
|
||||
return std::string(R"(
|
||||
task->get_req()->set_request("SET", {"k1", "v1"});
|
||||
)");
|
||||
|
||||
return std::string("Unknown type");
|
||||
}
|
||||
|
||||
static std::string username_passwd_codes(uint8_t type)
|
||||
{
|
||||
if (type == PROTOCOL_TYPE_REDIS || type == PROTOCOL_TYPE_MYSQL)
|
||||
return std::string(R"(config.client_user_name() +
|
||||
std::string(":") + config.client_password() +
|
||||
std::string("@") +)");
|
||||
|
||||
return std::string("");
|
||||
}
|
||||
|
||||
static uint8_t get_protocol_type(const struct srpc_config *config, uint8_t type)
|
||||
{
|
||||
if (config->type == COMMAND_HTTP ||
|
||||
(config->type == COMMAND_PROXY && type == PROTOCOL_TYPE_HTTP))
|
||||
return PROTOCOL_TYPE_HTTP;
|
||||
|
||||
if (config->type == COMMAND_REDIS ||
|
||||
(config->type == COMMAND_PROXY && type == PROTOCOL_TYPE_REDIS))
|
||||
return PROTOCOL_TYPE_REDIS;
|
||||
|
||||
if (config->type == COMMAND_MYSQL ||
|
||||
(config->type == COMMAND_PROXY && type == PROTOCOL_TYPE_MYSQL))
|
||||
return PROTOCOL_TYPE_MYSQL;
|
||||
|
||||
return PROTOCOL_TYPE_MAX;
|
||||
}
|
||||
|
||||
static inline uint8_t get_client_protocol_type(const struct srpc_config *config)
|
||||
{
|
||||
return get_protocol_type(config, config->proxy_client_type);
|
||||
}
|
||||
|
||||
static inline uint8_t get_server_protocol_type(const struct srpc_config *config)
|
||||
{
|
||||
return get_protocol_type(config, config->proxy_server_type);
|
||||
}
|
||||
|
||||
bool basic_server_config_transform(const std::string& format, FILE *out,
|
||||
const struct srpc_config *config)
|
||||
{
|
||||
unsigned short port;
|
||||
|
||||
if (get_server_protocol_type(config) == PROTOCOL_TYPE_HTTP)
|
||||
port = 80;
|
||||
else if (get_server_protocol_type(config) == PROTOCOL_TYPE_REDIS)
|
||||
port = 6379;
|
||||
else if (get_server_protocol_type(config) == PROTOCOL_TYPE_MYSQL)
|
||||
port = 3306;
|
||||
else
|
||||
port = 1412;
|
||||
|
||||
size_t len = fprintf(out, format.c_str(), port);
|
||||
|
||||
return len > 0;
|
||||
}
|
||||
|
||||
bool basic_client_config_transform(const std::string& format, FILE *out,
|
||||
const struct srpc_config *config)
|
||||
{
|
||||
unsigned short port;
|
||||
std::string redirect_code;
|
||||
std::string user_and_passwd;
|
||||
|
||||
if (get_client_protocol_type(config) == PROTOCOL_TYPE_HTTP)
|
||||
{
|
||||
port = 80;
|
||||
redirect_code = R"(
|
||||
"redirect_max": 2,)";
|
||||
}
|
||||
else if (get_client_protocol_type(config) == PROTOCOL_TYPE_REDIS)
|
||||
{
|
||||
port = 6379;
|
||||
user_and_passwd = R"(,
|
||||
"user_name": "root",
|
||||
"password": "")";
|
||||
}
|
||||
else if (get_client_protocol_type(config) == PROTOCOL_TYPE_MYSQL)
|
||||
port = 3306;
|
||||
else
|
||||
port = 1412;
|
||||
|
||||
// for proxy
|
||||
if (config->type == COMMAND_PROXY)
|
||||
port = port - 1;
|
||||
|
||||
size_t len = fprintf(out, format.c_str(), port,
|
||||
redirect_code.c_str(), user_and_passwd.c_str());
|
||||
|
||||
return len > 0;
|
||||
}
|
||||
|
||||
bool basic_server_transform(const std::string& format, FILE *out,
|
||||
const struct srpc_config *config)
|
||||
{
|
||||
uint8_t server_type = get_server_protocol_type(config);
|
||||
const char *type = get_type_string(server_type);
|
||||
size_t len = fprintf(out, format.c_str(), type, type,
|
||||
server_process_codes(server_type).c_str(),
|
||||
type, type);
|
||||
|
||||
return len > 0;
|
||||
}
|
||||
|
||||
bool basic_client_transform(const std::string& format, FILE *out,
|
||||
const struct srpc_config *config)
|
||||
{
|
||||
uint8_t client_type = get_client_protocol_type(config);
|
||||
const char *type = get_type_string(client_type);
|
||||
std::string client_lower = type;
|
||||
std::transform(client_lower.begin(), client_lower.end(),
|
||||
client_lower.begin(), ::tolower);
|
||||
|
||||
size_t len = fprintf(out, format.c_str(), type, type, type,
|
||||
client_task_callback_codes(client_type).c_str(),
|
||||
client_lower.c_str(),
|
||||
username_passwd_codes(client_type).c_str(),
|
||||
type, client_lower.c_str(),
|
||||
client_redirect_codes(client_type).c_str(),
|
||||
client_set_request_codes(client_type).c_str());
|
||||
|
||||
return len > 0;
|
||||
}
|
||||
|
||||
static void basic_default_file_initialize(DEFAULT_FILES& files)
|
||||
{
|
||||
struct CommandController::file_info info;
|
||||
|
||||
info = { "basic/server.conf", "server.conf", basic_server_config_transform };
|
||||
files.push_back(info);
|
||||
|
||||
info = { "basic/client.conf", "client.conf", basic_client_config_transform };
|
||||
files.push_back(info);
|
||||
|
||||
info = { "basic/server_main.cc", "server_main.cc", basic_server_transform };
|
||||
files.push_back(info);
|
||||
|
||||
info = { "basic/client_main.cc", "client_main.cc", basic_client_transform };
|
||||
files.push_back(info);
|
||||
|
||||
info = { "common/config.json", "example.conf", nullptr };
|
||||
files.push_back(info);
|
||||
|
||||
info = { "common/util.h", "config/util.h", nullptr };
|
||||
files.push_back(info);
|
||||
|
||||
info = { "common/CMakeLists.txt", "CMakeLists.txt", common_cmake_transform };
|
||||
files.push_back(info);
|
||||
|
||||
info = { "common/GNUmakefile", "GNUmakefile", nullptr };
|
||||
files.push_back(info);
|
||||
|
||||
info = { "config/Json.h", "config/Json.h", nullptr };
|
||||
files.push_back(info);
|
||||
|
||||
info = { "config/Json.cc", "config/Json.cc", nullptr };
|
||||
files.push_back(info);
|
||||
|
||||
info = { "config/config_simple.h", "config/config.h", nullptr };
|
||||
files.push_back(info);
|
||||
|
||||
info = { "config/config_simple.cc", "config/config.cc", nullptr };
|
||||
files.push_back(info);
|
||||
}
|
||||
|
||||
static bool basic_get_opt(int argc, const char **argv, struct srpc_config *config)
|
||||
{
|
||||
char c;
|
||||
optind = 3;
|
||||
|
||||
while ((c = getopt(argc, (char * const *)argv, "o:t:d:")) != -1)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'o':
|
||||
if (sscanf(optarg, "%s", config->output_path) != 1)
|
||||
return false;
|
||||
break;
|
||||
case 't':
|
||||
if (sscanf(optarg, "%s", config->template_path) != 1)
|
||||
return false; //TODO:
|
||||
break;
|
||||
case 'd':
|
||||
config->specified_depend_path = true;
|
||||
memset(config->depend_path, 0, MAXPATHLEN);
|
||||
if (sscanf(optarg, "%s", config->depend_path) != 1)
|
||||
return false;
|
||||
default:
|
||||
printf("Error:\n Unknown args : %s\n\n", argv[optind - 1]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void basic_print_usage(const char *name, const char *command)
|
||||
{
|
||||
printf("Usage:\n"
|
||||
" %s %s <PROJECT_NAME> [FLAGS]\n\n"
|
||||
"Available Flags:\n"
|
||||
" -o : project output path (default: CURRENT_PATH)\n"
|
||||
" -d : path of dependencies (default: COMPILE_PATH)\n"
|
||||
, name, command);
|
||||
}
|
||||
|
||||
HttpController::HttpController()
|
||||
{
|
||||
this->config.type = COMMAND_HTTP;
|
||||
basic_default_file_initialize(this->default_files);
|
||||
}
|
||||
|
||||
void HttpController::print_usage(const char *name) const
|
||||
{
|
||||
basic_print_usage(name, "http");
|
||||
}
|
||||
|
||||
bool HttpController::get_opt(int argc, const char **argv)
|
||||
{
|
||||
return basic_get_opt(argc, argv, &this->config);
|
||||
}
|
||||
|
||||
RedisController::RedisController()
|
||||
{
|
||||
this->config.type = COMMAND_REDIS;
|
||||
basic_default_file_initialize(this->default_files);
|
||||
}
|
||||
|
||||
void RedisController::print_usage(const char *name) const
|
||||
{
|
||||
basic_print_usage(name, "redis");
|
||||
}
|
||||
|
||||
bool RedisController::get_opt(int argc, const char **argv)
|
||||
{
|
||||
return basic_get_opt(argc, argv, &this->config);
|
||||
}
|
||||
|
||||
FileServiceController::FileServiceController()
|
||||
{
|
||||
this->config.type = COMMAND_FILE;
|
||||
|
||||
struct file_info info;
|
||||
|
||||
info = { "file/server.conf", "server.conf", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "file/server_main.cc", "server_main.cc", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "file/file_service.cc", "file_service.cc", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "file/file_service.h", "file_service.h", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "file/index.html", "html/index.html", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "file/404.html", "html/404.html", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "file/50x.html", "html/50x.html", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "common/CMakeLists.txt", "CMakeLists.txt", common_cmake_transform };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "common/GNUmakefile", "GNUmakefile", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "config/Json.h", "config/Json.h", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "config/Json.cc", "config/Json.cc", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "config/config_simple.h", "config/config.h", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "config/config_simple.cc", "config/config.cc", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
}
|
||||
|
||||
void FileServiceController::print_usage(const char *name) const
|
||||
{
|
||||
basic_print_usage(name, "file");
|
||||
}
|
||||
|
||||
bool FileServiceController::get_opt(int argc, const char **argv)
|
||||
{
|
||||
return basic_get_opt(argc, argv, &this->config);
|
||||
}
|
||||
|
||||
void FileServiceController::print_success_info() const
|
||||
{
|
||||
printf("Success:\n make project path \" %s \" done.\n\n",
|
||||
this->config.output_path);
|
||||
printf("Commands:\n cd %s\n make -j\n\n",
|
||||
this->config.output_path);
|
||||
printf("Execute:\n ./server\n\n");
|
||||
printf("Try file service:\n");
|
||||
printf(" curl localhost:8080/index.html\n");
|
||||
printf(" curl -i localhost:8080/a/b/\n\n");
|
||||
}
|
||||
|
492
tools/srpc_config.cc
Normal file
492
tools/srpc_config.cc
Normal file
@ -0,0 +1,492 @@
|
||||
/*
|
||||
Copyright (c) 2022 Sogou, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include "srpc_config.h"
|
||||
|
||||
std::vector<std::string> RPC_PROTOC_SKIP_FILES =
|
||||
{ "config_simple.h", "config_simple.cc",
|
||||
"rpc.thrift", "client_thrift.cc", "server_thrift.cc" };
|
||||
|
||||
std::vector<std::string> RPC_THRIFT_SKIP_FILES =
|
||||
{ "config_simple.h", "config_simple.cc",
|
||||
"rpc.proto", "client_protobuf.cc", "server_protobuf.cc" };
|
||||
|
||||
void usage_kafka(int argc, const char *argv[])
|
||||
{
|
||||
printf("Usage:\n"
|
||||
" %s kafka <PROJECT_NAME> [FLAGS]\n\n"
|
||||
"Available Flags:\n"
|
||||
" -o : project output path (default: CURRENT_PATH)\n"
|
||||
" -h : kafka broker url (example: kafka://10.160.23.23:9000,"
|
||||
"10.123.23.23,kafka://kafka.sogou)\n"
|
||||
" -g : group name\n"
|
||||
" -d : path of dependencies (default: COMPILE_PATH)\n"
|
||||
, argv[0]);
|
||||
}
|
||||
|
||||
const char *get_type_string(uint8_t type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case PROTOCOL_TYPE_HTTP:
|
||||
return "Http";
|
||||
case PROTOCOL_TYPE_REDIS:
|
||||
return "Redis";
|
||||
case PROTOCOL_TYPE_MYSQL:
|
||||
return "MySQL";
|
||||
case PROTOCOL_TYPE_KAFKA:
|
||||
return "Kafka";
|
||||
case PROTOCOL_TYPE_SRPC:
|
||||
return "SRPC";
|
||||
case PROTOCOL_TYPE_SRPC_HTTP:
|
||||
return "SRPCHttp";
|
||||
case PROTOCOL_TYPE_BRPC:
|
||||
return "BRPC";
|
||||
case PROTOCOL_TYPE_THRIFT:
|
||||
return "Thrift";
|
||||
case PROTOCOL_TYPE_THRIFT_HTTP:
|
||||
return "ThriftHttp";
|
||||
case PROTOCOL_TYPE_TRPC:
|
||||
return "TRPC";
|
||||
case PROTOCOL_TYPE_TRPC_HTTP:
|
||||
return "TRPCHttp";
|
||||
default:
|
||||
return "Unknown type";
|
||||
}
|
||||
}
|
||||
|
||||
static int check_file_idl_type(const char *filename)
|
||||
{
|
||||
size_t len = strlen(filename);
|
||||
|
||||
if (len > 6 && strcmp(filename + len - 6, ".proto") == 0)
|
||||
return IDL_TYPE_PROTOBUF;
|
||||
else if (len > 7 && strcmp(filename + len - 7, ".thrift") == 0)
|
||||
return IDL_TYPE_THRIFT;
|
||||
|
||||
return IDL_TYPE_MAX;
|
||||
}
|
||||
|
||||
srpc_config::srpc_config()
|
||||
{
|
||||
rpc_type = PROTOCOL_TYPE_SRPC;
|
||||
idl_type = IDL_TYPE_DEFAULT;
|
||||
data_type = DATA_TYPE_DEFAULT;
|
||||
compress_type = COMPRESS_TYPE_NONE;
|
||||
specified_depend_path = false;
|
||||
specified_idl_file = NULL;
|
||||
specified_idl_path = NULL;
|
||||
}
|
||||
|
||||
bool srpc_config::prepare_specified_idl_file()
|
||||
{
|
||||
if (this->specified_idl_file == NULL)
|
||||
{
|
||||
if (this->specified_idl_path != NULL)
|
||||
{
|
||||
printf("Error:\n idl_path is specified but NO idl_file.\n\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
this->idl_type = check_file_idl_type(this->specified_idl_file);
|
||||
if (this->idl_type == IDL_TYPE_MAX)
|
||||
{
|
||||
printf("Error:\n Invalid idl type. file : \" %s \"\n\n",
|
||||
this->specified_idl_file);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (access(this->specified_idl_file, F_OK) != 0)
|
||||
{
|
||||
printf("Error:\n idl_file \" %s \" does NOT exist.\n\n",
|
||||
this->specified_idl_file);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->specified_idl_path == NULL)
|
||||
this->specified_idl_path = this->output_path;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *srpc_config::rpc_type_string() const
|
||||
{
|
||||
return get_type_string(this->rpc_type);
|
||||
}
|
||||
|
||||
const char *srpc_config::rpc_compress_string() const
|
||||
{
|
||||
switch (this->compress_type)
|
||||
{
|
||||
case COMPRESS_TYPE_SNAPPY:
|
||||
return "RPCCompressSnappy";
|
||||
case COMPRESS_TYPE_GZIP:
|
||||
return "RPCCompressGzip";
|
||||
case COMPRESS_TYPE_ZLIB:
|
||||
return "RPCCompressZlib";
|
||||
case COMPRESS_TYPE_LZ4:
|
||||
return "RPCCompressLz4";
|
||||
default:
|
||||
return "Unknown type";
|
||||
}
|
||||
}
|
||||
|
||||
const char *srpc_config::rpc_data_string() const
|
||||
{
|
||||
switch (this->data_type)
|
||||
{
|
||||
case DATA_TYPE_PROTOBUF:
|
||||
return "RPCDataProtobuf";
|
||||
case DATA_TYPE_THRIFT:
|
||||
return "RPCDataThrift";
|
||||
case DATA_TYPE_JSON:
|
||||
return "RPCDataJson";
|
||||
default:
|
||||
return "Unknown type";
|
||||
}
|
||||
}
|
||||
|
||||
void srpc_config::set_rpc_type(const char *type)
|
||||
{
|
||||
if (strcasecmp(type, "SRPCHttp") == 0)
|
||||
this->rpc_type = PROTOCOL_TYPE_SRPC_HTTP;
|
||||
else if (strcasecmp(type, "SRPC") == 0)
|
||||
this->rpc_type = PROTOCOL_TYPE_SRPC;
|
||||
else if (strcasecmp(type, "BRPC") == 0)
|
||||
this->rpc_type = PROTOCOL_TYPE_BRPC;
|
||||
else if (strcasecmp(type, "ThriftHttp") == 0)
|
||||
this->rpc_type = PROTOCOL_TYPE_THRIFT_HTTP;
|
||||
else if (strcasecmp(type, "Thrift") == 0)
|
||||
this->rpc_type = PROTOCOL_TYPE_THRIFT;
|
||||
else if (strcasecmp(type, "TRPCHttp") == 0)
|
||||
this->rpc_type = PROTOCOL_TYPE_TRPC_HTTP;
|
||||
else if (strcasecmp(type, "TRPC") == 0)
|
||||
this->rpc_type = PROTOCOL_TYPE_TRPC;
|
||||
else
|
||||
this->rpc_type = PROTOCOL_TYPE_MAX;
|
||||
}
|
||||
|
||||
void srpc_config::set_idl_type(const char *type)
|
||||
{
|
||||
if (strcasecmp(type, "protobuf") == 0)
|
||||
this->idl_type = IDL_TYPE_PROTOBUF;
|
||||
else if (strcasecmp(type, "thrift") == 0)
|
||||
this->idl_type = IDL_TYPE_THRIFT;
|
||||
else
|
||||
this->idl_type = IDL_TYPE_MAX;
|
||||
}
|
||||
|
||||
void srpc_config::set_data_type(const char *type)
|
||||
{
|
||||
if (strcasecmp(type, "protobuf") == 0)
|
||||
this->data_type = DATA_TYPE_PROTOBUF;
|
||||
else if (strcasecmp(type, "thrift") == 0)
|
||||
this->data_type = DATA_TYPE_THRIFT;
|
||||
else if (strcasecmp(type, "json") == 0)
|
||||
this->data_type = DATA_TYPE_JSON;
|
||||
else
|
||||
this->data_type = DATA_TYPE_MAX;
|
||||
}
|
||||
|
||||
void srpc_config::set_compress_type(const char *type)
|
||||
{
|
||||
if (strcasecmp(type, "snappy") == 0)
|
||||
this->compress_type = COMPRESS_TYPE_SNAPPY;
|
||||
else if (strcasecmp(type, "gzip") == 0)
|
||||
this->compress_type = COMPRESS_TYPE_GZIP;
|
||||
else if (strcasecmp(type, "zlib") == 0)
|
||||
this->compress_type = COMPRESS_TYPE_ZLIB;
|
||||
else if (strcasecmp(type, "lz4") == 0)
|
||||
this->compress_type = COMPRESS_TYPE_LZ4;
|
||||
else
|
||||
this->compress_type = COMPRESS_TYPE_MAX;
|
||||
}
|
||||
|
||||
const char *srpc_config::proxy_server_type_string() const
|
||||
{
|
||||
return get_type_string(this->proxy_server_type);
|
||||
}
|
||||
|
||||
const char *srpc_config::proxy_client_type_string() const
|
||||
{
|
||||
return get_type_string(this->proxy_client_type);
|
||||
}
|
||||
|
||||
ControlGenerator::ControlGenerator(const struct srpc_config *config) :
|
||||
Generator(config->idl_type == IDL_TYPE_THRIFT ? true : false),
|
||||
ctl_printer(config->idl_type == IDL_TYPE_THRIFT ? true : false),
|
||||
config(config)
|
||||
{ }
|
||||
|
||||
bool ControlGenerator::generate_client_cpp_file(const idl_info& info,
|
||||
const std::string& idl_file)
|
||||
{
|
||||
this->client_cpp_file = this->config->output_path;
|
||||
this->client_cpp_file += "client_main.cc";
|
||||
|
||||
if (this->ctl_printer.open(this->client_cpp_file) == false)
|
||||
return false;
|
||||
|
||||
this->ctl_printer.print_clt_include();
|
||||
this->ctl_printer.print_client_file_include(idl_file);
|
||||
|
||||
for (const auto& desc : info.desc_list)
|
||||
{
|
||||
if (desc.block_type != "service")
|
||||
continue;
|
||||
|
||||
for (const auto& rpc : desc.rpcs)
|
||||
{
|
||||
this->ctl_printer.print_client_done_method_ctl(info.package_name,
|
||||
desc.block_name,
|
||||
rpc.method_name,
|
||||
rpc.response_name);
|
||||
}
|
||||
}
|
||||
|
||||
this->ctl_printer.print_client_main_begin();
|
||||
this->ctl_printer.print_client_load_config();
|
||||
this->ctl_printer.print_client_params();
|
||||
|
||||
int id = 0;
|
||||
|
||||
for (const auto& desc : info.desc_list)
|
||||
{
|
||||
if (desc.block_type != "service")
|
||||
continue;
|
||||
|
||||
std::string suffix;
|
||||
if (id != 0)
|
||||
suffix = std::to_string(id);
|
||||
id++;
|
||||
|
||||
this->ctl_printer.print_client_main_service_ctl(this->config->rpc_type_string(),
|
||||
info.package_name,
|
||||
desc.block_name,
|
||||
suffix);
|
||||
|
||||
auto rpc_it = desc.rpcs.cbegin();
|
||||
if (rpc_it != desc.rpcs.cend())
|
||||
{
|
||||
this->ctl_printer.print_client_main_method_call(info.package_name,
|
||||
desc.block_name,
|
||||
rpc_it->method_name,
|
||||
rpc_it->request_name,
|
||||
suffix);
|
||||
rpc_it++;
|
||||
}
|
||||
}
|
||||
|
||||
this->ctl_printer.print_client_main_end();
|
||||
this->ctl_printer.close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ControlGenerator::generate_server_cpp_file(const idl_info& info,
|
||||
const std::string& idl_file)
|
||||
{
|
||||
this->server_cpp_file = this->config->output_path;
|
||||
this->server_cpp_file += "server_main.cc";
|
||||
|
||||
if (this->ctl_printer.open(this->server_cpp_file) == false)
|
||||
return false;
|
||||
|
||||
this->ctl_printer.print_clt_include();
|
||||
this->ctl_printer.print_server_file_include(idl_file);
|
||||
|
||||
for (const auto& desc : info.desc_list)
|
||||
{
|
||||
if (desc.block_type != "service")
|
||||
continue;
|
||||
|
||||
this->ctl_printer.print_server_class_impl(info.package_name,
|
||||
desc.block_name);
|
||||
|
||||
for (const auto& rpc : desc.rpcs)
|
||||
{
|
||||
this->ctl_printer.print_server_impl_method(info.package_name,
|
||||
desc.block_name,
|
||||
rpc.method_name,
|
||||
rpc.request_name,
|
||||
rpc.response_name);
|
||||
}
|
||||
this->ctl_printer.print_server_class_impl_end();
|
||||
}
|
||||
|
||||
this->ctl_printer.print_server_main_begin();
|
||||
this->ctl_printer.print_server_load_config();
|
||||
this->ctl_printer.print_server_construct(this->config->rpc_type_string());
|
||||
|
||||
for (const auto& desc : info.desc_list)
|
||||
{
|
||||
if (desc.block_type != "service")
|
||||
continue;
|
||||
|
||||
this->ctl_printer.print_server_main_method(desc.block_name);
|
||||
}
|
||||
|
||||
this->ctl_printer.print_server_main_end_ctl(this->config->project_name,
|
||||
this->config->rpc_type_string());
|
||||
this->ctl_printer.print_server_main_return();
|
||||
this->ctl_printer.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string ctl_include_format = R"(#include <stdio.h>
|
||||
#include "config/config.h"
|
||||
)";
|
||||
|
||||
static std::string ctl_load_config_format = R"(
|
||||
// load config
|
||||
srpc::RPCConfig config;
|
||||
if (config.load("./%s") == false)
|
||||
{
|
||||
perror("Load config failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
)";
|
||||
|
||||
static std::string ctl_client_load_params_format = R"(
|
||||
// start client
|
||||
RPCClientParams params = RPC_CLIENT_PARAMS_DEFAULT;
|
||||
params.host = config.client_host();
|
||||
params.port = config.client_port();
|
||||
)";
|
||||
|
||||
static std::string ctl_client_main_params_format = R"(
|
||||
%s::%sClient client%s(¶ms);
|
||||
config.load_filter(client%s);
|
||||
)";
|
||||
|
||||
static std::string ctl_client_done_protobuf_format = R"(
|
||||
// printf("%%s\n", response->DebugString().c_str());
|
||||
)";
|
||||
|
||||
static std::string ctl_client_done_format = R"(
|
||||
static void %s_done(%s *response, srpc::RPCContext *context)
|
||||
{
|
||||
if (context->success())
|
||||
{
|
||||
// TODO: fill your logic to set response%s
|
||||
}
|
||||
else
|
||||
printf("%s %s status[%%d] error[%%d] errmsg:%%s\n",
|
||||
context->get_status_code(), context->get_error(),
|
||||
context->get_errmsg());
|
||||
}
|
||||
)";
|
||||
|
||||
static std::string ctl_server_main_end_format = R"(
|
||||
config.load_filter(server);
|
||||
|
||||
if (server.start(config.server_port()) == 0)
|
||||
{
|
||||
printf("%s %s server start, port %%u\n", config.server_port());
|
||||
wait_group.wait();
|
||||
server.stop();
|
||||
}
|
||||
)";
|
||||
|
||||
void ControlGenerator::ControlPrinter::print_clt_include()
|
||||
{
|
||||
fprintf(this->out_file, "%s", ctl_include_format.c_str());
|
||||
}
|
||||
|
||||
void ControlGenerator::ControlPrinter::print_client_load_config()
|
||||
{
|
||||
fprintf(this->out_file, ctl_load_config_format.c_str(), "./client.conf");
|
||||
}
|
||||
|
||||
void ControlGenerator::ControlPrinter::print_server_load_config()
|
||||
{
|
||||
fprintf(this->out_file, ctl_load_config_format.c_str(), "./server.conf");
|
||||
}
|
||||
|
||||
void ControlGenerator::ControlPrinter::print_client_params()
|
||||
{
|
||||
fprintf(this->out_file, "%s", ctl_client_load_params_format.c_str());
|
||||
fprintf(this->out_file, "\tparams.is_ssl = config.client_is_ssl();\n");
|
||||
fprintf(this->out_file, "\tparams.url = config.client_url();\n");
|
||||
fprintf(this->out_file, "\tparams.caller = config.client_caller();\n");
|
||||
|
||||
// TODO: set client task params
|
||||
}
|
||||
|
||||
void ControlGenerator::ControlPrinter::
|
||||
print_client_done_method_ctl(const std::vector<std::string>& package,
|
||||
const std::string& service,
|
||||
const std::string& method,
|
||||
const std::string& response)
|
||||
{
|
||||
std::string method_lower = method;
|
||||
std::transform(method_lower.begin(), method_lower.end(),
|
||||
method_lower.begin(), ::tolower);
|
||||
|
||||
std::string resp;
|
||||
const char *set_resp_code = "";
|
||||
|
||||
if (this->is_thrift)
|
||||
resp = make_thrift_package_prefix(package, service, response);
|
||||
else
|
||||
resp = make_package_prefix(package, response);
|
||||
|
||||
if (!this->is_thrift)
|
||||
set_resp_code = ctl_client_done_protobuf_format.c_str();
|
||||
|
||||
fprintf(this->out_file, ctl_client_done_format.c_str(),
|
||||
method_lower.c_str(), resp.c_str(), set_resp_code,
|
||||
service.c_str(), method.c_str(), service.c_str(), method.c_str());
|
||||
}
|
||||
|
||||
void ControlGenerator::ControlPrinter::
|
||||
print_client_main_service_ctl(const std::string& type,
|
||||
const std::vector<std::string>& package,
|
||||
const std::string& service,
|
||||
const std::string& suffix)
|
||||
{
|
||||
std::string base_service = make_package_prefix(package, service);
|
||||
fprintf(this->out_file, ctl_client_main_params_format.c_str(),
|
||||
base_service.c_str(), type.c_str(),
|
||||
suffix.c_str(), suffix.c_str());
|
||||
}
|
||||
|
||||
void ControlGenerator::ControlPrinter::
|
||||
print_server_construct(const char *rpc_type)
|
||||
{
|
||||
fprintf(this->out_file, " %sServer server;\n", rpc_type);
|
||||
}
|
||||
|
||||
void ControlGenerator::ControlPrinter::
|
||||
print_server_main_end_ctl(const char *project_name, const char *rpc_type)
|
||||
{
|
||||
fprintf(this->out_file, ctl_server_main_end_format.c_str(),
|
||||
project_name, rpc_type);
|
||||
}
|
||||
|
170
tools/srpc_config.h
Normal file
170
tools/srpc_config.h
Normal file
@ -0,0 +1,170 @@
|
||||
/*
|
||||
Copyright (c) 2022 Sogou, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __SRPC_CONFIG__
|
||||
#define __SRPC_CONFIG__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <generator/generator.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/param.h>
|
||||
#else
|
||||
#define MAXPATHLEN 4096
|
||||
#include <direct.h>
|
||||
#endif
|
||||
|
||||
struct srpc_config
|
||||
{
|
||||
uint8_t type;
|
||||
const char *project_name;
|
||||
const char *service_name;
|
||||
char template_path[MAXPATHLEN];
|
||||
char depend_path[MAXPATHLEN];
|
||||
char output_path[MAXPATHLEN];
|
||||
// rpc
|
||||
uint8_t rpc_type;
|
||||
uint8_t idl_type;
|
||||
uint8_t data_type;
|
||||
uint8_t compress_type;
|
||||
bool specified_depend_path;
|
||||
const char *specified_idl_file;
|
||||
const char *specified_idl_path;
|
||||
// proxy
|
||||
uint8_t proxy_server_type;
|
||||
uint8_t proxy_client_type;
|
||||
|
||||
srpc_config();
|
||||
|
||||
bool prepare_args();
|
||||
bool prepare_project_path();
|
||||
bool prepare_dependencies() const;
|
||||
bool prepare_specified_idl_file();
|
||||
const char *rpc_type_string() const;
|
||||
const char *rpc_compress_string() const;
|
||||
const char *rpc_data_string() const;
|
||||
void set_rpc_type(const char *optarg);
|
||||
void set_idl_type(const char *optarg);
|
||||
void set_data_type(const char *optarg);
|
||||
void set_compress_type(const char *optarg);
|
||||
const char *proxy_server_type_string() const;
|
||||
const char *proxy_client_type_string() const;
|
||||
};
|
||||
|
||||
class ControlGenerator : public Generator
|
||||
{
|
||||
public:
|
||||
bool generate_client_cpp_file(const idl_info& cur_info,
|
||||
const std::string& idl_file_name);
|
||||
bool generate_server_cpp_file(const idl_info& cur_info,
|
||||
const std::string& idl_file_name);
|
||||
|
||||
ControlGenerator(const struct srpc_config *config);
|
||||
|
||||
private:
|
||||
class ControlPrinter : public Printer
|
||||
{
|
||||
public:
|
||||
ControlPrinter(bool is_thrift) : Printer(is_thrift) { }
|
||||
|
||||
void print_clt_include();
|
||||
void print_server_load_config();
|
||||
void print_client_load_config();
|
||||
|
||||
void print_client_params();
|
||||
void print_client_done_method_ctl(const std::vector<std::string>& pkg,
|
||||
const std::string& service,
|
||||
const std::string& method,
|
||||
const std::string& response);
|
||||
void print_client_main_service_ctl(const std::string& type,
|
||||
const std::vector<std::string>& pkg,
|
||||
const std::string& service,
|
||||
const std::string& suffix);
|
||||
|
||||
void print_server_construct(const char *rpc_type);
|
||||
void print_server_main_end_ctl(const char *project_name,
|
||||
const char *rpc_type);
|
||||
};
|
||||
|
||||
ControlPrinter ctl_printer;
|
||||
const struct srpc_config *config;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
COMMAND_HTTP,
|
||||
COMMAND_REDIS,
|
||||
COMMAND_MYSQL,
|
||||
COMMAND_KAFKA,
|
||||
COMMAND_RPC,
|
||||
COMMAND_PROXY,
|
||||
COMMAND_FILE,
|
||||
COMMAND_EXTRA
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROTOCOL_TYPE_HTTP = COMMAND_HTTP,
|
||||
PROTOCOL_TYPE_REDIS = COMMAND_REDIS,
|
||||
PROTOCOL_TYPE_MYSQL = COMMAND_MYSQL,
|
||||
PROTOCOL_TYPE_KAFKA = COMMAND_KAFKA,
|
||||
PROTOCOL_TYPE_SRPC = 22,
|
||||
PROTOCOL_TYPE_SRPC_HTTP,
|
||||
PROTOCOL_TYPE_BRPC,
|
||||
PROTOCOL_TYPE_THRIFT,
|
||||
PROTOCOL_TYPE_THRIFT_HTTP,
|
||||
PROTOCOL_TYPE_TRPC,
|
||||
PROTOCOL_TYPE_TRPC_HTTP,
|
||||
PROTOCOL_TYPE_MAX
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
IDL_TYPE_DEFAULT,
|
||||
IDL_TYPE_PROTOBUF,
|
||||
IDL_TYPE_THRIFT,
|
||||
IDL_TYPE_MAX
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
DATA_TYPE_DEFAULT,
|
||||
DATA_TYPE_PROTOBUF,
|
||||
DATA_TYPE_THRIFT,
|
||||
DATA_TYPE_JSON,
|
||||
DATA_TYPE_MAX
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
COMPRESS_TYPE_NONE,
|
||||
COMPRESS_TYPE_SNAPPY,
|
||||
COMPRESS_TYPE_GZIP,
|
||||
COMPRESS_TYPE_ZLIB,
|
||||
COMPRESS_TYPE_LZ4,
|
||||
COMPRESS_TYPE_MAX
|
||||
};
|
||||
|
||||
void usage(int argc, const char *argv[]);
|
||||
void usage_http(int argc, const char *argv[]);
|
||||
void usage_db(int argc, const char *argv[], const struct srpc_config *config);
|
||||
void usage_kafka(int argc, const char *argv[]);
|
||||
void usage_rpc(int argc, const char *argv[], const struct srpc_config *config);
|
||||
const char *get_type_string(uint8_t type);
|
||||
|
||||
#endif
|
||||
|
557
tools/srpc_controller.cc
Normal file
557
tools/srpc_controller.cc
Normal file
@ -0,0 +1,557 @@
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "srpc_controller.h"
|
||||
|
||||
static constexpr const char *DEPENDENCIES_ERROR = R"(Warning:
|
||||
Default dependencies path : %s does not have
|
||||
Workflow or other third_party dependencies.
|
||||
This may cause link error in project : %s
|
||||
|
||||
Please check or specify srpc path with '-d'
|
||||
|
||||
Or use the following command to pull srpc:
|
||||
"git clone --recursive https://github.com/sogou/srpc.git"
|
||||
"cd srpc && make"
|
||||
)";
|
||||
|
||||
static constexpr const char *CMAKE_PROTOC_CODES = R"(
|
||||
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}))";
|
||||
|
||||
static int mkdir_p(const char *name, mode_t mode)
|
||||
{
|
||||
int ret = mkdir(name, mode);
|
||||
if (ret == 0 || errno != ENOENT)
|
||||
return ret;
|
||||
|
||||
size_t len = strlen(name);
|
||||
if (len > MAXPATHLEN)
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
char path[MAXPATHLEN + 1] = {};
|
||||
memcpy(path, name, len);
|
||||
|
||||
if (name[len - 1] != '/')
|
||||
{
|
||||
path[len] = '/';
|
||||
len++;
|
||||
}
|
||||
|
||||
bool has_valid = false;
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (path[i] != '/' && path[i] != '.') // simple check of valid path
|
||||
{
|
||||
has_valid = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (path[i] == '/' && has_valid == true)
|
||||
{
|
||||
path[i] = '\0';
|
||||
ret = mkdir(path, mode);
|
||||
if (ret != 0 && errno != EEXIST)
|
||||
return ret;
|
||||
|
||||
path[i] = '/';
|
||||
has_valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char *get_proxy_rpc_string(const char *type)
|
||||
{
|
||||
if (strcasecmp(type, "SRPCHttp") == 0)
|
||||
return "SRPCHttp";
|
||||
if (strcasecmp(type, "SRPC") == 0)
|
||||
return "SRPC";
|
||||
if (strcasecmp(type, "BRPC") == 0)
|
||||
return "BRPC";
|
||||
if (strcasecmp(type, "ThriftHttp") == 0)
|
||||
return "ThriftHttp";
|
||||
if (strcasecmp(type, "Thrift") == 0)
|
||||
return "Thrift";
|
||||
if (strcasecmp(type, "TRPCHttp") == 0)
|
||||
return "TRPCHttp";
|
||||
if (strcasecmp(type, "TRPC") == 0)
|
||||
return "TRPC";
|
||||
|
||||
return "Unknown type";
|
||||
}
|
||||
|
||||
// path=/root/a/
|
||||
// file=b/c/d.txt
|
||||
// make the middle path and return the full file name
|
||||
static std::string make_file_path(const char *path, const std::string& file)
|
||||
{
|
||||
DIR *dir;
|
||||
auto pos = file.find_last_of('/');
|
||||
std::string file_name;
|
||||
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
file_name = file.substr(pos + 1);
|
||||
std::string dir_name = std::string(path) + std::string("/") +
|
||||
file.substr(0, pos + 1);
|
||||
|
||||
dir = opendir(dir_name.c_str());
|
||||
if (dir == NULL)
|
||||
{
|
||||
if (mkdir_p(dir_name.c_str(), 0755) != 0)
|
||||
return "";
|
||||
}
|
||||
else
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
file_name = path;
|
||||
file_name += "/";
|
||||
file_name += file;
|
||||
|
||||
return file_name;
|
||||
}
|
||||
|
||||
static std::string rpc_client_file_codes(const struct srpc_config *config)
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
if (config->compress_type != COMPRESS_TYPE_NONE)
|
||||
{
|
||||
ret += "\tparams.task_params.compress_type = ";
|
||||
ret += config->rpc_compress_string();
|
||||
ret += ";\n";
|
||||
}
|
||||
|
||||
if (config->data_type != DATA_TYPE_DEFAULT)
|
||||
{
|
||||
ret += "\tparams.task_params.data_type = ";
|
||||
ret += config->rpc_data_string();
|
||||
ret += ";\n";
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::string rpc_server_file_codes(const struct srpc_config *config)
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
if (config->compress_type != COMPRESS_TYPE_NONE)
|
||||
{
|
||||
ret += " ctx->set_compress_type(";
|
||||
ret += config->rpc_compress_string();
|
||||
ret += ");\n";
|
||||
}
|
||||
|
||||
if (config->data_type != DATA_TYPE_DEFAULT)
|
||||
{
|
||||
ret += " ctx->set_data_type(";
|
||||
ret += config->rpc_data_string();
|
||||
ret += ");\n";
|
||||
}
|
||||
|
||||
ret += " ";
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool rpc_idl_transform(const std::string& format, FILE *out,
|
||||
const struct srpc_config *config)
|
||||
{
|
||||
size_t len = fprintf(out, format.c_str(), config->service_name);
|
||||
|
||||
return len > 0;
|
||||
}
|
||||
|
||||
bool rpc_client_transform(const std::string& format, FILE *out,
|
||||
const struct srpc_config *config)
|
||||
{
|
||||
std::string prepare_params = rpc_client_file_codes(config);
|
||||
const char *rpc_type;
|
||||
if (config->type == COMMAND_PROXY)
|
||||
rpc_type = get_proxy_rpc_string(config->proxy_client_type_string());
|
||||
else
|
||||
rpc_type = config->rpc_type_string();
|
||||
|
||||
size_t len = fprintf(out, format.c_str(),
|
||||
config->service_name, prepare_params.c_str(),
|
||||
config->service_name, rpc_type);
|
||||
return len > 0;
|
||||
}
|
||||
|
||||
bool rpc_server_transform(const std::string& format, FILE *out,
|
||||
const struct srpc_config *config)
|
||||
{
|
||||
std::string prepare_ctx = rpc_server_file_codes(config);
|
||||
const char *rpc_type;
|
||||
if (config->type == COMMAND_PROXY)
|
||||
rpc_type = get_proxy_rpc_string(config->proxy_server_type_string());
|
||||
else
|
||||
rpc_type = config->rpc_type_string();
|
||||
|
||||
size_t len = fprintf(out, format.c_str(),
|
||||
config->service_name, config->service_name,
|
||||
prepare_ctx.c_str(), rpc_type,
|
||||
config->project_name, rpc_type);
|
||||
|
||||
return len > 0;
|
||||
}
|
||||
|
||||
bool CommandController::parse_args(int argc, const char **argv)
|
||||
{
|
||||
if (argc < 3)
|
||||
{
|
||||
printf("Missing: PROJECT_NAME\n\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
getcwd(this->config.output_path, MAXPATHLEN);
|
||||
this->config.project_name = argv[2];
|
||||
this->config.service_name = argv[2];
|
||||
if (get_path(__FILE__, this->config.depend_path, 2) == false)
|
||||
return false;
|
||||
|
||||
if (get_path(__FILE__, this->config.template_path, 1) == false)
|
||||
return false;
|
||||
|
||||
snprintf(this->config.template_path + strlen(this->config.template_path),
|
||||
MAXPATHLEN - strlen(this->config.template_path), "templates/");
|
||||
|
||||
if (this->get_opt(argc, argv) == false)
|
||||
return false;
|
||||
|
||||
if (this->check_args() == false)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommandController::check_args()
|
||||
{
|
||||
if (*(this->config.project_name) == '-')
|
||||
return false;
|
||||
|
||||
size_t path_len = strlen(this->config.output_path);
|
||||
|
||||
if (strlen(this->config.project_name) >= MAXPATHLEN - path_len - 2)
|
||||
{
|
||||
printf("Error:\n project name \" %s \" or path \" %s \" "\
|
||||
" is too long. total limit %d.\n\n",
|
||||
this->config.project_name, this->config.output_path, MAXPATHLEN);
|
||||
return false;
|
||||
}
|
||||
|
||||
snprintf(this->config.output_path + path_len, MAXPATHLEN - path_len, "/%s/",
|
||||
this->config.project_name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommandController::dependencies_and_dir()
|
||||
{
|
||||
struct srpc_config *config = &this->config;
|
||||
DIR *dir;
|
||||
|
||||
dir = opendir(config->output_path);
|
||||
if (dir != NULL)
|
||||
{
|
||||
printf("Error:\n project path \" %s \" EXISTS.\n\n",
|
||||
config->output_path);
|
||||
closedir(dir);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
dir = opendir(config->template_path);
|
||||
if (dir == NULL)
|
||||
{
|
||||
printf("Error:\n template path \" %s \" does NOT exist.\n",
|
||||
config->template_path);
|
||||
|
||||
return false;
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
if (config->specified_depend_path == false)
|
||||
{
|
||||
std::string workflow_file = config->depend_path;
|
||||
workflow_file += "workflow/workflow-config.cmake.in";
|
||||
if (access(workflow_file.c_str(), 0) != 0)
|
||||
{
|
||||
std::string cmd = "cd ";
|
||||
cmd += config->depend_path;
|
||||
cmd += "&& git submodule init && git submodule update";
|
||||
system(cmd.c_str());
|
||||
|
||||
if (access(workflow_file.c_str(), 0) != 0)
|
||||
{
|
||||
printf(DEPENDENCIES_ERROR, config->depend_path,
|
||||
config->project_name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string srpc_lib = config->depend_path;
|
||||
srpc_lib += "_lib";
|
||||
|
||||
if (access(srpc_lib.c_str(), 0) != 0)
|
||||
{
|
||||
std::string cmd = "cd ";
|
||||
cmd += config->depend_path;
|
||||
cmd += " && make -j4";
|
||||
system(cmd.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (mkdir_p(config->output_path, 0755) != 0)
|
||||
{
|
||||
perror("Error:\n failed to make project ");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string config_path = config->output_path;
|
||||
config_path += "/config";
|
||||
if (mkdir(config_path.c_str(), 0755) != 0)
|
||||
{
|
||||
perror("Error:\n failed to make project config path ");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// get the depth-th upper path of file
|
||||
bool CommandController::get_path(const char *file, char *path, int depth)
|
||||
{
|
||||
size_t len = strlen(file);
|
||||
size_t i;
|
||||
|
||||
memset(path, 0, MAXPATHLEN);
|
||||
|
||||
if (len == 0 || depth <= 0)
|
||||
return false;
|
||||
|
||||
int state = 0;
|
||||
|
||||
for (i = len - 1; i >= 0; i--)
|
||||
{
|
||||
if (file[i] == '/')
|
||||
{
|
||||
state++;
|
||||
if (state >= depth)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (state != depth)
|
||||
return false;
|
||||
|
||||
memcpy(path, file, i + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommandController::copy_single_file(const std::string& in_file,
|
||||
const std::string& out_file,
|
||||
transform_function_t transform)
|
||||
{
|
||||
FILE *read_fp;
|
||||
FILE *write_fp;
|
||||
size_t size;
|
||||
char *buf;
|
||||
bool ret = false;
|
||||
|
||||
read_fp = fopen(in_file.c_str(), "r");
|
||||
if (read_fp)
|
||||
{
|
||||
write_fp = fopen(out_file.c_str(), "w");
|
||||
if (write_fp)
|
||||
{
|
||||
fseek(read_fp, 0, SEEK_END);
|
||||
size = ftell(read_fp);
|
||||
|
||||
buf = (char *)malloc(size);
|
||||
if (buf)
|
||||
{
|
||||
fseek(read_fp, 0, SEEK_SET);
|
||||
|
||||
if (fread(buf, size, 1, read_fp) == 1)
|
||||
{
|
||||
if (transform != nullptr)
|
||||
{
|
||||
std::string format = std::string(buf, size);
|
||||
ret = transform(format, write_fp, &this->config);
|
||||
}
|
||||
else
|
||||
ret = fwrite(buf, size, 1, write_fp) >= 0 ? true : false;
|
||||
}
|
||||
else
|
||||
printf("Error:\n read \" %s \" failed\n\n", in_file.c_str());
|
||||
|
||||
free(buf);
|
||||
}
|
||||
else
|
||||
printf("Error:\n system error.\n\n");
|
||||
|
||||
fclose(write_fp);
|
||||
}
|
||||
else
|
||||
printf("Error:\n write \" %s \" failed\n\n", out_file.c_str());
|
||||
}
|
||||
else
|
||||
printf("Error:\n open \" %s \" failed.\n\n", in_file.c_str());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool CommandController::copy_files()
|
||||
{
|
||||
std::string read_file;
|
||||
std::string write_file;
|
||||
bool ret = true;
|
||||
|
||||
for (auto it = this->default_files.begin();
|
||||
it != this->default_files.end() && ret == true; it++)
|
||||
{
|
||||
read_file = this->config.template_path;
|
||||
read_file += it->in_file;
|
||||
|
||||
write_file = make_file_path(this->config.output_path, it->out_file);
|
||||
|
||||
if (write_file.empty())
|
||||
ret = false;
|
||||
else
|
||||
ret = this->copy_single_file(read_file, write_file, it->transform);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CommandController::print_success_info() const
|
||||
{
|
||||
printf("Success:\n make project path \" %s \" done.\n\n",
|
||||
this->config.output_path);
|
||||
printf("Commands:\n cd %s\n make -j\n\n",
|
||||
this->config.output_path);
|
||||
printf("Execute:\n ./server\n ./client\n\n");
|
||||
}
|
||||
|
||||
bool common_cmake_transform(const std::string& format, FILE *out,
|
||||
const struct srpc_config *config)
|
||||
{
|
||||
std::string path = config->depend_path;
|
||||
path += "workflow";
|
||||
|
||||
std::string codes_str;
|
||||
std::string executors_str;
|
||||
|
||||
if (config->type == COMMAND_FILE)
|
||||
codes_str = " file_service.cc";
|
||||
else
|
||||
executors_str = " client";
|
||||
|
||||
if (config->type == COMMAND_PROXY)
|
||||
executors_str += " proxy";
|
||||
|
||||
size_t len = fprintf(out, format.c_str(), config->project_name,
|
||||
path.c_str(), codes_str.c_str(), executors_str.c_str());
|
||||
|
||||
return len > 0;
|
||||
}
|
||||
|
||||
void CommandController::fill_rpc_default_files()
|
||||
{
|
||||
struct file_info info;
|
||||
std::string idl_file_name, client_file_name, server_file_name;
|
||||
std::string out_file_name = this->config.project_name;
|
||||
|
||||
if (this->config.idl_type == IDL_TYPE_PROTOBUF)
|
||||
{
|
||||
out_file_name += ".proto";
|
||||
idl_file_name = "rpc/rpc.proto";
|
||||
client_file_name = "rpc/client_protobuf.cc";
|
||||
server_file_name = "rpc/server_protobuf.cc";
|
||||
}
|
||||
else
|
||||
{
|
||||
out_file_name += ".thrift";
|
||||
idl_file_name = "rpc/rpc.thrift";
|
||||
client_file_name = "rpc/client_thrift.cc";
|
||||
server_file_name = "rpc/server_thrift.cc";
|
||||
}
|
||||
|
||||
info = { idl_file_name , out_file_name, rpc_idl_transform };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { client_file_name, "client_main.cc", rpc_client_transform };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { server_file_name, "server_main.cc", rpc_server_transform };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "rpc/server.conf", "server.conf", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
if (this->config.type == COMMAND_RPC)
|
||||
info = { "rpc/client.conf", "client.conf", nullptr };
|
||||
else
|
||||
info = { "proxy/client_rpc.conf", "client.conf", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
}
|
||||
|
||||
bool rpc_cmake_transform(const std::string& format, FILE *out,
|
||||
const struct srpc_config *config)
|
||||
{
|
||||
std::string idl_file_name;
|
||||
|
||||
std::string srpc_path = config->depend_path;
|
||||
std::string workflow_path = config->depend_path;
|
||||
workflow_path += "workflow";
|
||||
|
||||
if (config->specified_idl_file != NULL)
|
||||
{
|
||||
idl_file_name = config->specified_idl_file;
|
||||
size_t pos = idl_file_name.find_last_of('/');
|
||||
if (pos != std::string::npos)
|
||||
idl_file_name = idl_file_name.substr(pos + 1);
|
||||
}
|
||||
else if (config->idl_type == IDL_TYPE_PROTOBUF)
|
||||
{
|
||||
idl_file_name = config->project_name;
|
||||
idl_file_name += ".proto";
|
||||
}
|
||||
else
|
||||
{
|
||||
idl_file_name = config->project_name;
|
||||
idl_file_name += ".thrift";
|
||||
}
|
||||
|
||||
std::string is_proxy_str = config->type == COMMAND_PROXY ? " proxy" : "";
|
||||
|
||||
size_t len = fprintf(out, format.c_str(), config->project_name,
|
||||
workflow_path.c_str(), srpc_path.c_str(),
|
||||
idl_file_name.c_str(),
|
||||
config->idl_type == IDL_TYPE_PROTOBUF ?
|
||||
CMAKE_PROTOC_CODES : "",
|
||||
is_proxy_str.c_str());
|
||||
|
||||
return len > 0;
|
||||
}
|
||||
|
161
tools/srpc_controller.h
Normal file
161
tools/srpc_controller.h
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
Copyright (c) 2022 Sogou, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __SRPC_CONTROLLER__
|
||||
#define __SRPC_CONTROLLER__
|
||||
|
||||
#include "srpc_config.h"
|
||||
|
||||
class CommandController
|
||||
{
|
||||
public:
|
||||
bool parse_args(int argc, const char **argv);
|
||||
bool dependencies_and_dir();
|
||||
virtual bool copy_files();
|
||||
virtual void print_success_info() const;
|
||||
virtual void print_usage(const char *name) const = 0;
|
||||
|
||||
protected:
|
||||
virtual bool check_args();
|
||||
|
||||
private:
|
||||
virtual bool get_opt(int argc, const char **argv) = 0;
|
||||
|
||||
public:
|
||||
CommandController() { }
|
||||
virtual ~CommandController() { }
|
||||
|
||||
using transform_function_t = bool (*)(const std::string&, FILE *,
|
||||
const struct srpc_config *);
|
||||
struct file_info
|
||||
{
|
||||
std::string in_file;
|
||||
std::string out_file;
|
||||
transform_function_t transform;
|
||||
};
|
||||
|
||||
protected:
|
||||
std::vector<struct file_info> default_files;
|
||||
struct srpc_config config;
|
||||
|
||||
protected:
|
||||
bool get_path(const char *file, char *path, int depth);
|
||||
bool copy_single_file(const std::string& in_file,
|
||||
const std::string& out_file,
|
||||
transform_function_t transform);
|
||||
void fill_rpc_default_files();
|
||||
};
|
||||
|
||||
class HttpController : public CommandController
|
||||
{
|
||||
public:
|
||||
void print_usage(const char *name) const override;
|
||||
|
||||
private:
|
||||
bool get_opt(int argc, const char **argv) override;
|
||||
|
||||
public:
|
||||
HttpController();
|
||||
~HttpController() { }
|
||||
};
|
||||
|
||||
class RedisController : public CommandController
|
||||
{
|
||||
public:
|
||||
void print_usage(const char *name) const override;
|
||||
|
||||
private:
|
||||
bool get_opt(int argc, const char **argv) override;
|
||||
|
||||
public:
|
||||
RedisController();
|
||||
~RedisController() { }
|
||||
};
|
||||
|
||||
class RPCController : public CommandController
|
||||
{
|
||||
public:
|
||||
void print_usage(const char *name) const override;
|
||||
bool copy_files() override;
|
||||
|
||||
protected:
|
||||
bool check_args() override;
|
||||
|
||||
private:
|
||||
bool get_opt(int argc, const char **argv) override;
|
||||
|
||||
public:
|
||||
RPCController();
|
||||
~RPCController() { }
|
||||
};
|
||||
|
||||
class ProxyController : public CommandController
|
||||
{
|
||||
public:
|
||||
void print_usage(const char *name) const override;
|
||||
void print_success_info() const override;
|
||||
bool copy_files() override;
|
||||
|
||||
protected:
|
||||
bool check_args() override;
|
||||
|
||||
private:
|
||||
bool get_opt(int argc, const char **argv) override;
|
||||
|
||||
public:
|
||||
ProxyController();
|
||||
~ProxyController() { }
|
||||
};
|
||||
|
||||
class FileServiceController : public CommandController
|
||||
{
|
||||
public:
|
||||
void print_usage(const char *name) const override;
|
||||
void print_success_info() const override;
|
||||
|
||||
private:
|
||||
bool get_opt(int argc, const char **argv) override;
|
||||
|
||||
public:
|
||||
FileServiceController();
|
||||
~FileServiceController() { }
|
||||
};
|
||||
|
||||
////////// common transform functions //////////
|
||||
|
||||
bool common_cmake_transform(const std::string& format, FILE *out,
|
||||
const struct srpc_config *config);
|
||||
|
||||
bool basic_client_transform(const std::string& format, FILE *out,
|
||||
const struct srpc_config *config);
|
||||
bool basic_server_transform(const std::string& format, FILE *out,
|
||||
const struct srpc_config *config);
|
||||
bool basic_client_config_transform(const std::string& format, FILE *out,
|
||||
const struct srpc_config *config);
|
||||
bool basic_server_config_transform(const std::string& format, FILE *out,
|
||||
const struct srpc_config *config);
|
||||
|
||||
bool rpc_idl_transform(const std::string& format, FILE *out,
|
||||
const struct srpc_config *config);
|
||||
bool rpc_client_transform(const std::string& format, FILE *out,
|
||||
const struct srpc_config *config);
|
||||
bool rpc_server_transform(const std::string& format, FILE *out,
|
||||
const struct srpc_config *config);
|
||||
bool rpc_cmake_transform(const std::string& format, FILE *out,
|
||||
const struct srpc_config *config);
|
||||
|
||||
#endif
|
||||
|
84
tools/srpc_ctl.cc
Normal file
84
tools/srpc_ctl.cc
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
Copyright (c) 2022 Sogou, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "srpc_controller.h"
|
||||
|
||||
static void usage(const char *name)
|
||||
{
|
||||
printf("Description:\n"
|
||||
" A handy generator for Workflow and SRPC project.\n\n"
|
||||
"Usage:\n"
|
||||
" %s <COMMAND> <PROJECT_NAME> [FLAGS]\n\n"
|
||||
"Available Commands:\n"
|
||||
" \"http\" - create project with both client and server\n"
|
||||
" \"redis\" - create project with both client and server\n"
|
||||
" \"rpc\" - create project with both client and server\n"
|
||||
" \"proxy\" - create proxy for some client and server protocol\n"
|
||||
" \"file\" - create project with file service\n"
|
||||
, name);
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
usage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
CommandController *ctl;
|
||||
|
||||
if (strcasecmp(argv[1], "http") == 0)
|
||||
{
|
||||
ctl = new HttpController;
|
||||
}
|
||||
else if (strcasecmp(argv[1], "redis") == 0)
|
||||
{
|
||||
ctl = new RedisController;
|
||||
}
|
||||
else if (strcasecmp(argv[1], "rpc") == 0)
|
||||
{
|
||||
ctl = new RPCController;
|
||||
}
|
||||
else if (strcasecmp(argv[1], "proxy") == 0)
|
||||
{
|
||||
ctl = new ProxyController;
|
||||
}
|
||||
else if (strcasecmp(argv[1], "file") == 0)
|
||||
{
|
||||
ctl = new FileServiceController;
|
||||
}
|
||||
else
|
||||
{
|
||||
usage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ctl->parse_args(argc, argv) == true)
|
||||
{
|
||||
if (ctl->dependencies_and_dir() == true)
|
||||
{
|
||||
if (ctl->copy_files() == true)
|
||||
ctl->print_success_info();
|
||||
}
|
||||
}
|
||||
else
|
||||
ctl->print_usage(argv[0]);
|
||||
|
||||
delete ctl;
|
||||
return 0;
|
||||
}
|
413
tools/srpc_proxy_controller.cc
Normal file
413
tools/srpc_proxy_controller.cc
Normal file
@ -0,0 +1,413 @@
|
||||
/*
|
||||
Copyright (c) 2022 Sogou, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "srpc_controller.h"
|
||||
|
||||
enum
|
||||
{
|
||||
BASIC_TYPE = 1,
|
||||
PROTOBUF_TYPE = 2,
|
||||
THRIFT_TYPE = 3
|
||||
};
|
||||
|
||||
static std::string default_server_port(uint8_t type)
|
||||
{
|
||||
if (type == PROTOCOL_TYPE_HTTP)
|
||||
return "80";
|
||||
if (type == PROTOCOL_TYPE_REDIS)
|
||||
return "6379";
|
||||
if (type == PROTOCOL_TYPE_MYSQL)
|
||||
return "3306";
|
||||
// Add other protocol here
|
||||
return "1412";
|
||||
}
|
||||
|
||||
static std::string default_proxy_port(uint8_t type)
|
||||
{
|
||||
if (type == PROTOCOL_TYPE_HTTP)
|
||||
return "8888";
|
||||
if (type == PROTOCOL_TYPE_REDIS)
|
||||
return "6378";
|
||||
if (type == PROTOCOL_TYPE_MYSQL)
|
||||
return "3305";
|
||||
// Add other protocol here
|
||||
return "1411";
|
||||
}
|
||||
|
||||
static int check_proxy_type(uint8_t type)
|
||||
{
|
||||
if (type == PROTOCOL_TYPE_HTTP ||
|
||||
type == PROTOCOL_TYPE_REDIS ||
|
||||
type == PROTOCOL_TYPE_MYSQL ||
|
||||
type == PROTOCOL_TYPE_KAFKA)
|
||||
return BASIC_TYPE;
|
||||
|
||||
if (type == PROTOCOL_TYPE_SRPC ||
|
||||
type == PROTOCOL_TYPE_SRPC_HTTP ||
|
||||
type == PROTOCOL_TYPE_BRPC ||
|
||||
type == PROTOCOL_TYPE_TRPC ||
|
||||
type == PROTOCOL_TYPE_TRPC_HTTP)
|
||||
return PROTOBUF_TYPE;
|
||||
|
||||
if (type == PROTOCOL_TYPE_THRIFT ||
|
||||
type == PROTOCOL_TYPE_THRIFT_HTTP)
|
||||
return THRIFT_TYPE;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static std::string proxy_process_request_codes(uint8_t server_type,
|
||||
uint8_t client_type)
|
||||
{
|
||||
if (server_type == client_type)
|
||||
return std::string(R"(
|
||||
*client_task->get_req() = std::move(*req);
|
||||
)");
|
||||
else
|
||||
return std::string(R"( {
|
||||
// TODO: fill the client request to server request
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
static std::string proxy_callback_response_codes(uint8_t server_type,
|
||||
uint8_t client_type)
|
||||
{
|
||||
if (server_type != client_type)
|
||||
return std::string(R"(
|
||||
{
|
||||
// TODO: fill the server response to client response
|
||||
}
|
||||
)");
|
||||
|
||||
if (server_type == PROTOCOL_TYPE_HTTP)
|
||||
return std::string(R"(
|
||||
{
|
||||
const void *body;
|
||||
size_t len;
|
||||
|
||||
resp->get_parsed_body(&body, &len);
|
||||
resp->append_output_body_nocopy(body, len);
|
||||
*proxy_resp = std::move(*resp);
|
||||
}
|
||||
else
|
||||
{
|
||||
proxy_resp->set_status_code("404");
|
||||
proxy_resp->append_output_body_nocopy(
|
||||
"<html>404 Not Found.</html>", 27);
|
||||
}
|
||||
)");
|
||||
|
||||
return std::string(R"(
|
||||
*proxy_resp = std::move(*resp);
|
||||
)");
|
||||
}
|
||||
|
||||
static std::string proxy_redirect_codes(uint8_t type)
|
||||
{
|
||||
if (type == PROTOCOL_TYPE_HTTP)
|
||||
return std::string(R"(
|
||||
config.redirect_max(),)");
|
||||
|
||||
return std::string("");
|
||||
}
|
||||
|
||||
static bool proxy_proxy_transform(const std::string& format, FILE *out,
|
||||
const struct srpc_config *config)
|
||||
{
|
||||
const char *server_type = config->proxy_server_type_string();
|
||||
const char *client_type = config->proxy_client_type_string();
|
||||
std::string server_lower = server_type;
|
||||
std::transform(server_lower.begin(), server_lower.end(),
|
||||
server_lower.begin(), ::tolower);
|
||||
std::string server_port = default_server_port(config->proxy_server_type);
|
||||
std::string proxy_port = default_proxy_port(config->proxy_client_type);
|
||||
|
||||
size_t len = fprintf(out, format.c_str(), client_type, server_type,
|
||||
server_type, client_type, client_type,
|
||||
proxy_callback_response_codes(config->proxy_server_type,
|
||||
config->proxy_client_type).c_str(),
|
||||
// process
|
||||
client_type, client_type, server_lower.c_str(),
|
||||
server_type, server_lower.c_str(),
|
||||
proxy_redirect_codes(config->proxy_server_type).c_str(),
|
||||
proxy_process_request_codes(config->proxy_server_type,
|
||||
config->proxy_client_type).c_str(),
|
||||
client_type,
|
||||
// main
|
||||
client_type, server_type, client_type);
|
||||
|
||||
return len > 0;
|
||||
}
|
||||
|
||||
static bool proxy_config_transform(const std::string& format, FILE *out,
|
||||
const struct srpc_config *config)
|
||||
{
|
||||
std::string server_port = default_server_port(config->proxy_server_type);
|
||||
std::string proxy_port = default_proxy_port(config->proxy_client_type);
|
||||
|
||||
size_t len = fprintf(out, format.c_str(),
|
||||
proxy_port.c_str(), server_port.c_str());
|
||||
|
||||
return len > 0;
|
||||
}
|
||||
|
||||
static bool proxy_rpc_proxy_transform(const std::string& format, FILE *out,
|
||||
const struct srpc_config *config)
|
||||
{
|
||||
const char *server_type = config->proxy_server_type_string();
|
||||
const char *client_type = config->proxy_client_type_string();
|
||||
|
||||
size_t len = fprintf(out, format.c_str(),
|
||||
config->project_name, // not support specified idl file
|
||||
config->project_name, config->project_name,
|
||||
config->project_name, server_type,
|
||||
// main
|
||||
client_type, config->project_name,
|
||||
client_type, server_type);
|
||||
|
||||
return len > 0;
|
||||
}
|
||||
|
||||
ProxyController::ProxyController()
|
||||
{
|
||||
this->config.type = COMMAND_PROXY;
|
||||
this->config.proxy_client_type = PROTOCOL_TYPE_HTTP;
|
||||
this->config.proxy_server_type = PROTOCOL_TYPE_HTTP;
|
||||
|
||||
struct file_info info;
|
||||
|
||||
info = { "proxy/proxy.conf", "proxy.conf", proxy_config_transform };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "basic/server.conf", "server.conf", basic_server_config_transform };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "basic/client.conf", "client.conf", basic_client_config_transform };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "common/GNUmakefile", "GNUmakefile", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "config/Json.h", "config/Json.h", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "config/Json.cc", "config/Json.cc", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
}
|
||||
|
||||
void ProxyController::print_usage(const char *name) const
|
||||
{
|
||||
printf("Usage:\n"
|
||||
" %s proxy <PROJECT_NAME> [FLAGS]\n\n"
|
||||
"Available Flags:\n"
|
||||
" -c : client type for proxy [ Http | Redis | SRPC | SRPCHttp"
|
||||
" | BRPC | Thrift | ThriftHttp | TRPC | TRPCHttp ] (default: Http)\n"
|
||||
" -s : server type for proxy [ Http | Redis | SRPC | SRPCHttp"
|
||||
" | BRPC | Thrift | ThriftHttp | TRPC | TRPCHttp ] (default: Http)\n"
|
||||
" -o : project output path (default: CURRENT_PATH)\n"
|
||||
" -d : path of dependencies (default: COMPILE_PATH)\n"
|
||||
, name);
|
||||
}
|
||||
|
||||
void ProxyController::print_success_info() const
|
||||
{
|
||||
printf("Success:\n make project path \" %s \" done.\n\n",
|
||||
this->config.output_path);
|
||||
printf("Commands:\n cd %s\n make -j\n\n",
|
||||
this->config.output_path);
|
||||
printf("Execute:\n ./server\n ./proxy\n ./client\n\n");
|
||||
}
|
||||
|
||||
bool ProxyController::copy_files()
|
||||
{
|
||||
struct file_info info;
|
||||
|
||||
if (check_proxy_type(this->config.proxy_client_type) == BASIC_TYPE &&
|
||||
check_proxy_type(this->config.proxy_server_type) == BASIC_TYPE)
|
||||
{
|
||||
info = { "common/CMakeLists.txt", "CMakeLists.txt", common_cmake_transform };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "common/util.h", "config/util.h", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "proxy/proxy_main.cc", "proxy_main.cc", proxy_proxy_transform };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "basic/client_main.cc", "client_main.cc", basic_client_transform };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "basic/server_main.cc", "server_main.cc", basic_server_transform };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "config/config_simple.h", "config/config.h", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "config/config_simple.cc", "config/config.cc", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string proxy_main = "proxy/proxy_main_";
|
||||
if (this->config.idl_type == IDL_TYPE_PROTOBUF)
|
||||
proxy_main += "proto.cc";
|
||||
else
|
||||
proxy_main += "thrift.cc";
|
||||
|
||||
info = { std::move(proxy_main), "proxy_main.cc", proxy_rpc_proxy_transform };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "rpc/CMakeLists.txt", "CMakeLists.txt", rpc_cmake_transform };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "config/config_full.h", "config/config.h", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "config/config_full.cc", "config/config.cc", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
if (this->config.specified_idl_file == NULL)
|
||||
this->fill_rpc_default_files();
|
||||
else
|
||||
return false; // TODO: NOT supported yet
|
||||
}
|
||||
|
||||
return CommandController::copy_files();
|
||||
}
|
||||
|
||||
static uint8_t proxy_string_to_type(const char *type)
|
||||
{
|
||||
if (strcasecmp(type, "http") == 0)
|
||||
return PROTOCOL_TYPE_HTTP;
|
||||
else if (strcasecmp(type, "redis") == 0)
|
||||
return PROTOCOL_TYPE_REDIS;
|
||||
else if (strcasecmp(type, "mysql") == 0)
|
||||
return PROTOCOL_TYPE_MYSQL;
|
||||
else if (strcasecmp(type, "kafka") == 0)
|
||||
return PROTOCOL_TYPE_KAFKA;
|
||||
else if (strcasecmp(type, "SRPC") == 0)
|
||||
return PROTOCOL_TYPE_SRPC;
|
||||
else if (strcasecmp(type, "SRPCHttp") == 0)
|
||||
return PROTOCOL_TYPE_SRPC_HTTP;
|
||||
else if (strcasecmp(type, "BRPC") == 0)
|
||||
return PROTOCOL_TYPE_BRPC;
|
||||
else if (strcasecmp(type, "TRPC") == 0)
|
||||
return PROTOCOL_TYPE_TRPC;
|
||||
else if (strcasecmp(type, "TRPCHttp") == 0)
|
||||
return PROTOCOL_TYPE_TRPC_HTTP;
|
||||
else if (strcasecmp(type, "Thrift") == 0)
|
||||
return PROTOCOL_TYPE_THRIFT;
|
||||
else if (strcasecmp(type, "ThriftHTTP") == 0)
|
||||
return PROTOCOL_TYPE_THRIFT_HTTP;
|
||||
|
||||
return PROTOCOL_TYPE_MAX;
|
||||
}
|
||||
|
||||
bool ProxyController::get_opt(int argc, const char **argv)
|
||||
{
|
||||
struct srpc_config *config = &this->config;
|
||||
char c;
|
||||
optind = 3;
|
||||
|
||||
while ((c = getopt(argc, (char * const *)argv, "o:c:s:d:")) != -1)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'o':
|
||||
if (sscanf(optarg, "%s", config->output_path) != 1)
|
||||
return false;
|
||||
break;
|
||||
case 'c':
|
||||
config->proxy_client_type = proxy_string_to_type(optarg);
|
||||
break;
|
||||
case 's':
|
||||
config->proxy_server_type = proxy_string_to_type(optarg);
|
||||
break;
|
||||
case 'd':
|
||||
config->specified_depend_path = true;
|
||||
memset(config->depend_path, 0, MAXPATHLEN);
|
||||
if (sscanf(optarg, "%s", config->depend_path) != 1)
|
||||
return false;
|
||||
default:
|
||||
printf("Error:\n Unknown args : %s\n\n", argv[optind - 1]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProxyController::check_args()
|
||||
{
|
||||
int server_type = check_proxy_type(this->config.proxy_server_type);
|
||||
int client_type = check_proxy_type(this->config.proxy_client_type);
|
||||
|
||||
if (CommandController::check_args() == false)
|
||||
return false;
|
||||
|
||||
if (client_type < 0 || server_type < 0)
|
||||
{
|
||||
printf("Error:\n Invalid type : %s, %s\n\n",
|
||||
this->config.proxy_server_type_string(),
|
||||
this->config.proxy_client_type_string());
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: temperarily only support workflow to workflow, rpc to rpc
|
||||
if ((client_type == BASIC_TYPE && server_type > BASIC_TYPE) ||
|
||||
(server_type == BASIC_TYPE && client_type > BASIC_TYPE))
|
||||
{
|
||||
printf("Error:\n Temperarily not support %s and %s together\n\n",
|
||||
this->config.proxy_server_type_string(),
|
||||
this->config.proxy_client_type_string());
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: temperarily NOT support protobuf with thrift
|
||||
if ((client_type == PROTOBUF_TYPE && server_type == THRIFT_TYPE) ||
|
||||
(server_type == PROTOBUF_TYPE && client_type == THRIFT_TYPE))
|
||||
{
|
||||
printf("Error:\n Temperarily not support %s and %s together\n\n",
|
||||
this->config.proxy_server_type_string(),
|
||||
this->config.proxy_client_type_string());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (client_type == PROTOBUF_TYPE)
|
||||
{
|
||||
this->config.idl_type = IDL_TYPE_PROTOBUF;
|
||||
}
|
||||
else if (client_type == THRIFT_TYPE)
|
||||
{
|
||||
// this->config.idl_type = IDL_TYPE_THRIFT;
|
||||
printf("Error:\n Temperarily not support IDL thrift.\n\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
227
tools/srpc_rpc_controller.cc
Normal file
227
tools/srpc_rpc_controller.cc
Normal file
@ -0,0 +1,227 @@
|
||||
/*
|
||||
Copyright (c) 2022 Sogou, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "srpc_controller.h"
|
||||
|
||||
RPCController::RPCController()
|
||||
{
|
||||
this->config.type = COMMAND_RPC;
|
||||
struct file_info info;
|
||||
|
||||
info = { "rpc/CMakeLists.txt", "CMakeLists.txt", rpc_cmake_transform };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "common/config.json", "client.conf", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "common/config.json", "server.conf", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "common/GNUmakefile", "GNUmakefile", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "config/Json.h", "config/Json.h", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "config/Json.cc", "config/Json.cc", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "config/config_full.h", "config/config.h", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
|
||||
info = { "config/config_full.cc", "config/config.cc", nullptr };
|
||||
this->default_files.push_back(info);
|
||||
}
|
||||
|
||||
void RPCController::print_usage(const char *name) const
|
||||
{
|
||||
printf("Usage:\n"
|
||||
" %s rpc <PROJECT_NAME> [FLAGS]\n\n"
|
||||
"Available Flags:\n"
|
||||
" -r : rpc type [ SRPC | SRPCHttp | BRPC | Thrift | "
|
||||
"ThriftHttp | TRPC | TRPCHttp ] (default: SRPC)\n"
|
||||
" -o : project output path (default: CURRENT_PATH)\n"
|
||||
" -s : service name (default: PROJECT_NAME)\n"
|
||||
" -i : idl type [ protobuf | thrift ] (default: protobuf)\n"
|
||||
" -x : data type [ protobuf | thrift | json ] "
|
||||
"(default: idl type. json for http)\n"
|
||||
" -c : compress type [ gzip | zlib | snappy | lz4 ] "
|
||||
"(default: no compression)\n"
|
||||
" -d : path of dependencies (default: COMPILE_PATH)\n"
|
||||
" -f : specify the idl_file to generate codes "
|
||||
"(default: templates/rpc/IDL_FILE)\n"
|
||||
" -p : specify the path for idl_file to depend "
|
||||
"(default: templates/rpc/)\n"
|
||||
, name);
|
||||
}
|
||||
|
||||
bool RPCController::copy_files()
|
||||
{
|
||||
struct srpc_config *config = &this->config;
|
||||
|
||||
if (config->specified_idl_file == NULL) // fill the default rpc files
|
||||
{
|
||||
this->fill_rpc_default_files();
|
||||
}
|
||||
else
|
||||
{
|
||||
// copy specified idl file and generate rpc files
|
||||
struct GeneratorParams params;
|
||||
params.out_dir = config->output_path;
|
||||
params.input_dir = config->specified_idl_path;
|
||||
if (params.input_dir[params.input_dir.length() - 1] != '/')
|
||||
params.input_dir += "/";
|
||||
|
||||
params.idl_file = config->specified_idl_file;
|
||||
|
||||
auto pos = params.idl_file.find_last_of('/');
|
||||
if (pos != std::string::npos)
|
||||
params.idl_file = params.idl_file.substr(pos + 1);
|
||||
|
||||
std::string out_file = std::string(config->output_path) +
|
||||
std::string("/") + params.idl_file;
|
||||
|
||||
if (CommandController::copy_single_file(config->specified_idl_file,
|
||||
out_file, nullptr) == false)
|
||||
return false;
|
||||
|
||||
ControlGenerator gen(config);
|
||||
printf("Info: srpc-ctl generator begin.\n");
|
||||
gen.generate(params);
|
||||
printf("Info: srpc-ctl generator done.\n\n");
|
||||
}
|
||||
|
||||
return CommandController::copy_files();
|
||||
}
|
||||
|
||||
bool RPCController::get_opt(int argc, const char **argv)
|
||||
{
|
||||
struct srpc_config *config = &this->config;
|
||||
char c;
|
||||
optind = 3;
|
||||
|
||||
while ((c = getopt(argc, (char * const *)argv,
|
||||
"o:r:i:x:c:s:t:d:f:p:")) != -1)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'o':
|
||||
if (sscanf(optarg, "%s", config->output_path) != 1)
|
||||
return false;
|
||||
break;
|
||||
case 'r':
|
||||
config->set_rpc_type(optarg);
|
||||
break;
|
||||
case 'i':
|
||||
config->set_idl_type(optarg);
|
||||
break;
|
||||
case 'x':
|
||||
config->set_data_type(optarg);
|
||||
break;
|
||||
case 'c':
|
||||
config->set_compress_type(optarg);
|
||||
break;
|
||||
case 's':
|
||||
config->service_name = optarg;
|
||||
break;
|
||||
case 't':
|
||||
if (sscanf(optarg, "%s", config->template_path) != 1)
|
||||
return false; //TODO:
|
||||
break;
|
||||
case 'd':
|
||||
config->specified_depend_path = true;
|
||||
memset(config->depend_path, 0, MAXPATHLEN);
|
||||
if (sscanf(optarg, "%s", config->depend_path) != 1)
|
||||
return false;
|
||||
case 'f':
|
||||
config->specified_idl_file = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
config->specified_idl_path = optarg;
|
||||
break;
|
||||
default:
|
||||
printf("Error:\n Unknown args : %s\n\n", argv[optind - 1]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RPCController::check_args()
|
||||
{
|
||||
if (CommandController::check_args() == false)
|
||||
return false;
|
||||
|
||||
struct srpc_config *config = &this->config;
|
||||
|
||||
if (config->rpc_type == PROTOCOL_TYPE_MAX ||
|
||||
config->idl_type == IDL_TYPE_MAX ||
|
||||
config->data_type == DATA_TYPE_MAX ||
|
||||
config->compress_type == COMPRESS_TYPE_MAX)
|
||||
{
|
||||
printf("Error:\n Invalid rpc args.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (config->rpc_type)
|
||||
{
|
||||
case PROTOCOL_TYPE_THRIFT:
|
||||
case PROTOCOL_TYPE_THRIFT_HTTP:
|
||||
if (config->idl_type == IDL_TYPE_PROTOBUF ||
|
||||
config->data_type == DATA_TYPE_PROTOBUF)
|
||||
{
|
||||
printf("Error:\n "
|
||||
"\" %s \" does NOT support protobuf as idl or data type.\n",
|
||||
config->rpc_type_string());
|
||||
return false;
|
||||
}
|
||||
if (config->idl_type == IDL_TYPE_DEFAULT)
|
||||
config->idl_type = IDL_TYPE_THRIFT; // as default;
|
||||
break;
|
||||
case PROTOCOL_TYPE_BRPC:
|
||||
case PROTOCOL_TYPE_TRPC:
|
||||
case PROTOCOL_TYPE_TRPC_HTTP:
|
||||
if (config->idl_type == IDL_TYPE_THRIFT ||
|
||||
config->data_type == DATA_TYPE_THRIFT)
|
||||
{
|
||||
printf("Error:\n "
|
||||
"\" %s \" does NOT support thrift as idl or data type.\n",
|
||||
config->rpc_type_string());
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
if (config->idl_type == IDL_TYPE_DEFAULT)
|
||||
config->idl_type = IDL_TYPE_PROTOBUF; // as default;
|
||||
break;
|
||||
}
|
||||
|
||||
if (config->prepare_specified_idl_file() == false)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
9
tools/templates/basic/client.conf
Normal file
9
tools/templates/basic/client.conf
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"client":
|
||||
{
|
||||
"remote_host": "127.0.0.1",
|
||||
"remote_port": %u,%s
|
||||
"retry_max": 2%s
|
||||
}
|
||||
}
|
||||
|
53
tools/templates/basic/client_main.cc
Normal file
53
tools/templates/basic/client_main.cc
Normal file
@ -0,0 +1,53 @@
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include "workflow/%sMessage.h"
|
||||
#include "workflow/WFTaskFactory.h"
|
||||
#include "workflow/WFFacilities.h"
|
||||
|
||||
#include "config/config.h"
|
||||
|
||||
static WFFacilities::WaitGroup wait_group(1);
|
||||
static srpc::RPCConfig config;
|
||||
|
||||
void sig_handler(int signo)
|
||||
{
|
||||
wait_group.done();
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
if (config.load("./client.conf") == false)
|
||||
{
|
||||
perror("Load config failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
signal(SIGTERM, sig_handler);
|
||||
}
|
||||
|
||||
void callback(WF%sTask *task)
|
||||
{
|
||||
int state = task->get_state();
|
||||
int error = task->get_error();
|
||||
fprintf(stderr, "%s client state = %%d error = %%d\n", state, error);
|
||||
%s
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
init();
|
||||
|
||||
std::string url = std::string("%s://") + %sconfig.client_host() +
|
||||
std::string(":") + std::to_string(config.client_port());
|
||||
|
||||
WF%sTask *task = WFTaskFactory::create_%s_task(url,%s
|
||||
config.retry_max(),
|
||||
callback);
|
||||
%s
|
||||
task->start();
|
||||
|
||||
wait_group.wait();
|
||||
return 0;
|
||||
}
|
||||
|
7
tools/templates/basic/server.conf
Normal file
7
tools/templates/basic/server.conf
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"server":
|
||||
{
|
||||
"port": %u
|
||||
}
|
||||
}
|
||||
|
50
tools/templates/basic/server_main.cc
Normal file
50
tools/templates/basic/server_main.cc
Normal file
@ -0,0 +1,50 @@
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include "workflow/WF%sServer.h"
|
||||
#include "workflow/WFFacilities.h"
|
||||
|
||||
#include "config/util.h"
|
||||
#include "config/config.h"
|
||||
|
||||
static WFFacilities::WaitGroup wait_group(1);
|
||||
static srpc::RPCConfig config;
|
||||
|
||||
void sig_handler(int signo)
|
||||
{
|
||||
wait_group.done();
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
if (config.load("./server.conf") == false)
|
||||
{
|
||||
perror("Load config failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
signal(SIGTERM, sig_handler);
|
||||
}
|
||||
|
||||
void process(WF%sTask *task)
|
||||
{
|
||||
// delete the example codes and fill your logic
|
||||
%s
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
init();
|
||||
|
||||
WF%sServer server(process);
|
||||
|
||||
if (server.start(config.server_port()) == 0)
|
||||
{
|
||||
fprintf(stderr, "%s server start, port %%u\n", config.server_port());
|
||||
wait_group.wait();
|
||||
server.stop();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
33
tools/templates/common/CMakeLists.txt
Normal file
33
tools/templates/common/CMakeLists.txt
Normal file
@ -0,0 +1,33 @@
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
project(%s LANGUAGES CXX)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_BUILD_TYPE RelWithDebInfo)
|
||||
|
||||
# Find all the dependencies
|
||||
find_package(OpenSSL REQUIRED)
|
||||
set(Workflow_DIR "%s")
|
||||
find_package(Workflow REQUIRED CONFIG HINTS ${Workflow_DIR})
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_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)
|
||||
|
||||
# Set all the libraries here
|
||||
set(LIB ${Workflow_LIB} pthread OpenSSL::SSL OpenSSL::Crypto)
|
||||
|
||||
# Add all the common codes here
|
||||
set(COMMON_CODE config/config.cc config/Json.cc%s)
|
||||
|
||||
# Add header directories and library directories here
|
||||
include_directories(${OPENSSL_INCLUDE_DIR} ${WORKFLOW_INCLUDE_DIR})
|
||||
link_directories(${OPENSSL_LINK_DIR} ${WORKFLOW_LIB_DIR})
|
||||
|
||||
# Build executable outputs
|
||||
set(PROJECT_OUTPUT server%s)
|
||||
foreach(output ${PROJECT_OUTPUT})
|
||||
add_executable(${output} ${output}_main.cc ${COMMON_CODE})
|
||||
target_link_libraries(${output} ${LIB})
|
||||
endforeach()
|
||||
|
15
tools/templates/common/GNUmakefile
Normal file
15
tools/templates/common/GNUmakefile
Normal file
@ -0,0 +1,15 @@
|
||||
ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
BUILD_DIR := build.cmake
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: base
|
||||
make -C $(BUILD_DIR) -f Makefile
|
||||
|
||||
base:
|
||||
mkdir -p $(BUILD_DIR)
|
||||
cd $(BUILD_DIR) && cmake $(ROOT_DIR)
|
||||
|
||||
clean:
|
||||
make -C $(BUILD_DIR) clean
|
||||
rm -rf $(BUILD_DIR)
|
107
tools/templates/common/config.json
Normal file
107
tools/templates/common/config.json
Normal file
@ -0,0 +1,107 @@
|
||||
{
|
||||
"server":
|
||||
{
|
||||
"port": 8080
|
||||
},
|
||||
|
||||
"client":
|
||||
{
|
||||
"remote_host": "127.0.0.1",
|
||||
"remote_port": 8080,
|
||||
"is_ssl" : false,
|
||||
"redirect_max": 2,
|
||||
"retry_max": 1,
|
||||
"user_name": "root",
|
||||
"password": "",
|
||||
"callee" : "rpc_client"
|
||||
},
|
||||
|
||||
"global":
|
||||
{
|
||||
"poller_threads": 4,
|
||||
"handler_threads": 20,
|
||||
"dns_threads": 4,
|
||||
"compute_threads": -1,
|
||||
"dns_ttl_default": 43200,
|
||||
"dns_ttl_min": 180,
|
||||
"resolv_conf_path": "/etc/resolv.conf",
|
||||
"hosts_path": "/etc/hosts",
|
||||
"endpoint_params":
|
||||
{
|
||||
"max_connections": 200,
|
||||
"connect_timeout": 10000,
|
||||
"response_timeout": 10000,
|
||||
"ssl_connect_timeout": 10000,
|
||||
"use_tls_sni": false
|
||||
},
|
||||
"dns_server_params":
|
||||
{
|
||||
"max_connections": 200
|
||||
}
|
||||
},
|
||||
|
||||
"metrics":[
|
||||
{
|
||||
"filter": "prometheus",
|
||||
"port": 8000
|
||||
},
|
||||
{
|
||||
"filter": "opentelemetry",
|
||||
"address": "http://opentelemetry.com:4389",
|
||||
"redirect_max": 0,
|
||||
"retry_max": 1,
|
||||
"report_threshold": 100,
|
||||
"report_interval_ms": 1000,
|
||||
"attributes": [
|
||||
{
|
||||
"key": "tenant.id",
|
||||
"value": "abcd"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
"trace":[
|
||||
{
|
||||
"filter": "default",
|
||||
"span_per_second": 1000
|
||||
},
|
||||
{
|
||||
"filter": "opentelemetry",
|
||||
"address": "http://opentelemetry.com:4389",
|
||||
"redirect_max": 0,
|
||||
"retry_max": 1,
|
||||
"span_per_second": 1000,
|
||||
"report_threshold": 100,
|
||||
"report_interval_ms": 1000,
|
||||
"attributes": [
|
||||
{
|
||||
"key": "tenant.id",
|
||||
"value": "abcd"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
"upstream":[
|
||||
{
|
||||
"name": "test_weighted_random_host",
|
||||
"type": "weighted_random",
|
||||
"try_another": false,
|
||||
"server":[
|
||||
{ "host": "127.0.0.1:8080", "params": {"weight": 2} },
|
||||
{ "host": "127.0.0.1:8081" }
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"name": "test_consistent_hash_host",
|
||||
"type": "consistent_hash",
|
||||
"server":[
|
||||
{ "host": "127.0.0.1:8083" },
|
||||
{ "host": "127.0.0.1:8084" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
35
tools/templates/common/util.h
Normal file
35
tools/templates/common/util.h
Normal file
@ -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);
|
||||
}
|
||||
|
933
tools/templates/config/Json.cc
Normal file
933
tools/templates/config/Json.cc
Normal file
@ -0,0 +1,933 @@
|
||||
#include "Json.h"
|
||||
|
||||
namespace wfrest
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
json_value_t* json_value_copy(const json_value_t* val);
|
||||
|
||||
json_value_t* json_value_copy_object(const json_value_t* val)
|
||||
{
|
||||
json_value_t* dest_val = json_value_create(JSON_VALUE_OBJECT);
|
||||
json_object_t* dest_obj = json_value_object(dest_val);
|
||||
json_object_t* obj = json_value_object(val);
|
||||
const char* name;
|
||||
|
||||
json_object_for_each(name, val, obj)
|
||||
json_object_append(dest_obj, name, 0, json_value_copy(val));
|
||||
|
||||
return dest_val;
|
||||
}
|
||||
|
||||
json_value_t* json_value_copy_array(const json_value_t* val)
|
||||
{
|
||||
json_value_t* dest_val = json_value_create(JSON_VALUE_ARRAY);
|
||||
json_array_t* dest_arr = json_value_array(dest_val);
|
||||
json_array_t* arr = json_value_array(val);
|
||||
json_array_for_each(val, arr)
|
||||
json_array_append(dest_arr, 0, json_value_copy(val));
|
||||
return dest_val;
|
||||
}
|
||||
|
||||
json_value_t* json_value_copy(const json_value_t* val)
|
||||
{
|
||||
switch (json_value_type(val))
|
||||
{
|
||||
case JSON_VALUE_STRING:
|
||||
return json_value_create(JSON_VALUE_STRING, json_value_string(val));
|
||||
case JSON_VALUE_NUMBER:
|
||||
return json_value_create(JSON_VALUE_NUMBER, json_value_number(val));
|
||||
case JSON_VALUE_OBJECT:
|
||||
return json_value_copy_object(val);
|
||||
case JSON_VALUE_ARRAY:
|
||||
return json_value_copy_array(val);
|
||||
default:
|
||||
return json_value_create(json_value_type(val));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Json::Json() : node_(json_value_create(JSON_VALUE_NULL))
|
||||
{
|
||||
}
|
||||
|
||||
Json::Json(const std::string& str, bool parse_flag)
|
||||
: node_(json_value_parse(str.c_str()))
|
||||
{
|
||||
}
|
||||
|
||||
Json::Json(const std::string& str)
|
||||
: node_(json_value_create(JSON_VALUE_STRING, str.c_str()))
|
||||
{
|
||||
}
|
||||
|
||||
Json::Json(const char* str) : node_(json_value_create(JSON_VALUE_STRING, str))
|
||||
{
|
||||
}
|
||||
|
||||
Json::Json(std::nullptr_t null) : node_(json_value_create(JSON_VALUE_NULL))
|
||||
{
|
||||
}
|
||||
|
||||
Json::Json(double val) : node_(json_value_create(JSON_VALUE_NUMBER, val))
|
||||
{
|
||||
}
|
||||
|
||||
Json::Json(int val)
|
||||
: node_(json_value_create(JSON_VALUE_NUMBER, static_cast<double>(val)))
|
||||
{
|
||||
}
|
||||
|
||||
Json::Json(bool val)
|
||||
: node_(val ? json_value_create(JSON_VALUE_TRUE)
|
||||
: json_value_create(JSON_VALUE_FALSE))
|
||||
{
|
||||
}
|
||||
|
||||
// todo : optimize
|
||||
Json::Json(const Array& val)
|
||||
: node_(json_value_copy(val.node_)), parent_(val.parent_),
|
||||
parent_key_(val.parent_key_)
|
||||
{
|
||||
}
|
||||
|
||||
Json::Json(const Object& val)
|
||||
: node_(json_value_copy(val.node_)), parent_(val.parent_),
|
||||
parent_key_(val.parent_key_)
|
||||
{
|
||||
}
|
||||
|
||||
Json::Json(Array&& val)
|
||||
: node_(val.node_), parent_(val.parent_),
|
||||
parent_key_(std::move(val.parent_key_))
|
||||
{
|
||||
val.node_ = nullptr;
|
||||
val.parent_ = nullptr;
|
||||
}
|
||||
|
||||
Json::Json(Object&& val)
|
||||
: node_(val.node_), parent_(val.parent_),
|
||||
parent_key_(std::move(val.parent_key_))
|
||||
{
|
||||
val.node_ = nullptr;
|
||||
val.parent_ = nullptr;
|
||||
}
|
||||
|
||||
Json::~Json()
|
||||
{
|
||||
if (allocated_)
|
||||
{
|
||||
destroy_node(node_);
|
||||
}
|
||||
}
|
||||
|
||||
// watcher constructor
|
||||
Json::Json(const json_value_t* parent, std::string&& key)
|
||||
: node_(json_value_create(JSON_VALUE_NULL)), parent_(parent),
|
||||
parent_key_(std::move(key)), allocated_(false)
|
||||
{
|
||||
}
|
||||
|
||||
Json::Json(const json_value_t* parent, const std::string& key)
|
||||
: node_(json_value_create(JSON_VALUE_NULL)), parent_(parent),
|
||||
parent_key_(key), allocated_(false)
|
||||
{
|
||||
}
|
||||
|
||||
Json::Json(const json_value_t* parent)
|
||||
: node_(json_value_create(JSON_VALUE_NULL)), parent_(parent),
|
||||
allocated_(false)
|
||||
{
|
||||
}
|
||||
|
||||
Json::Json(const json_value_t* node, const json_value_t* parent)
|
||||
: node_(node), parent_(parent), allocated_(false)
|
||||
{
|
||||
}
|
||||
|
||||
Json::Json(const json_value_t* node, const json_value_t* parent,
|
||||
std::string&& key)
|
||||
: node_(node), parent_(parent), parent_key_(std::move(key)),
|
||||
allocated_(false)
|
||||
{
|
||||
}
|
||||
|
||||
Json::Json(const json_value_t* node, const json_value_t* parent,
|
||||
const std::string& key)
|
||||
: node_(node), parent_(parent), parent_key_(key), allocated_(false)
|
||||
{
|
||||
}
|
||||
|
||||
Json::Json(const Empty&)
|
||||
{
|
||||
}
|
||||
|
||||
Json::Json(Json&& other)
|
||||
: node_(other.node_), parent_(other.parent_),
|
||||
parent_key_(std::move(other.parent_key_)), allocated_(other.allocated_)
|
||||
{
|
||||
other.node_ = nullptr;
|
||||
other.parent_ = nullptr;
|
||||
}
|
||||
|
||||
Json& Json::operator=(Json&& other)
|
||||
{
|
||||
if (this == &other)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
if (allocated_)
|
||||
{
|
||||
destroy_node(node_);
|
||||
}
|
||||
node_ = other.node_;
|
||||
other.node_ = nullptr;
|
||||
parent_ = other.parent_;
|
||||
other.parent_ = nullptr;
|
||||
parent_key_ = std::move(other.parent_key_);
|
||||
allocated_ = other.allocated_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Json Json::parse(const std::string& str)
|
||||
{
|
||||
return Json(str, true);
|
||||
}
|
||||
|
||||
Json Json::parse(const std::ifstream& stream)
|
||||
{
|
||||
std::stringstream buffer;
|
||||
buffer << stream.rdbuf();
|
||||
return Json(buffer.str(), true);
|
||||
}
|
||||
|
||||
Json Json::parse(FILE* fp)
|
||||
{
|
||||
if (fp == nullptr)
|
||||
{
|
||||
return Json(Empty());
|
||||
}
|
||||
fseek(fp, 0, SEEK_END);
|
||||
long length = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
char* buffer = (char*)malloc(length + 1);
|
||||
buffer[length] = '\0';
|
||||
size_t ret = fread(buffer, 1, length, fp);
|
||||
Json js;
|
||||
if (ret != length)
|
||||
{
|
||||
js = Json(Empty());
|
||||
}
|
||||
else
|
||||
{
|
||||
js = Json(buffer, true);
|
||||
}
|
||||
free(buffer);
|
||||
return js;
|
||||
}
|
||||
|
||||
std::string Json::dump() const
|
||||
{
|
||||
return dump(0);
|
||||
}
|
||||
|
||||
std::string Json::dump(int spaces) const
|
||||
{
|
||||
std::string str;
|
||||
str.reserve(64);
|
||||
value_convert(node_, spaces, 0, &str);
|
||||
return str;
|
||||
}
|
||||
|
||||
Json Json::operator[](const char* key)
|
||||
{
|
||||
if (!is_valid())
|
||||
{
|
||||
return Json(Empty());
|
||||
}
|
||||
if (is_null() && is_root())
|
||||
{
|
||||
// todo : need is_root here?
|
||||
to_object();
|
||||
}
|
||||
else if (is_object())
|
||||
{
|
||||
// if exists
|
||||
json_object_t* obj = json_value_object(node_);
|
||||
const json_value_t* res = json_object_find(key, obj);
|
||||
if (res != nullptr)
|
||||
{
|
||||
return Json(res, node_, key);
|
||||
}
|
||||
}
|
||||
if (is_placeholder())
|
||||
{
|
||||
destroy_node(node_);
|
||||
json_object_t* parent_obj = json_value_object(parent_);
|
||||
node_ = json_object_append(parent_obj, parent_key_.c_str(),
|
||||
JSON_VALUE_OBJECT);
|
||||
}
|
||||
if (!is_object())
|
||||
{
|
||||
return Json(Empty());
|
||||
}
|
||||
// (null, parent(node_), key)
|
||||
return Json(node_, key);
|
||||
}
|
||||
|
||||
Json Json::operator[](const char* key) const
|
||||
{
|
||||
if (!is_valid() || !is_object())
|
||||
{
|
||||
return Json(Empty());
|
||||
}
|
||||
const json_value_t* val = node_;
|
||||
json_object_t* obj = json_value_object(val);
|
||||
const json_value_t* res = json_object_find(key, obj);
|
||||
if (res != nullptr)
|
||||
{
|
||||
return Json(res, node_);
|
||||
}
|
||||
return Json(Empty());
|
||||
}
|
||||
|
||||
Json Json::operator[](const std::string& key)
|
||||
{
|
||||
return this->operator[](key.c_str());
|
||||
}
|
||||
|
||||
Json Json::operator[](const std::string& key) const
|
||||
{
|
||||
return this->operator[](key.c_str());
|
||||
}
|
||||
|
||||
bool Json::has(const std::string& key) const
|
||||
{
|
||||
json_object_t* obj = json_value_object(node_);
|
||||
const json_value_t* find = json_object_find(key.c_str(), obj);
|
||||
return find != nullptr;
|
||||
}
|
||||
|
||||
void Json::erase(const std::string& key)
|
||||
{
|
||||
if (!is_object())
|
||||
return;
|
||||
json_object_t* obj = json_value_object(node_);
|
||||
const json_value_t* find = json_object_find(key.c_str(), obj);
|
||||
if (find == nullptr)
|
||||
return;
|
||||
json_value_t* remove_val = json_object_remove(find, obj);
|
||||
json_value_destroy(remove_val);
|
||||
}
|
||||
|
||||
Json Json::operator[](int index)
|
||||
{
|
||||
if (!is_array() || index < 0 || index > this->size())
|
||||
{
|
||||
return Json(Empty());
|
||||
}
|
||||
const json_value_t* val;
|
||||
json_array_t* arr = json_value_array(node_);
|
||||
json_array_for_each(val, arr)
|
||||
{
|
||||
if (index == 0)
|
||||
{
|
||||
return Json(val, node_);
|
||||
}
|
||||
index--;
|
||||
}
|
||||
return Json(Empty());
|
||||
}
|
||||
|
||||
void Json::erase(int index)
|
||||
{
|
||||
if (!is_array())
|
||||
return;
|
||||
int cnt = 0;
|
||||
json_array_t* arr = json_value_array(node_);
|
||||
const json_value_t* arr_cursor = nullptr;
|
||||
json_array_for_each(arr_cursor, arr)
|
||||
{
|
||||
if (cnt++ == index)
|
||||
break;
|
||||
}
|
||||
json_value_t* remove_val = json_array_remove(arr_cursor, arr);
|
||||
json_value_destroy(remove_val);
|
||||
}
|
||||
|
||||
Json Json::operator[](int index) const
|
||||
{
|
||||
if (!is_array() || index < 0 || index > this->size())
|
||||
{
|
||||
return Json(Empty());
|
||||
}
|
||||
const json_value_t* val;
|
||||
json_array_t* arr = json_value_array(node_);
|
||||
json_array_for_each(val, arr)
|
||||
{
|
||||
if (index == 0)
|
||||
{
|
||||
return Json(val, node_);
|
||||
}
|
||||
index--;
|
||||
}
|
||||
return Json(Empty());
|
||||
}
|
||||
|
||||
bool Json::can_obj_push_back()
|
||||
{
|
||||
if (is_incomplete())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (is_placeholder() ||
|
||||
(parent_ != nullptr && json_value_type(parent_) == JSON_VALUE_OBJECT))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (is_root() && is_null())
|
||||
{
|
||||
to_object();
|
||||
}
|
||||
return is_object();
|
||||
}
|
||||
|
||||
void Json::push_back(const std::string& key, bool val)
|
||||
{
|
||||
if (!can_obj_push_back())
|
||||
{
|
||||
return;
|
||||
}
|
||||
json_object_t* obj = json_value_object(node_);
|
||||
int type = val ? JSON_VALUE_TRUE : JSON_VALUE_FALSE;
|
||||
json_object_append(obj, key.c_str(), type);
|
||||
}
|
||||
|
||||
void Json::push_back(const std::string& key, std::nullptr_t val)
|
||||
{
|
||||
if (!can_obj_push_back())
|
||||
{
|
||||
return;
|
||||
}
|
||||
json_object_t* obj = json_value_object(node_);
|
||||
json_object_append(obj, key.c_str(), JSON_VALUE_NULL);
|
||||
}
|
||||
|
||||
void Json::push_back(const std::string& key, const std::string& val)
|
||||
{
|
||||
push_back(key, val.c_str());
|
||||
}
|
||||
|
||||
void Json::push_back(const std::string& key, const char* val)
|
||||
{
|
||||
if (!can_obj_push_back())
|
||||
{
|
||||
return;
|
||||
}
|
||||
json_object_t* obj = json_value_object(node_);
|
||||
json_object_append(obj, key.c_str(), JSON_VALUE_STRING, val);
|
||||
}
|
||||
|
||||
void Json::push_back(const std::string& key, const Json& val)
|
||||
{
|
||||
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.node_ = nullptr;
|
||||
}
|
||||
|
||||
void Json::placeholder_push_back(const std::string& key, bool val)
|
||||
{
|
||||
json_object_t* obj = json_value_object(parent_);
|
||||
destroy_node(node_);
|
||||
if (val)
|
||||
{
|
||||
node_ = json_object_append(obj, key.c_str(), JSON_VALUE_TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
node_ = json_object_append(obj, key.c_str(), JSON_VALUE_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
void Json::placeholder_push_back(const std::string& key, std::nullptr_t val)
|
||||
{
|
||||
json_object_t* obj = json_value_object(parent_);
|
||||
destroy_node(node_);
|
||||
node_ = json_object_append(obj, key.c_str(), JSON_VALUE_NULL);
|
||||
}
|
||||
|
||||
void Json::placeholder_push_back(const std::string& key, const std::string& val)
|
||||
{
|
||||
placeholder_push_back(key, val.c_str());
|
||||
}
|
||||
|
||||
void Json::placeholder_push_back(const std::string& key, const char* val)
|
||||
{
|
||||
json_object_t* obj = json_value_object(parent_);
|
||||
destroy_node(node_);
|
||||
node_ = json_object_append(obj, key.c_str(), JSON_VALUE_STRING, val);
|
||||
}
|
||||
|
||||
void Json::placeholder_push_back(const std::string& key, const Json& val)
|
||||
{
|
||||
json_object_t* obj = json_value_object(parent_);
|
||||
destroy_node(node_);
|
||||
Json copy_json = val.copy();
|
||||
node_ = json_object_append(obj, key.c_str(), 0, copy_json.node_);
|
||||
copy_json.node_ = nullptr;
|
||||
}
|
||||
|
||||
void Json::normal_push_back(const std::string& key, bool val)
|
||||
{
|
||||
json_object_t* obj = json_value_object(parent_);
|
||||
const json_value_t* find = json_object_find(key.c_str(), obj);
|
||||
int type = val ? JSON_VALUE_TRUE : JSON_VALUE_FALSE;
|
||||
if (find == nullptr)
|
||||
{
|
||||
json_object_append(obj, key.c_str(), type);
|
||||
return;
|
||||
}
|
||||
json_object_insert_before(find, obj, key.c_str(), type);
|
||||
json_value_t* remove_val = json_object_remove(find, obj);
|
||||
json_value_destroy(remove_val);
|
||||
}
|
||||
|
||||
void Json::normal_push_back(const std::string& key, std::nullptr_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_NULL);
|
||||
return;
|
||||
}
|
||||
json_object_insert_before(find, obj, key.c_str(), JSON_VALUE_NULL);
|
||||
json_value_t* remove_val = json_object_remove(find, obj);
|
||||
json_value_destroy(remove_val);
|
||||
}
|
||||
|
||||
void Json::normal_push_back(const std::string& key, const std::string& val)
|
||||
{
|
||||
normal_push_back(key, val.c_str());
|
||||
}
|
||||
|
||||
void Json::normal_push_back(const std::string& key, const char* 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_STRING, val);
|
||||
return;
|
||||
}
|
||||
json_object_insert_before(find, obj, key.c_str(), JSON_VALUE_STRING, val);
|
||||
json_value_t* remove_val = json_object_remove(find, obj);
|
||||
json_value_destroy(remove_val);
|
||||
}
|
||||
|
||||
void Json::normal_push_back(const std::string& key, const Json& 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.node_ = nullptr;
|
||||
return;
|
||||
}
|
||||
json_object_insert_before(find, obj, key.c_str(), 0, copy_json.node_);
|
||||
copy_json.node_ = nullptr;
|
||||
json_value_t* remove_val = json_object_remove(find, obj);
|
||||
json_value_destroy(remove_val);
|
||||
}
|
||||
|
||||
bool Json::can_arr_push_back()
|
||||
{
|
||||
if (is_root() && is_null())
|
||||
{
|
||||
to_array();
|
||||
}
|
||||
return is_array();
|
||||
}
|
||||
|
||||
Json Json::copy() const
|
||||
{
|
||||
return Json(json_value_copy(node_), nullptr);
|
||||
}
|
||||
|
||||
void Json::push_back(bool val)
|
||||
{
|
||||
if (!can_arr_push_back())
|
||||
{
|
||||
return;
|
||||
}
|
||||
json_array_t* arr = json_value_array(node_);
|
||||
int type = val ? JSON_VALUE_TRUE : JSON_VALUE_FALSE;
|
||||
json_array_append(arr, type);
|
||||
}
|
||||
|
||||
void Json::push_back(const std::string& val)
|
||||
{
|
||||
push_back(val.c_str());
|
||||
}
|
||||
|
||||
void Json::push_back(const char* val)
|
||||
{
|
||||
if (!can_arr_push_back())
|
||||
{
|
||||
return;
|
||||
}
|
||||
json_array_t* arr = json_value_array(node_);
|
||||
json_array_append(arr, JSON_VALUE_STRING, val);
|
||||
}
|
||||
|
||||
void Json::push_back(std::nullptr_t val)
|
||||
{
|
||||
if (!can_arr_push_back())
|
||||
{
|
||||
return;
|
||||
}
|
||||
json_array_t* arr = json_value_array(node_);
|
||||
json_array_append(arr, JSON_VALUE_NULL);
|
||||
}
|
||||
|
||||
void Json::push_back(const Json& val)
|
||||
{
|
||||
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.node_ = nullptr;
|
||||
}
|
||||
|
||||
void Json::update_arr(bool val)
|
||||
{
|
||||
json_array_t* arr = json_value_array(parent_);
|
||||
int type = val ? JSON_VALUE_TRUE : JSON_VALUE_FALSE;
|
||||
json_array_insert_before(node_, arr, type);
|
||||
json_value_t* remove_val = json_array_remove(node_, arr);
|
||||
json_value_destroy(remove_val);
|
||||
}
|
||||
|
||||
void Json::update_arr(const std::string& val)
|
||||
{
|
||||
update_arr(val.c_str());
|
||||
}
|
||||
|
||||
void Json::update_arr(const char* val)
|
||||
{
|
||||
json_array_t* arr = json_value_array(parent_);
|
||||
json_array_insert_before(node_, arr, JSON_VALUE_STRING, val);
|
||||
json_value_t* remove_val = json_array_remove(node_, arr);
|
||||
json_value_destroy(remove_val);
|
||||
}
|
||||
|
||||
void Json::update_arr(std::nullptr_t val)
|
||||
{
|
||||
json_array_t* arr = json_value_array(parent_);
|
||||
json_array_insert_before(node_, arr, JSON_VALUE_NULL);
|
||||
json_value_t* remove_val = json_array_remove(node_, arr);
|
||||
json_value_destroy(remove_val);
|
||||
}
|
||||
|
||||
void Json::update_arr(const Json& 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.node_ = nullptr;
|
||||
json_value_t* remove_val = json_array_remove(node_, arr);
|
||||
json_value_destroy(remove_val);
|
||||
}
|
||||
|
||||
std::string Json::type_str() const
|
||||
{
|
||||
switch (type())
|
||||
{
|
||||
case JSON_VALUE_STRING:
|
||||
return "string";
|
||||
case JSON_VALUE_NUMBER:
|
||||
return "number";
|
||||
case JSON_VALUE_OBJECT:
|
||||
return "object";
|
||||
case JSON_VALUE_ARRAY:
|
||||
return "array";
|
||||
case JSON_VALUE_TRUE:
|
||||
return "true";
|
||||
case JSON_VALUE_FALSE:
|
||||
return "false";
|
||||
case JSON_VALUE_NULL:
|
||||
return "null";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
int Json::size() const
|
||||
{
|
||||
if (type() == JSON_VALUE_ARRAY)
|
||||
{
|
||||
json_array_t* array = json_value_array(node_);
|
||||
return json_array_size(array);
|
||||
}
|
||||
else if (type() == JSON_VALUE_OBJECT)
|
||||
{
|
||||
json_object_t* obj = json_value_object(node_);
|
||||
return json_object_size(obj);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool Json::empty() const
|
||||
{
|
||||
switch (type())
|
||||
{
|
||||
case JSON_VALUE_NULL:
|
||||
{
|
||||
// null values are empty
|
||||
return true;
|
||||
}
|
||||
case JSON_VALUE_ARRAY:
|
||||
case JSON_VALUE_OBJECT:
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
default:
|
||||
// all other types are nonempty
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Json::clear()
|
||||
{
|
||||
if (allocated_)
|
||||
{
|
||||
destroy_node(node_);
|
||||
node_ = json_value_create(JSON_VALUE_OBJECT);
|
||||
}
|
||||
}
|
||||
|
||||
void Json::to_object()
|
||||
{
|
||||
if (allocated_ && is_null())
|
||||
{
|
||||
destroy_node(node_);
|
||||
node_ = json_value_create(JSON_VALUE_OBJECT);
|
||||
}
|
||||
}
|
||||
|
||||
void Json::to_array()
|
||||
{
|
||||
if (allocated_ && is_null())
|
||||
{
|
||||
destroy_node(node_);
|
||||
node_ = json_value_create(JSON_VALUE_ARRAY);
|
||||
}
|
||||
}
|
||||
|
||||
void Json::value_convert(const json_value_t* val, int spaces, int depth,
|
||||
std::string* out_str)
|
||||
{
|
||||
if (val == nullptr || out_str == nullptr)
|
||||
return;
|
||||
switch (json_value_type(val))
|
||||
{
|
||||
case JSON_VALUE_STRING:
|
||||
string_convert(json_value_string(val), out_str);
|
||||
break;
|
||||
case JSON_VALUE_NUMBER:
|
||||
number_convert(json_value_number(val), out_str);
|
||||
break;
|
||||
case JSON_VALUE_OBJECT:
|
||||
object_convert(json_value_object(val), spaces, depth, out_str);
|
||||
break;
|
||||
case JSON_VALUE_ARRAY:
|
||||
array_convert(json_value_array(val), spaces, depth, out_str);
|
||||
break;
|
||||
case JSON_VALUE_TRUE:
|
||||
out_str->append("true");
|
||||
break;
|
||||
case JSON_VALUE_FALSE:
|
||||
out_str->append("false");
|
||||
break;
|
||||
case JSON_VALUE_NULL:
|
||||
out_str->append("null");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Json::string_convert(const char* str, std::string* out_str)
|
||||
{
|
||||
out_str->append("\"");
|
||||
while (*str)
|
||||
{
|
||||
switch (*str)
|
||||
{
|
||||
case '\r':
|
||||
out_str->append("\\r");
|
||||
break;
|
||||
case '\n':
|
||||
out_str->append("\\n");
|
||||
break;
|
||||
case '\f':
|
||||
out_str->append("\\f");
|
||||
break;
|
||||
case '\b':
|
||||
out_str->append("\\b");
|
||||
break;
|
||||
case '\"':
|
||||
out_str->append("\\\"");
|
||||
break;
|
||||
case '\t':
|
||||
out_str->append("\\t");
|
||||
break;
|
||||
case '\\':
|
||||
out_str->append("\\\\");
|
||||
break;
|
||||
default:
|
||||
out_str->push_back(*str);
|
||||
break;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
out_str->append("\"");
|
||||
}
|
||||
|
||||
void Json::number_convert(double number, std::string* out_str)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
long long integer = number;
|
||||
if (integer == number)
|
||||
oss << integer;
|
||||
else
|
||||
oss << number;
|
||||
|
||||
out_str->append(oss.str());
|
||||
}
|
||||
|
||||
void Json::array_convert_not_format(const json_array_t* arr,
|
||||
std::string* out_str)
|
||||
{
|
||||
const json_value_t* val;
|
||||
int n = 0;
|
||||
|
||||
out_str->append("[");
|
||||
json_array_for_each(val, arr)
|
||||
{
|
||||
if (n != 0)
|
||||
{
|
||||
out_str->append(",");
|
||||
}
|
||||
n++;
|
||||
value_convert(val, 0, 0, out_str);
|
||||
}
|
||||
out_str->append("]");
|
||||
}
|
||||
|
||||
void Json::array_convert(const json_array_t* arr, int spaces, int depth,
|
||||
std::string* out_str)
|
||||
{
|
||||
if (spaces == 0)
|
||||
{
|
||||
return array_convert_not_format(arr, out_str);
|
||||
}
|
||||
const json_value_t* val;
|
||||
int n = 0;
|
||||
int i;
|
||||
std::string padding(spaces, ' ');
|
||||
out_str->append("[\n");
|
||||
json_array_for_each(val, arr)
|
||||
{
|
||||
if (n != 0)
|
||||
{
|
||||
out_str->append(",\n");
|
||||
}
|
||||
n++;
|
||||
for (i = 0; i < depth + 1; i++)
|
||||
{
|
||||
out_str->append(padding);
|
||||
}
|
||||
value_convert(val, spaces, depth + 1, out_str);
|
||||
}
|
||||
|
||||
out_str->append("\n");
|
||||
for (i = 0; i < depth; i++)
|
||||
{
|
||||
out_str->append(padding);
|
||||
}
|
||||
out_str->append("]");
|
||||
}
|
||||
|
||||
void Json::object_convert_not_format(const json_object_t* obj,
|
||||
std::string* out_str)
|
||||
{
|
||||
const char* name;
|
||||
const json_value_t* val;
|
||||
int n = 0;
|
||||
|
||||
out_str->append("{");
|
||||
json_object_for_each(name, val, obj)
|
||||
{
|
||||
if (n != 0)
|
||||
{
|
||||
out_str->append(",");
|
||||
}
|
||||
n++;
|
||||
out_str->append("\"");
|
||||
out_str->append(name);
|
||||
out_str->append("\":");
|
||||
value_convert(val, 0, 0, out_str);
|
||||
}
|
||||
out_str->append("}");
|
||||
}
|
||||
|
||||
void Json::object_convert(const json_object_t* obj, int spaces, int depth,
|
||||
std::string* out_str)
|
||||
{
|
||||
if (spaces == 0)
|
||||
{
|
||||
return object_convert_not_format(obj, out_str);
|
||||
}
|
||||
const char* name;
|
||||
const json_value_t* val;
|
||||
int n = 0;
|
||||
int i;
|
||||
std::string padding(spaces, ' ');
|
||||
out_str->append("{\n");
|
||||
json_object_for_each(name, val, obj)
|
||||
{
|
||||
if (n != 0)
|
||||
{
|
||||
out_str->append(",\n");
|
||||
}
|
||||
n++;
|
||||
for (i = 0; i < depth + 1; i++)
|
||||
{
|
||||
out_str->append(padding);
|
||||
}
|
||||
out_str->append("\"");
|
||||
out_str->append(name);
|
||||
out_str->append("\": ");
|
||||
value_convert(val, spaces, depth + 1, out_str);
|
||||
}
|
||||
|
||||
out_str->append("\n");
|
||||
for (i = 0; i < depth; i++)
|
||||
{
|
||||
out_str->append(padding);
|
||||
}
|
||||
out_str->append("}");
|
||||
}
|
||||
|
||||
} // namespace wfrest
|
877
tools/templates/config/Json.h
Normal file
877
tools/templates/config/Json.h
Normal file
@ -0,0 +1,877 @@
|
||||
#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>
|
||||
|
||||
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 (!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);
|
||||
|
||||
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 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_ = 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);
|
||||
|
||||
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 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);
|
||||
|
||||
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 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 (!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);
|
||||
|
||||
void push_back(bool val);
|
||||
void push_back(const std::string& val);
|
||||
void push_back(const char* val);
|
||||
void push_back(std::nullptr_t 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);
|
||||
|
||||
void update_arr(bool val);
|
||||
void update_arr(const std::string& val);
|
||||
void update_arr(const char* val);
|
||||
void update_arr(std::nullptr_t 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)
|
||||
{
|
||||
}
|
||||
|
||||
~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_,
|
||||
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_ = 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_ = 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_ = 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_ = 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;
|
||||
}
|
||||
|
||||
bool is_incomplete() const
|
||||
{
|
||||
return node_ == nullptr;
|
||||
}
|
||||
|
||||
void destroy_node(const json_value_t* node)
|
||||
{
|
||||
if (node != nullptr)
|
||||
{
|
||||
json_value_destroy(const_cast<json_value_t*>(node));
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
public:
|
||||
// Constructors for the various types of JSON value.
|
||||
Json();
|
||||
Json(const std::string& str, bool parse_flag);
|
||||
Json(const std::string& str);
|
||||
Json(const char* str);
|
||||
Json(std::nullptr_t null);
|
||||
Json(double val);
|
||||
Json(int val);
|
||||
Json(bool val);
|
||||
Json(const Array& val);
|
||||
Json(const Object& val);
|
||||
Json(Array&& val);
|
||||
Json(Object&& val);
|
||||
|
||||
~Json();
|
||||
|
||||
Json(const Json& json) = delete;
|
||||
Json& operator=(const Json& json) = delete;
|
||||
Json(Json&& other);
|
||||
Json& operator=(Json&& other);
|
||||
|
||||
protected:
|
||||
// todo : need remove, default null type
|
||||
struct Empty
|
||||
{
|
||||
};
|
||||
// watcher
|
||||
Json(const json_value_t* parent, std::string&& key);
|
||||
Json(const json_value_t* parent, const std::string& key);
|
||||
Json(const json_value_t* parent);
|
||||
Json(const json_value_t* node, const json_value_t* parent);
|
||||
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);
|
||||
|
||||
Json(const Empty&);
|
||||
|
||||
bool is_root() const
|
||||
{
|
||||
return parent_ == nullptr;
|
||||
}
|
||||
void to_object();
|
||||
|
||||
void to_array();
|
||||
|
||||
void reset()
|
||||
{
|
||||
node_ = nullptr;
|
||||
parent_ = nullptr;
|
||||
parent_key_.clear();
|
||||
}
|
||||
|
||||
void reset(const json_value_t* node, const json_value_t* parent,
|
||||
const std::string& parent_key)
|
||||
{
|
||||
node_ = node;
|
||||
parent_ = parent;
|
||||
parent_key_ = parent_key;
|
||||
}
|
||||
|
||||
void reset(const json_value_t* node, const json_value_t* parent,
|
||||
std::string&& parent_key)
|
||||
{
|
||||
node_ = node;
|
||||
parent_ = parent;
|
||||
parent_key_ = std::move(parent_key);
|
||||
}
|
||||
|
||||
private:
|
||||
const json_value_t* node_ = nullptr;
|
||||
const json_value_t* parent_ = nullptr;
|
||||
std::string parent_key_;
|
||||
bool allocated_ = true;
|
||||
};
|
||||
|
||||
class Object_S : public Json
|
||||
{
|
||||
public:
|
||||
Object_S()
|
||||
{
|
||||
to_object();
|
||||
}
|
||||
Object_S(const json_value_t* node, const json_value_t* parent)
|
||||
: Json(node, parent)
|
||||
{
|
||||
}
|
||||
using string_type = typename 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()
|
||||
{
|
||||
to_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_);
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_same<T, Json::Object>::value ||
|
||||
std::is_same<T, Json::Array>::value,
|
||||
bool>::type>
|
||||
void Json::push_back(const std::string& key, const T& val)
|
||||
{
|
||||
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.node_ = nullptr;
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_same<T, Json::Object>::value ||
|
||||
std::is_same<T, Json::Array>::value,
|
||||
bool>::type>
|
||||
void Json::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_ = json_object_append(obj, key.c_str(), 0, copy_json.node_);
|
||||
copy_json.node_ = nullptr;
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_same<T, Json::Object>::value ||
|
||||
std::is_same<T, Json::Array>::value,
|
||||
bool>::type>
|
||||
void Json::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.node_ = nullptr;
|
||||
return;
|
||||
}
|
||||
json_object_insert_before(find, obj, key.c_str(), 0, copy_json.node_);
|
||||
copy_json.node_ = nullptr;
|
||||
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, Json::Object>::value ||
|
||||
::std::is_same<T, Json::Array>::value,
|
||||
bool>::type>
|
||||
void Json::push_back(const T& val)
|
||||
{
|
||||
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.node_ = nullptr;
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<std::is_same<T, Json::Object>::value ||
|
||||
::std::is_same<T, Json::Array>::value,
|
||||
bool>::type>
|
||||
void Json::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.node_ = nullptr;
|
||||
json_value_t* remove_val = json_array_remove(node_, arr);
|
||||
json_value_destroy(remove_val);
|
||||
}
|
||||
|
||||
} // namespace wfrest
|
||||
|
||||
#endif // WFREST_JSON_H_
|
522
tools/templates/config/config_full.cc
Normal file
522
tools/templates/config/config_full.cc
Normal file
@ -0,0 +1,522 @@
|
||||
#include <fstream>
|
||||
#include "config.h"
|
||||
#include "workflow/WFGlobal.h"
|
||||
#include "workflow/UpstreamManager.h"
|
||||
#include "workflow/UpstreamPolicies.h"
|
||||
#include "srpc/rpc_filter_metrics.h"
|
||||
#include "srpc/rpc_filter_span.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(), ¶m.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], ¶ms[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void RPCConfig::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 RPCConfig::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>();
|
||||
}
|
||||
|
||||
bool RPCConfig::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
|
||||
printf("[INFO][RPCConfig::load] Unknown key: %s\n", it.key().c_str());
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
void RPCConfig::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 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(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][RPCConfig::load_metrics] Unknown metrics: %s\n",
|
||||
filter_name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RPCConfig::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 RPCSpanDefault(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 RPCSpanOpenTelemetry(url,
|
||||
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][RPCConfig::load_metrics] Unknown metrics: %s\n",
|
||||
filter_name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RPCConfig::load_filter(SRPCServer& server)
|
||||
{
|
||||
for (auto *filter : this->filters)
|
||||
server.add_filter(filter);
|
||||
}
|
||||
|
||||
void RPCConfig::load_filter(SRPCClient& client)
|
||||
{
|
||||
for (auto *filter : this->filters)
|
||||
client.add_filter(filter);
|
||||
}
|
||||
|
||||
void RPCConfig::load_filter(SRPCHttpServer& server)
|
||||
{
|
||||
for (auto *filter : this->filters)
|
||||
server.add_filter(filter);
|
||||
}
|
||||
|
||||
void RPCConfig::load_filter(SRPCHttpClient& client)
|
||||
{
|
||||
for (auto *filter : this->filters)
|
||||
client.add_filter(filter);
|
||||
}
|
||||
|
||||
void RPCConfig::load_filter(BRPCServer& server)
|
||||
{
|
||||
for (auto *filter : this->filters)
|
||||
server.add_filter(filter);
|
||||
}
|
||||
|
||||
void RPCConfig::load_filter(BRPCClient& client)
|
||||
{
|
||||
for (auto *filter : this->filters)
|
||||
client.add_filter(filter);
|
||||
}
|
||||
|
||||
void RPCConfig::load_filter(ThriftServer& server)
|
||||
{
|
||||
for (auto *filter : this->filters)
|
||||
server.add_filter(filter);
|
||||
}
|
||||
|
||||
void RPCConfig::load_filter(ThriftClient& client)
|
||||
{
|
||||
for (auto *filter : this->filters)
|
||||
client.add_filter(filter);
|
||||
}
|
||||
|
||||
void RPCConfig::load_filter(ThriftHttpServer& server)
|
||||
{
|
||||
for (auto *filter : this->filters)
|
||||
server.add_filter(filter);
|
||||
}
|
||||
|
||||
void RPCConfig::load_filter(ThriftHttpClient& client)
|
||||
{
|
||||
for (auto *filter : this->filters)
|
||||
client.add_filter(filter);
|
||||
}
|
||||
|
||||
void RPCConfig::load_filter(TRPCServer& server)
|
||||
{
|
||||
for (auto *filter : this->filters)
|
||||
server.add_filter(filter);
|
||||
}
|
||||
|
||||
void RPCConfig::load_filter(TRPCClient& client)
|
||||
{
|
||||
for (auto *filter : this->filters)
|
||||
client.add_filter(filter);
|
||||
}
|
||||
|
||||
void RPCConfig::load_filter(TRPCHttpServer& server)
|
||||
{
|
||||
for (auto *filter : this->filters)
|
||||
server.add_filter(filter);
|
||||
}
|
||||
|
||||
void RPCConfig::load_filter(TRPCHttpClient& client)
|
||||
{
|
||||
for (auto *filter : this->filters)
|
||||
client.add_filter(filter);
|
||||
}
|
||||
|
||||
RPCConfig::~RPCConfig()
|
||||
{
|
||||
for (size_t i = 0; i < this->filters.size(); i++)
|
||||
delete this->filters[i];
|
||||
}
|
||||
|
90
tools/templates/config/config_full.h
Normal file
90
tools/templates/config/config_full.h
Normal file
@ -0,0 +1,90 @@
|
||||
#ifndef _RPC_CONFIG_H_
|
||||
#define _RPC_CONFIG_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include "Json.h"
|
||||
#include "srpc/rpc_types.h"
|
||||
#include "srpc/rpc_define.h"
|
||||
#include "srpc/rpc_filter.h"
|
||||
|
||||
namespace srpc
|
||||
{
|
||||
|
||||
class RPCConfig
|
||||
{
|
||||
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(); }
|
||||
unsigned short client_port() const { return this->c_port; }
|
||||
const char *client_host() const { return this->c_host.c_str(); }
|
||||
bool client_is_ssl() const { return this->c_is_ssl; }
|
||||
const char *client_url() const { return this->c_url.c_str(); }
|
||||
int redirect_max() const { return this->c_redirect_max; }
|
||||
int retry_max() const { return this->c_retry_max; }
|
||||
const char *client_caller() const { return this->c_caller.c_str(); }
|
||||
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; }
|
||||
|
||||
public:
|
||||
RPCConfig() :
|
||||
s_port(0), c_port(0), c_is_ssl(false), c_redirect_max(0), c_retry_max(0)
|
||||
{ }
|
||||
~RPCConfig();
|
||||
|
||||
private:
|
||||
void load_server();
|
||||
void load_client();
|
||||
void load_metrics();
|
||||
void load_trace();
|
||||
|
||||
wfrest::Json data;
|
||||
std::vector<RPCFilter *> filters;
|
||||
unsigned short s_port;
|
||||
std::string s_cert_file;
|
||||
std::string s_file_key;
|
||||
std::string c_host;
|
||||
unsigned short c_port;
|
||||
bool c_is_ssl;
|
||||
std::string c_url;
|
||||
int c_redirect_max;
|
||||
int c_retry_max;
|
||||
std::string c_caller;
|
||||
std::string c_user_name;
|
||||
std::string c_password;
|
||||
std::string root_path;
|
||||
ErrorPageMap error_page;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
297
tools/templates/config/config_simple.cc
Normal file
297
tools/templates/config/config_simple.cc
Normal file
@ -0,0 +1,297 @@
|
||||
#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(), ¶m.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], ¶ms[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void RPCConfig::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 RPCConfig::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>();
|
||||
}
|
||||
|
||||
bool RPCConfig::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
|
||||
printf("[INFO][RPCConfig::load] Unknown key: %s\n", it.key().c_str());
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
RPCConfig::~RPCConfig()
|
||||
{
|
||||
}
|
||||
|
55
tools/templates/config/config_simple.h
Normal file
55
tools/templates/config/config_simple.h
Normal file
@ -0,0 +1,55 @@
|
||||
#ifndef _RPC_CONFIG_H_
|
||||
#define _RPC_CONFIG_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include "Json.h"
|
||||
|
||||
namespace srpc
|
||||
{
|
||||
|
||||
class RPCConfig
|
||||
{
|
||||
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; }
|
||||
|
||||
public:
|
||||
RPCConfig() : s_port(0), c_port(0), c_redirect_max(0), c_retry_max(0) { }
|
||||
~RPCConfig();
|
||||
|
||||
private:
|
||||
void load_server();
|
||||
void load_client();
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
1
tools/templates/file/404.html
Normal file
1
tools/templates/file/404.html
Normal file
@ -0,0 +1 @@
|
||||
<html>This is not the web page you are looking for.</html>
|
21
tools/templates/file/50x.html
Normal file
21
tools/templates/file/50x.html
Normal file
@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Error</title>
|
||||
<style>
|
||||
body {
|
||||
width: 35em;
|
||||
margin: 0 auto;
|
||||
font-family: Tahoma, Verdana, Arial, sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>An error occurred.</h1>
|
||||
<p>Sorry, the page you are looking for is currently unavailable.<br/>
|
||||
Please try again later.</p>
|
||||
<p>If you are the system administrator of this resource then you should check
|
||||
the <a href="https://github.com/sogou/srpc/tools">srpc-ctl README.md</a> for details.</p>
|
||||
<p><em>Faithfully yours, srpc.</em></p>
|
||||
</body>
|
||||
</html>
|
102
tools/templates/file/file_service.cc
Normal file
102
tools/templates/file/file_service.cc
Normal file
@ -0,0 +1,102 @@
|
||||
#include "file_service.h"
|
||||
|
||||
SubTask *FileService::create_error_task(int code,
|
||||
struct FileService::ModuleCtx *ctx)
|
||||
{
|
||||
if (ctx->is_error_set == false)
|
||||
{
|
||||
ctx->resp->set_status_code(std::to_string(code));
|
||||
ctx->is_error_set = true;
|
||||
const auto it = this->error_page.find(code);
|
||||
if (it != this->error_page.end())
|
||||
return this->create_file_task(it->second, ctx);
|
||||
}
|
||||
|
||||
return WFTaskFactory::create_empty_task();
|
||||
}
|
||||
|
||||
void FileService::pread_callback(WFFileIOTask *task)
|
||||
{
|
||||
FileIOArgs *args = task->get_args();
|
||||
long ret = task->get_retval();
|
||||
struct ModuleCtx *ctx = (struct ModuleCtx *)series_of(task)->get_context();
|
||||
protocol::HttpResponse *resp = ctx->resp;
|
||||
|
||||
if (task->get_state() == WFT_STATE_SUCCESS)
|
||||
{
|
||||
resp->append_output_body_nocopy(args->buf, ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto *error_task = this->create_error_task(503, ctx);
|
||||
series_of(task)->push_back(error_task);
|
||||
}
|
||||
}
|
||||
|
||||
SubTask *FileService::create_file_task(const std::string& path,
|
||||
struct FileService::ModuleCtx *ctx)
|
||||
{
|
||||
SubTask *task;
|
||||
struct stat st;
|
||||
std::string abs_path = this->root + path;
|
||||
if (abs_path.back() == '/')
|
||||
abs_path += "index.html";
|
||||
|
||||
if (stat(abs_path.c_str(), &st) >= 0)
|
||||
{
|
||||
size_t size = st.st_size;
|
||||
void *buf = malloc(size);
|
||||
if (buf)
|
||||
{
|
||||
ctx->buf = buf;
|
||||
auto&& cb = std::bind(&FileService::pread_callback,
|
||||
this, std::placeholders::_1);
|
||||
task = WFTaskFactory::create_pread_task(abs_path, buf, size, 0, cb);
|
||||
}
|
||||
else
|
||||
{
|
||||
task = this->create_error_task(503, ctx);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
task = this->create_error_task(404, ctx);
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
WFModuleTask *FileService::create_module(WFHttpTask *server_task,
|
||||
const std::string& path)
|
||||
{
|
||||
fprintf(stderr, "file service get request: %s\n", path.c_str());
|
||||
struct ModuleCtx *ctx = new ModuleCtx(server_task->get_resp());
|
||||
SubTask *next = this->create_file_task(path, ctx);
|
||||
WFModuleTask *module = WFTaskFactory::create_module_task(next,
|
||||
[server_task](const WFModuleTask *mod)
|
||||
{
|
||||
struct ModuleCtx *ctx;
|
||||
ctx = (struct ModuleCtx *)mod->sub_series()->get_context();
|
||||
void *buf = ctx->buf;
|
||||
server_task->set_callback([buf](WFHttpTask *t){ free(buf); });
|
||||
delete ctx;
|
||||
});
|
||||
|
||||
module->sub_series()->set_context(ctx);
|
||||
return module;
|
||||
}
|
||||
|
||||
void FileService::process(WFHttpTask *server_task)
|
||||
{
|
||||
protocol::HttpRequest *req = server_task->get_req();
|
||||
protocol::HttpResponse *resp = server_task->get_resp();
|
||||
std::string path = req->get_request_uri();
|
||||
auto pos = path.find_first_of('?');
|
||||
if (pos != std::string::npos)
|
||||
path = path.substr(0, pos);
|
||||
|
||||
resp->add_header_pair("Server", "SRPC HTTP File Server");
|
||||
|
||||
WFModuleTask *module = this->create_module(server_task, path);
|
||||
series_of(server_task)->push_back(module);
|
||||
}
|
58
tools/templates/file/file_service.h
Normal file
58
tools/templates/file/file_service.h
Normal file
@ -0,0 +1,58 @@
|
||||
#ifndef _RPC_FILE_SERVICE_H_
|
||||
#define _RPC_FILE_SERVICE_H_
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include "workflow/Workflow.h"
|
||||
#include "workflow/HttpMessage.h"
|
||||
#include "workflow/HttpUtil.h"
|
||||
#include "workflow/WFHttpServer.h"
|
||||
#include "workflow/WFFacilities.h"
|
||||
|
||||
// This is a simple exmaple for file service
|
||||
|
||||
class FileService
|
||||
{
|
||||
public:
|
||||
using ErrorPageMap = std::unordered_map<int, std::string>;
|
||||
|
||||
void process(WFHttpTask *server_task);
|
||||
void pread_callback(WFFileIOTask *task);
|
||||
protected:
|
||||
struct ModuleCtx
|
||||
{
|
||||
protocol::HttpResponse *resp;
|
||||
void *buf;
|
||||
bool is_error_set;
|
||||
ModuleCtx(protocol::HttpResponse * resp) :
|
||||
resp(resp),
|
||||
buf(NULL),
|
||||
is_error_set(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
WFModuleTask *create_module(WFHttpTask *task, const std::string& path);
|
||||
SubTask *create_file_task(const std::string& path, struct ModuleCtx *ctx);
|
||||
SubTask *create_error_task(int code, struct ModuleCtx *ctx);
|
||||
|
||||
public:
|
||||
FileService(std::string root, const ErrorPageMap& error_page) :
|
||||
root(std::move(root)),
|
||||
error_page(error_page)
|
||||
{
|
||||
if (this->root.empty())
|
||||
root = "./";
|
||||
else if (this->root.at(this->root.length() - 1) != '/')
|
||||
root += "/";
|
||||
}
|
||||
|
||||
private:
|
||||
std::string root;
|
||||
const ErrorPageMap& error_page;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
1
tools/templates/file/index.html
Normal file
1
tools/templates/file/index.html
Normal file
@ -0,0 +1 @@
|
||||
<html>Hello from workflow and srpc file server!</html>
|
18
tools/templates/file/server.conf
Normal file
18
tools/templates/file/server.conf
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"server":
|
||||
{
|
||||
"port": 8080,
|
||||
"root": "./html/",
|
||||
"error_page" : [
|
||||
{
|
||||
"error" : [ 404 ],
|
||||
"page" : "404.html"
|
||||
},
|
||||
{
|
||||
"error" : [ 500, 502, 503, 504],
|
||||
"page" : "50x.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
63
tools/templates/file/server_main.cc
Normal file
63
tools/templates/file/server_main.cc
Normal file
@ -0,0 +1,63 @@
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include "workflow/HttpMessage.h"
|
||||
#include "workflow/HttpUtil.h"
|
||||
#include "workflow/WFHttpServer.h"
|
||||
#include "workflow/WFFacilities.h"
|
||||
|
||||
#include "config/config.h"
|
||||
#include "file_service.h"
|
||||
|
||||
using namespace protocol;
|
||||
|
||||
static WFFacilities::WaitGroup wait_group(1);
|
||||
static srpc::RPCConfig config;
|
||||
|
||||
void sig_handler(int signo)
|
||||
{
|
||||
wait_group.done();
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
if (config.load("./server.conf") == false)
|
||||
{
|
||||
perror("Load config failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
init();
|
||||
|
||||
unsigned short port = config.server_port();
|
||||
const char *cert_file = config.server_cert_file();
|
||||
const char *file_key = config.server_file_key();
|
||||
|
||||
FileService service(config.get_root_path(), config.get_error_page());
|
||||
auto&& proc = std::bind(&FileService::process, &service,
|
||||
std::placeholders::_1);
|
||||
|
||||
int ret;
|
||||
WFHttpServer server(proc);
|
||||
|
||||
if (strlen(cert_file) != 0 && strlen(file_key) != 0)
|
||||
ret = server.start(port, cert_file, file_key);
|
||||
else
|
||||
ret = server.start(port);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
fprintf(stderr, "http file service start, port %u\n", port);
|
||||
wait_group.wait();
|
||||
server.stop();
|
||||
}
|
||||
else
|
||||
perror("http file service start");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
12
tools/templates/proxy/client_rpc.conf
Normal file
12
tools/templates/proxy/client_rpc.conf
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"client":
|
||||
{
|
||||
"remote_host": "127.0.0.1",
|
||||
"remote_port": 1411,
|
||||
"is_ssl" : false,
|
||||
"redirect_max": 2,
|
||||
"retry_max": 1,
|
||||
"callee" : "rpc_client"
|
||||
}
|
||||
}
|
||||
|
14
tools/templates/proxy/proxy.conf
Normal file
14
tools/templates/proxy/proxy.conf
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"server":
|
||||
{
|
||||
"port": %s
|
||||
},
|
||||
|
||||
"client":
|
||||
{
|
||||
"remote_host": "127.0.0.1",
|
||||
"remote_port": %s,
|
||||
"retry_max": 2
|
||||
}
|
||||
}
|
||||
|
81
tools/templates/proxy/proxy_main.cc
Normal file
81
tools/templates/proxy/proxy_main.cc
Normal file
@ -0,0 +1,81 @@
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include "workflow/WFTaskFactory.h"
|
||||
#include "workflow/WF%sServer.h"
|
||||
#include "workflow/WFFacilities.h"
|
||||
|
||||
#include "config/config.h"
|
||||
#include "config/util.h"
|
||||
|
||||
static WFFacilities::WaitGroup wait_group(1);
|
||||
static srpc::RPCConfig config;
|
||||
|
||||
void sig_handler(int signo)
|
||||
{
|
||||
wait_group.done();
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
if (config.load("./proxy.conf") == false)
|
||||
{
|
||||
perror("Load config failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
signal(SIGTERM, sig_handler);
|
||||
}
|
||||
|
||||
void callback(WF%sTask *client_task)
|
||||
{
|
||||
int state = client_task->get_state();
|
||||
int error = client_task->get_error();
|
||||
SeriesWork *series = series_of(client_task);
|
||||
protocol::%sResponse *resp = client_task->get_resp();
|
||||
protocol::%sResponse *proxy_resp = (protocol::%sResponse *)series->get_context();
|
||||
|
||||
// Copy the remote server's response, to proxy response.
|
||||
if (state == WFT_STATE_SUCCESS)%s
|
||||
fprintf(stderr, "backend server state = %%d error = %%d. response client.\n",
|
||||
state, error);
|
||||
}
|
||||
|
||||
void process(WF%sTask *server_task)
|
||||
{
|
||||
protocol::%sRequest *req = server_task->get_req();
|
||||
std::string backend_server = config.client_host();
|
||||
unsigned short backend_server_port = config.client_port();
|
||||
std::string url = std::string("%s://") + backend_server +
|
||||
std::string(":") + std::to_string(backend_server_port);
|
||||
|
||||
WF%sTask *client_task = WFTaskFactory::create_%s_task(url,%s
|
||||
config.retry_max(),
|
||||
callback);
|
||||
|
||||
// Copy user's request to the new task's request using std::move()
|
||||
%s
|
||||
SeriesWork *series = series_of(server_task);
|
||||
series->set_context(server_task->get_resp());
|
||||
series->push_back(client_task);
|
||||
|
||||
fprintf(stderr, "proxy get request from client: ");
|
||||
print_peer_address<WF%sTask>(server_task);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
init();
|
||||
|
||||
WF%sServer proxy_server(process);
|
||||
|
||||
if (proxy_server.start(config.server_port()) == 0)
|
||||
{
|
||||
fprintf(stderr, "[%s]-[%s] proxy start, port %%u\n", config.server_port());
|
||||
wait_group.wait();
|
||||
proxy_server.stop();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
91
tools/templates/proxy/proxy_main_proto.cc
Normal file
91
tools/templates/proxy/proxy_main_proto.cc
Normal file
@ -0,0 +1,91 @@
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "workflow/WFFacilities.h"
|
||||
#include "srpc/rpc_types.h"
|
||||
|
||||
#include "config/config.h"
|
||||
#include "%s.srpc.h"
|
||||
|
||||
using namespace srpc;
|
||||
|
||||
static srpc::RPCConfig config;
|
||||
static WFFacilities::WaitGroup wait_group(1);
|
||||
|
||||
static void sig_handler(int signo)
|
||||
{
|
||||
wait_group.done();
|
||||
}
|
||||
|
||||
static void init()
|
||||
{
|
||||
if (config.load("./proxy.conf") == false)
|
||||
{
|
||||
perror("Load config failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
signal(SIGTERM, sig_handler);
|
||||
}
|
||||
|
||||
class ProxyServiceImpl : public %s::Service
|
||||
{
|
||||
public:
|
||||
void Echo(EchoRequest *request, EchoResponse *response,
|
||||
RPCContext *context) override
|
||||
{
|
||||
fprintf(stderr, "%s proxy get request from client. ip : %%s\n%%s\n",
|
||||
context->get_remote_ip().c_str(), request->DebugString().c_str());
|
||||
|
||||
// 5. process() : get request from client and send to remote server
|
||||
auto *task = this->client.create_Echo_task([response](EchoResponse *resp,
|
||||
RPCContext *ctx) {
|
||||
// 6. callback() : fill remote server response to client
|
||||
if (ctx->success())
|
||||
*response = std::move(*resp);
|
||||
});
|
||||
task->serialize_input(request);
|
||||
context->get_series()->push_back(task);
|
||||
}
|
||||
|
||||
public:
|
||||
ProxyServiceImpl(RPCClientParams *params) :
|
||||
client(params)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
%s::%sClient client;
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
// 1. load config
|
||||
init();
|
||||
|
||||
// 2. make client for remote server
|
||||
RPCClientParams client_params = RPC_CLIENT_PARAMS_DEFAULT;
|
||||
client_params.host = config.client_host();
|
||||
client_params.port = config.client_port();
|
||||
|
||||
// 3. start proxy server
|
||||
%sServer server;
|
||||
ProxyServiceImpl impl(&client_params);
|
||||
server.add_service(&impl);
|
||||
config.load_filter(server);
|
||||
|
||||
if (server.start(config.server_port()) == 0)
|
||||
{
|
||||
// 4. main thread success and wait
|
||||
fprintf(stderr, "%s [%s]-[%s] proxy start, port %%u\n",
|
||||
config.server_port());
|
||||
wait_group.wait();
|
||||
server.stop();
|
||||
}
|
||||
else
|
||||
perror("server start");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
87
tools/templates/rpc/CMakeLists.txt
Normal file
87
tools/templates/rpc/CMakeLists.txt
Normal file
@ -0,0 +1,87 @@
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
project(%s LANGUAGES CXX)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_BUILD_TYPE RelWithDebInfo)
|
||||
|
||||
set(Protobuf_ERROR_MSG "ERROR: Failed to find protobuf. Some suggestions for installation.")
|
||||
set(Protobuf_ERROR_MSG_LINUX "${Protobuf_ERROR_MSG}
|
||||
For Debian / Ubuntu ...:
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libprotobuf-dev protobuf-compiler
|
||||
For RedHat / Fedora / CentOS ...:
|
||||
sudo yum makecache
|
||||
sudo yum install -y protobuf-devel protobuf-compiler")
|
||||
|
||||
set(Protobuf_ERROR_MSG_MACOS "${Protobuf_ERROR_MSG}
|
||||
For MacOS :
|
||||
sudo brew update
|
||||
sudo brew install protobuf protobuf-c")
|
||||
|
||||
# Find all the dependencies
|
||||
find_package(OpenSSL REQUIRED)
|
||||
|
||||
set(Workflow_DIR "%s")
|
||||
find_package(Workflow REQUIRED CONFIG HINTS ${Workflow_DIR})
|
||||
|
||||
set(Srpc_DIR "%s")
|
||||
find_package(srpc REQUIRED CONFIG HINTS ${Srpc_DIR})
|
||||
|
||||
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)
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR})
|
||||
|
||||
# Generate idl code: xx.srpc.h xx.pb.h xx.pb.cc xx.thrift.h
|
||||
set(IDL_FILE %s)
|
||||
set(SRPC_GEN_PROGRAM ${SRPC_BIN_DIR}/srpc_generator) %s
|
||||
|
||||
add_custom_target(SRPC_GEN ALL
|
||||
COMMAND ${SRPC_GEN_PROGRAM} ${PROJECT_SOURCE_DIR}/${IDL_FILE} ${PROJECT_SOURCE_DIR} -s
|
||||
COMMENT "sprc generator..."
|
||||
)
|
||||
|
||||
# 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)
|
||||
|
||||
# Set all the libraries here
|
||||
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 config/config.cc config/Json.cc ${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})
|
||||
|
||||
# Build executable outputs
|
||||
set(PROJECT_OUTPUT server client%s)
|
||||
foreach(output ${PROJECT_OUTPUT})
|
||||
add_executable(${output} ${output}_main.cc ${COMMON_CODE})
|
||||
target_link_libraries(${output} ${LIB})
|
||||
endforeach()
|
||||
|
12
tools/templates/rpc/client.conf
Normal file
12
tools/templates/rpc/client.conf
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"client":
|
||||
{
|
||||
"remote_host": "127.0.0.1",
|
||||
"remote_port": 1412,
|
||||
"is_ssl" : false,
|
||||
"redirect_max": 2,
|
||||
"retry_max": 1,
|
||||
"callee" : "rpc_client"
|
||||
}
|
||||
}
|
||||
|
48
tools/templates/rpc/client_protobuf.cc
Normal file
48
tools/templates/rpc/client_protobuf.cc
Normal file
@ -0,0 +1,48 @@
|
||||
#include <stdio.h>
|
||||
#include "srpc/rpc_types.h"
|
||||
#include "%s.srpc.h"
|
||||
#include "config/config.h"
|
||||
|
||||
using namespace srpc;
|
||||
|
||||
static srpc::RPCConfig config;
|
||||
|
||||
void init()
|
||||
{
|
||||
if (config.load("./client.conf") == false)
|
||||
{
|
||||
perror("Load config failed");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// 1. load config
|
||||
init();
|
||||
|
||||
// 2. start client
|
||||
RPCClientParams params = RPC_CLIENT_PARAMS_DEFAULT;
|
||||
params.host = config.client_host();
|
||||
params.port = config.client_port();
|
||||
%s
|
||||
%s::%sClient client(¶ms);
|
||||
config.load_filter(client);
|
||||
|
||||
// 3. request with sync api
|
||||
EchoRequest req;
|
||||
EchoResponse resp;
|
||||
RPCSyncContext ctx;
|
||||
|
||||
req.set_message("Hello, srpc!");
|
||||
client.Echo(&req, &resp, &ctx);
|
||||
|
||||
if (ctx.success)
|
||||
fprintf(stderr, "%%s\n", resp.DebugString().c_str());
|
||||
else
|
||||
fprintf(stderr, "status[%%d] error[%%d] errmsg:%%s\n",
|
||||
ctx.status_code, ctx.error, ctx.errmsg.c_str());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
49
tools/templates/rpc/client_thrift.cc
Normal file
49
tools/templates/rpc/client_thrift.cc
Normal file
@ -0,0 +1,49 @@
|
||||
#include <stdio.h>
|
||||
#include "srpc/rpc_types.h"
|
||||
#include "%s.srpc.h"
|
||||
#include "config/config.h"
|
||||
|
||||
using namespace srpc;
|
||||
|
||||
static srpc::RPCConfig config;
|
||||
|
||||
void init()
|
||||
{
|
||||
if (config.load("./client.conf") == false)
|
||||
{
|
||||
perror("Load config failed");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// 1. load config
|
||||
init();
|
||||
|
||||
// 2. start client
|
||||
RPCClientParams params = RPC_CLIENT_PARAMS_DEFAULT;
|
||||
params.host = config.client_host();
|
||||
params.port = config.client_port();
|
||||
%s
|
||||
%s::%sClient client(¶ms);
|
||||
config.load_filter(client);
|
||||
|
||||
// 3. request with sync api
|
||||
EchoResult res;
|
||||
|
||||
client.Echo(res, "Hello, srpc!");
|
||||
|
||||
if (client.thrift_last_sync_success())
|
||||
printf("%%s\n", res.message.c_str());
|
||||
else
|
||||
{
|
||||
const auto& sync_ctx = client.thrift_last_sync_ctx();
|
||||
|
||||
printf("status[%%d] error[%%d] errmsg:%%s\n",
|
||||
sync_ctx.status_code, sync_ctx.error, sync_ctx.errmsg.c_str());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
16
tools/templates/rpc/rpc.proto
Normal file
16
tools/templates/rpc/rpc.proto
Normal file
@ -0,0 +1,16 @@
|
||||
syntax="proto2";
|
||||
|
||||
message EchoRequest {
|
||||
required string message = 1;
|
||||
};
|
||||
|
||||
message EchoResponse {
|
||||
required string message = 1;
|
||||
optional int32 state = 2;
|
||||
optional int32 error = 3;
|
||||
};
|
||||
|
||||
service %s {
|
||||
rpc Echo(EchoRequest) returns (EchoResponse);
|
||||
};
|
||||
|
10
tools/templates/rpc/rpc.thrift
Normal file
10
tools/templates/rpc/rpc.thrift
Normal file
@ -0,0 +1,10 @@
|
||||
struct EchoResult {
|
||||
1:required string message;
|
||||
2:optional i32 state;
|
||||
3:optional i32 error;
|
||||
}
|
||||
|
||||
service %s {
|
||||
EchoResult Echo(1:string message);
|
||||
}
|
||||
|
6
tools/templates/rpc/server.conf
Normal file
6
tools/templates/rpc/server.conf
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"server":
|
||||
{
|
||||
"port": 1412
|
||||
}
|
||||
}
|
65
tools/templates/rpc/server_protobuf.cc
Normal file
65
tools/templates/rpc/server_protobuf.cc
Normal file
@ -0,0 +1,65 @@
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "workflow/WFFacilities.h"
|
||||
#include "srpc/rpc_types.h"
|
||||
|
||||
#include "config/config.h"
|
||||
#include "%s.srpc.h"
|
||||
|
||||
using namespace srpc;
|
||||
|
||||
static WFFacilities::WaitGroup wait_group(1);
|
||||
static srpc::RPCConfig config;
|
||||
|
||||
void sig_handler(int signo)
|
||||
{
|
||||
wait_group.done();
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
if (config.load("./server.conf") == false)
|
||||
{
|
||||
perror("Load config failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
signal(SIGTERM, sig_handler);
|
||||
}
|
||||
|
||||
class ServiceImpl : public %s::Service
|
||||
{
|
||||
public:
|
||||
void Echo(EchoRequest *req, EchoResponse *resp, RPCContext *ctx) override
|
||||
{
|
||||
%s// 4. delete the following codes and fill your logic
|
||||
fprintf(stderr, "get req: %%s\n", req->DebugString().c_str());
|
||||
resp->set_message("Hi back");
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
// 1. load config
|
||||
init();
|
||||
|
||||
// 2. start server
|
||||
%sServer server;
|
||||
ServiceImpl impl;
|
||||
server.add_service(&impl);
|
||||
|
||||
config.load_filter(server);
|
||||
|
||||
if (server.start(config.server_port()) == 0)
|
||||
{
|
||||
// 3. success and wait
|
||||
fprintf(stderr, "%s %s server start, port %%u\n", config.server_port());
|
||||
wait_group.wait();
|
||||
server.stop();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
62
tools/templates/rpc/server_thrift.cc
Normal file
62
tools/templates/rpc/server_thrift.cc
Normal file
@ -0,0 +1,62 @@
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "workflow/WFFacilities.h"
|
||||
#include "srpc/rpc_types.h"
|
||||
|
||||
#include "config/config.h"
|
||||
#include "%s.srpc.h"
|
||||
|
||||
using namespace srpc;
|
||||
|
||||
static WFFacilities::WaitGroup wait_group(1);
|
||||
static srpc::RPCConfig config;
|
||||
|
||||
void sig_handler(int signo)
|
||||
{
|
||||
wait_group.done();
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
if (config.load("./server.conf") == false)
|
||||
{
|
||||
perror("Load config failed");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
class ServiceImpl : public %s::Service
|
||||
{
|
||||
public:
|
||||
void Echo(EchoResult& _return, const std::string& message) override
|
||||
{
|
||||
%s// 4. delete the following codes and fill your logic
|
||||
fprintf(stderr, "get req: %%s\n", message.c_str());
|
||||
_return.message = "Hi back.";
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
// 1. load config
|
||||
init();
|
||||
|
||||
// 2. start server
|
||||
%sServer server;
|
||||
ServiceImpl impl;
|
||||
server.add_service(&impl);
|
||||
|
||||
config.load_filter(server);
|
||||
|
||||
if (server.start(config.server_port()) == 0)
|
||||
{
|
||||
// 3. success and wait
|
||||
printf("%s %s server start, port %%u\n", config.server_port());
|
||||
wait_group.wait();
|
||||
server.stop();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user