Merge pull request #286 from holmes1412/master

add srpc-ctl
This commit is contained in:
xiehan 2023-03-13 20:34:07 +08:00 committed by GitHub
commit f84d95711a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 7290 additions and 1 deletions

View File

@ -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
View 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
View 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
View 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
View 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/
```

View 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
View 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(&params);
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
View 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
View 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
View 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
View 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;
}

View 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;
}

View 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;
}

View File

@ -0,0 +1,9 @@
{
"client":
{
"remote_host": "127.0.0.1",
"remote_port": %u,%s
"retry_max": 2%s
}
}

View 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;
}

View File

@ -0,0 +1,7 @@
{
"server":
{
"port": %u
}
}

View 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;
}

View 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()

View 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)

View 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" }
]
}
]
}

View 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);
}

View 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

View 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_

View 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(), &param.endpoint_params);
else if (p.key() == "weight")
param.weight = p.value().get<unsigned short>();
else if (p.key() == "max_fails")
param.max_fails = p.value().get<unsigned int>();
else if (p.key() == "dns_ttl_default")
param.dns_ttl_default = p.value().get<unsigned int>();
else if (p.key() == "dns_ttl_min")
param.dns_ttl_min = p.value().get<unsigned int>();
else if (p.key() == "server_type")
param.server_type = p.value().get<int>();
else if (p.key() == "group_id")
param.group_id = p.value().get<int>();
else
printf("[ERROR][load_upstream] Invalid params: %s\n",
p.key().c_str());
}
}
hosts.push_back(server["host"]);
params.push_back(param);
}
if (hosts.size() == 0)
return false;
else
return true;
}
static void load_upstream(const wfrest::Json& data)
{
std::string name;
std::string type;
bool try_another;
std::vector<std::string> hosts;
std::vector<AddressParams> params;
for (const auto& it : data)
{
if (it.has("name") == false ||
it.has("type") == false ||
it.has("server") == false ||
load_upstream_server(it["server"], hosts, params) == false)
{
printf("[ERROR][load_upstream] Invalid upstream\n");
continue;
}
name = it["name"].get<std::string>();
type = it["type"].get<std::string>();
if (it.has("try_another"))
try_another = it["try_another"];
else
try_another = false;
if (type == "weighted_random")
{
UpstreamManager::upstream_create_weighted_random(name, try_another);
}
else if (type == "consistent_hash")
{
UpstreamManager::upstream_create_consistent_hash(name,
default_consistent_hash);
}
else if (type == "round_robin")
{
UpstreamManager::upstream_create_round_robin(name, try_another);
}
else if (type == "manual")
{
UpstreamManager::upstream_create_manual(name,
default_select_route,
try_another,
default_consistent_hash);
}
else if (type == "vnswrr")
{
UpstreamManager::upstream_create_vnswrr(name);
}
else
{
printf("[INFO][load_upstream] Unknown type : %s\n", type.c_str());
continue;
}
for (size_t i = 0; i < hosts.size(); i++)
UpstreamManager::upstream_add_server(name, hosts[i], &params[i]);
}
}
void 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];
}

View 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

View 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(), &param.endpoint_params);
else if (p.key() == "weight")
param.weight = p.value().get<unsigned short>();
else if (p.key() == "max_fails")
param.max_fails = p.value().get<unsigned int>();
else if (p.key() == "dns_ttl_default")
param.dns_ttl_default = p.value().get<unsigned int>();
else if (p.key() == "dns_ttl_min")
param.dns_ttl_min = p.value().get<unsigned int>();
else if (p.key() == "server_type")
param.server_type = p.value().get<int>();
else if (p.key() == "group_id")
param.group_id = p.value().get<int>();
else
printf("[ERROR][load_upstream] Invalid params: %s\n",
p.key().c_str());
}
}
hosts.push_back(server["host"]);
params.push_back(param);
}
if (hosts.size() == 0)
return false;
else
return true;
}
static void load_upstream(const wfrest::Json& data)
{
std::string name;
std::string type;
bool try_another;
std::vector<std::string> hosts;
std::vector<AddressParams> params;
for (const auto& it : data)
{
if (it.has("name") == false ||
it.has("type") == false ||
it.has("server") == false ||
load_upstream_server(it["server"], hosts, params) == false)
{
printf("[ERROR][load_upstream] Invalid upstream\n");
continue;
}
name = it["name"].get<std::string>();
type = it["type"].get<std::string>();
if (it.has("try_another"))
try_another = it["try_another"];
else
try_another = false;
if (type == "weighted_random")
{
UpstreamManager::upstream_create_weighted_random(name, try_another);
}
else if (type == "consistent_hash")
{
UpstreamManager::upstream_create_consistent_hash(name,
default_consistent_hash);
}
else if (type == "round_robin")
{
UpstreamManager::upstream_create_round_robin(name, try_another);
}
else if (type == "manual")
{
UpstreamManager::upstream_create_manual(name,
default_select_route,
try_another,
default_consistent_hash);
}
else if (type == "vnswrr")
{
UpstreamManager::upstream_create_vnswrr(name);
}
else
{
printf("[INFO][load_upstream] Unknown type : %s\n", type.c_str());
continue;
}
for (size_t i = 0; i < hosts.size(); i++)
UpstreamManager::upstream_add_server(name, hosts[i], &params[i]);
}
}
void 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()
{
}

View 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

View File

@ -0,0 +1 @@
<html>This is not the web page you are looking for.</html>

View 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>

View 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);
}

View 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

View File

@ -0,0 +1 @@
<html>Hello from workflow and srpc file server!</html>

View 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"
}
]
}
}

View 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;
}

View 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"
}
}

View File

@ -0,0 +1,14 @@
{
"server":
{
"port": %s
},
"client":
{
"remote_host": "127.0.0.1",
"remote_port": %s,
"retry_max": 2
}
}

View 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;
}

View 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;
}

View 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()

View 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"
}
}

View 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(&params);
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;
}

View 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(&params);
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;
}

View 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);
};

View 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);
}

View File

@ -0,0 +1,6 @@
{
"server":
{
"port": 1412
}
}

View 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;
}

View 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;
}