目前 tRPC-Cpp 支持 HTTP 标准服务和 HTTP RPC 服务(这两个名称仅仅为了区分两种不同服务)。
HTTP 标准服务是一个普通的 HTTP 服务,不使用 proto 文件定义服务接口,用户需要自己编写代码定义服务接口,注册 URL 路由。
HTTP RPC 服务是 RPC 服务,客户端可以通过 HTTP 协议来访问。服务调用接口由 proto 文件定义,可以由工具生产桩代码。
本文介绍如何基于 tRPC-Cpp (下面简称 tRPC)开发 HTTP 服务,开发者可以了解到如下内容:
- 如何开发 HTTP 标准服务
- 快速上手:快速搭建一个 HTTP 服务。
- 功能特性一览:RESTful;HTTPS。
- 基础用法:获取请求、响应常用接口;配置请求路由规则。
- 进阶用法:分组路由规则;HTTPS;消息体压缩、解压缩;文件上传、下载等。
- 如何开发 HTTP RPC 服务
- 快速上手:支持 HTTP 客户端访问 tRPC 服务。
- FAQ
在正式开始前,可以先尝试运行示例代码,直观感受下 HTTP 服务运行过程 。
示例:http
方法:进入 tRPC-Cpp 代码仓库主目录,然后运行下面的命令(感谢耐心等待一小会,此过程会经历代码下载、编译、启动运行阶段,会耗费一点点时间)。
sh examples/features/http/run.sh
示例程序输出:
# 部分过程输出片段内容
response content: hello world!
response content: {"msg":"hello world!"}
response content: {"msg": "hello world!", "age": 18, "height": 180}
# 测例运行成功或失败状态(1: 成功,0:失败)
name: GET string, ok: 1
name: GET json, ok: 1
name: GET http response, ok: 1
name: GET http response(not found), ok: 1
name: HEAD string, ok: 1
name: POST string, ok: 1
name: POST json, ok: 1
name: POST string, then wait http response, ok: 1
name: UNARY invoking, ok: 1
final result of http calling: 1
接下来看看 HTTP 服务的开发过程。
这里用一个简单的 Hello World
例子来介绍开发 HTTP 服务的基本步骤。
示例 http_server.cc。
基本功能:
- 支持 HTTP 客户端使用
HEAD or GET or POST
等方法访问。 - 例如,
POST
Hello world!
将得到Hello world!
响应。
基本步骤:
- 实现
HttpHandler
接口。 - 设置请求路由。
- 向 Server 注册一个
HttpService
实例。
FooHandler
实现HttpHandler
接口 ,处理 HTTP 请求,并设置 HTTP 响应内容。
class FooHandler : public ::trpc::http::HttpHandler {
public:
::trpc::Status Get(const ::trpc::ServerContextPtr& ctx, const ::trpc::http::RequestPtr& req,
::trpc::http::Response* rsp) override {
rsp->SetContent(greetings_);
return ::trpc::kSuccStatus;
}
::trpc::Status Head(const ::trpc::ServerContextPtr& ctx, const ::trpc::http::RequestPtr& req,
::trpc::http::Response* rsp) override {
rsp->SetHeader("Content-Length", std::to_string(greetings_.size()));
return ::trpc::kSuccStatus;
}
::trpc::Status Post(const ::trpc::ServerContextPtr& ctx, const ::trpc::http::RequestPtr& req,
::trpc::http::Response* rsp) override {
rsp->SetContent(req->GetContent());
return ::trpc::kSuccStatus;
}
private:
std::string greetings_{"hello world!"};
};
HttpHandler
是什么?
HttpHandler
提供了一种简单地对HTTP Endpoint
类进行抽象的模式,每个常见 HTTP Method 有对应的接口。 各个方法默认的响应为: "501 Not Implemented"。
接口签名大致如下:
class HttpHandler {
public:
virtual ::trpc::Status Head(const ::trpc::ServerContextPtr& ctx, const ::trpc::http::RequestPtr& req, ::trpc::http::Response* rsp);
virtual ::trpc::Status Get(const ::trpc::ServerContextPtr& ctx, const ::trpc::http::RequestPtr& req, ::trpc::http::Response* rsp);
virtual ::trpc::Status Post(const ::trpc::ServerContextPtr& ctx, const ::trpc::http::RequestPtr& req, ::trpc::http::Response* rsp);
virtual ::trpc::Status Put(const ::trpc::ServerContextPtr& ctx, const ::trpc::http::RequestPtr& req, ::trpc::http::Response* rsp);
virtual ::trpc::Status Delete(const ::trpc::ServerContextPtr& ctx, const ::trpc::http::RequestPtr& req, ::trpc::http::Response* rsp);
virtual ::trpc::Status Options(const ::trpc::ServerContextPtr& ctx, const ::trpc::http::RequestPtr& req, ::trpc::http::Response* rsp);
virtual ::trpc::Status Patch(const ::trpc::ServerContextPtr& ctx, const ::trpc::http::RequestPtr& req, ::trpc::http::Response* rsp);
// ...
};
- 设置请求路由:将请求 URI 为
/foo
的请求路由到FooHandler
,支持HEAD GET POST
三种方法。
void SetHttpRoutes(::trpc::http::HttpRoutes& r) {
// ...
auto foo_handler = std::make_shared<FooHandler>();
r.Add(::trpc::http::MethodType::GET, ::trpc::http::Path("/foo"), foo_handler);
r.Add(::trpc::http::MethodType::HEAD, ::trpc::http::Path("/foo"), foo_handler);
r.Add(::trpc::http::MethodType::POST, ::trpc::http::Path("/foo"), foo_handler);
// ...
}
- 在应用程序初始化阶段注册一个
HttpService
实例,并设置请求路由。
class HttpdServer : public ::trpc::TrpcApp {
public:
int Initialize() override {
auto http_service = std::make_shared<::trpc::HttpService>();
http_service->SetRoutes(SetHttpRoutes);
RegisterService("default_http_service", http_service);
return 0;
}
void Destroy() override {}
};
应用程序启动后,可以通过如下如下 URL 访问。
# GET /foo HTTP/1.1
# e.g: curl http://$ip:$port/foo
$ hello world!
功能/特性 | 支持状态 | 备注 |
---|---|---|
RESTful HEAD/GET/POST ... PATCH | Yes | 提供同步/异步接口 |
HTTPS | Yes | 支持双向认证,具体看 ssl 配置 |
压缩/解压缩 | Yes | 提供 gzip, snappy, lz4 等工具,需要用户自行处理 |
大文件上传/下载 | Yes | 提供同步/异步接口 |
HTTP2 | No | 准备中 |
HTTP3 | No | - |
可以通过 GetMethod()
接口获取 HTTP Method
。
const std::string& GetMethod() const;
// e.g. 记录请求方法到日志。
TRPC_FMT_INFO("request method: {}", request->GeMethod());
如果请求行为 GET /hello?a=foo&b=bar HTTP/1.1
,则日志会记录 request method: GET
。
可以通过 GetUrl()
获取 Request URI
。
const std::string& GetUrl() const;
// e.g. 记录 Request URI 到日志。
TRPC_FMT_INFO("request uri: {}", request->GetUrl());
如果请求行为 GET /hello?a=foo&b=bar HTTP/1.1
,则日志会记录 request uri: /hello?a=foo&b=bar
。
Query String
多个 key-value 键值对,key 区分大小。通过 Request URI
来传输,常见形式是 key1=value1&key2=value2&…
,易于阅读和修改,常用于传递应用层参数。
通过 GetQueryParameters()
获取参数对象。
const QueryParameters& GetQueryParameters() const;
// e.g. 获取 Query String 中的 a 和 b 参数。
auto a = GetQueryParameters().Get("a");
auto b = GetQueryParameters().Get("b");
如果请求行为 GET /hello?a=foo&b=bar HTTP/1.1
,则 a= "foo"; b = "bar"
。
Headers 是多个 key-value 键值对,在 HTTP 协议中, key 不区分大小写。
Request/Response
提供增、删、改、查接口。
// e.g. 从请求头中获取 User-Agent 值。
auto user_agent = request->GetHeader("User-Agent");
// e.g. 从请求中获取 Content-Type 值。
auto content_type = request->GetHeader("Content-Type");
// e.g. 设置响应的 Content-Type 为 JSON。
response->SetHeader("Content-Type", "application/json");
请求、响应的消息体是个缓冲区,提供两种接口访问:
- 连续 Buffer: std::string。
- 非连续 Buffer: NonContiguousBuffer。
const std::string& GetContent() const;
void SetContent(std::string content);
// Better performance.
const NoncontiguousBuffer& GetNonContiguousBufferContent() const;
void SetNonContiguousBufferContent(NoncontiguousBuffer&& content);
// e.g. 从请求中获取 Body
auto& body = request->GetContent();
// e.g. 设置响应 Body 为 "Hello world!"。
response->SetContent("Hello world!");
status 是 Response 特有的字段,标记 HTTP Response 完成状态,值定义在 http/status.h。
int GetStatus() const;
void SetStatus(int status);
// e.g. 设置响应码为 302 重定向。
response->SetStatus(::trpc::http::ResponseStatus::kFound);
response->SetHeader("Location", "https://github.com/");
当前支持如下匹配规则:
- 完全匹配
- 前缀匹配
- 正则匹配
- 占位符匹配
直接在 Path(path_value)
中填写路径时,默认是采取完全匹配。即当请求 URI 中的 Path 与路由设置的 path_value
值完全一致时,请求才会被相应的 handler 处理。
r.Add(trpc::http::MethodType::GET, trpc::http::Path("/hello/img_x"), handler);
curl http://$ip:$port/hello/img_x -X GET # 响应码 200
curl http://$ip:$port/hello/img -X GET # 响应码 404
通过 Path(path_value).Remainder("path")
追加 path 占位符,可使用前缀匹配, 即仅当请求 URI 中的 Path 前缀 与
Path 一致,请求也可被相应的 handler 处理。
Remainder
中内容仅为占位符,无实际含义,建议默认填写为path
。
r.Add(trpc::http::MethodType::GET, trpc::http::Path("/hello").Remainder("path"), handler_hello);
curl http://$ip:$port/hello/img -X GET # 响应码 200
curl http://$ip:$port/hello/img/1 -X GET # 响应码 200
curl http://$ip:$port/hello -X GET # 响应码 200
# 如果前缀不匹配,则访问失败。
curl http://$ip:$port/hello1 -X GET # 响应码 404
curl http://$ip:$port/hello1/img -X GET # 响应码 404
通过使用 <regex()>
来包裹 path_value
时, 可使用正则匹配,即实际填写的路径值为一个正则表达式,当请求 URI 中的 Path
符合该正则表达式时,请求会被相应的 handler 处理。
r.Add(trpc::http::MethodType::GET, trpc::http::Path("<regex(/img/[a-z]_.*)>"), handler);
curl http://$ip:$port/img/d_123/ccd -X GET` # 响应码 200
curl http://$ip:$port/img/123_123/ccd -X GET` # 响应码 404
通过使用 <ph()>
包裹 path_value
时,可使用占位符匹配。即实际填写的路径值允许包含占位符, 当请求 URI 中的 Path
符合该正则表达式时,请求会被相应的 handler 处理,这对于路径参数非常适用。
下面 URI 路径的占位符包括 channel_id
和 client_id
:
r.Add(trpc::http::MethodType::GET, trpc::http::Path("<ph(/channels/<channel_id>/clients/<client_id>)>"), handler);
可以方便取出占位符的值:
::trpc::Status Get(const ::trpc::ServerContextPtr& ctx, const ::trpc::http::RequestPtr& req,
::trpc::http::Response* rsp) override {
auto channel_id = req->GetParameters().Path("channel_id");
auto client_id = req->GetParameters().Path("client_id");
// ...
return ::trpc::kSuccStatus;
}
curl http://$ip:$port/channels/xyz/clients/123 -X GET # 响应码 200
curl http://$ip:$port/channels/clients -X GET # 响应码 404
注意:
占位符的符号仅支持字母、数字、下划线。
本节介绍进阶的用法,比如 分组路由规则、HTTPS、消息体压缩和解压缩、大文件上传和下载等。
使用 HttpHandlerGroups
工具类,可以提供可读性更高的简易 HTTP 路由注册方法,适用于需要提供多层次路径的服务,例如,RESTful
Services。
HttpHandlerGroups 目前仅支持 完全匹配
和 占位符匹配
两种形式,使用时需额外引入 trpc/util/http/http_handler_groups.h
头文件。
新增宏命令如下,具体使用方法详见下方代码示例:
宏名称 | 功能 | 说明 | 参数 | 示例 |
---|---|---|---|---|
TRPC_HTTP_ROUTE_HANDLER | 声明一层 HTTP RESTful 路由,可嵌套使用 | 默认引用捕获所有外界参数(如全局变量、成员变量),无法捕获超过最外层函数的scope(如局部变量) | { 具体的路由声明代码 } | TRPC_HTTP_ROUTE_HANDLER({ code; }) |
TRPC_HTTP_ROUTE_HANDLER_ARGS | 声明一层带参数捕获的 HTTP RESTful 路由,可嵌套使用 | 默认引用捕获所有外界参数(如全局变量、成员变量)和额外传入的参数(**!!#ff0000 可以!!**是局部变量) | ( 所有传入参数的值 ), ( 各个参数的类型和名称 ) { 具体的路由声明代码 } | TRPC_HTTP_ROUTE_HANDLER_ARGS((a, 1, a+1), (double a, int b, auto c) { code; }) |
TRPC_HTTP_HANDLER | 绑定指定对象和其成员方法为一个路由终点 | - | 对象的shared_ptr, 成员方法名(以类名::方法名 填写) |
TRPC_HTTP_HANDLER(coffee_pot_controller, CoffeePotController::Brew) |
TRPC_HTTP_STREAM_HANDLER | 绑定指定对象和其成员方法为一个流式路由终点 | - | 对象的shared_ptr, 成员方法名(以类名::方法名 填写) |
TRPC_HTTP_STREAM_HANDLER(stream_download_controller, StreamDownloadController::Get) |
先来看下完整示例:
void SetHttpRoutes(trpc::http::HttpRoutes& routes) {
// 建议加入 clang-format 控制字段, 避免不理想的代码格式化结果
// clang-format off
trpc::http::HttpHandlerGroups(routes).Path("/", TRPC_HTTP_ROUTE_HANDLER({ // TRPC_HTTP_ROUTE_HANDLER 宏声明一层路由, 可多层嵌套
// 默认 TRPC_HTTP_ROUTE_HANDLER 会捕获所有外界引用, 但受函数内部类限制, 无法捕获超过最外层函数的 scope
Path("/api", TRPC_HTTP_ROUTE_HANDLER({ // 每层 Path 可以是普通字符串, 或是以尖括号声明占位符, 见下方, 前缀和后缀 "/" 可忽略
auto hello_world_handler = std::make_shared<ApiHelloWorldHandler>();
Path("/user", TRPC_HTTP_ROUTE_HANDLER({
Get<ApiUserHandler>(); // Get - /api/user - 此处将会自动注册和管理一个 ApiUserHandler 对象(必须为可默认构造的)
Post<ApiUserHandler>("/"); // Post - /api/user - 同个 HttpHandlerGroups 中对同类型复用相同对象
Delete<ApiUserHandler>("/<user_id>"); // Delete - /api/user/<user_id> - 复用对象 - 路径占位符名为 user_id
});
Path("/hello", TRPC_HTTP_ROUTE_HANDLER_ARGS((hello_world_handler), (auto h) {
// 使用 TRPC_HTTP_ROUTE_HANDLER_ARGS((captures...), (args...) { ... }) 可自定义需捕获的值和捕获后的变量名
Head("/hello_world", h); // Head - /api/hello_world - 也可以通过 std::shared_ptr 注册对象
});
Get("/teapot", [](auto ctx, auto req, auto rep) { // 第一个匹配的路由优先, /api/teapot 会到这里
rep->SetStatus(::trpc::http::ResponseStatus::kIAmATeapot);
});
Get("<path>", TRPC_HTTP_HANDLER(coffee_pot_controller, CoffeePotController::Brew)); // 而 /api/coffee_pot 会到这里
});
});
// clang-format on
}
使用 TRPC_HTTP_ROUTE_HANDLER
宏注册 HttpHandler
接口路由。
class ApiUserHandler : public ::trpc::http::HttpHandler {
public:
void Get(const ::trpc::ServerContextPtr& ctx, const ::trpc::http::RequestPtr& req, ::trpc::http::Response* rsp) override {
// return user info.
}
void Post(const ::trpc::ServerContextPtr& ctx, const ::trpc::http::HttpRequestPtr& req, ::trpc::http::Response* rsp) override {
// create new user.
}
};
// ...
::trpc::http::HttpHandlerGroups(routes).Path("/", TRPC_HTTP_ROUTE_HANDLER({
Path("/api", TRPC_HTTP_ROUTE_HANDLER({
Path("/user", TRPC_HTTP_ROUTE_HANDLER({
Get<ApiUserHandler>(); // Get - /api/user - 此处将会自动注册和管理一个 ApiUserHandler 对象
Post<ApiUserHandler>("/"); // Post - /api/user - 同个 HttpHandlerGroups 中对同类型复用相同对象
});
auto hello_world_handler = std::make_shared<ApiHelloWorldHandler>();
Head("/hello_world", hello_world_handler); // Head - /api/hello_world - 也可以通过 std::shared_ptr 注册对象
});
});
// ...
使用 TRPC_HTTP_HANDLER
宏,用户也可以方便地注册类似于 Controller
类接口到路由中:
提示: Controller 未继承 HttpHandler
类,只是接口签名相同。
class UserController {
public:
void Get(const ::trpc::ServerContextPtr& ctx, const ::trpc::http::RequestPtr& req, ::trpc::http::Response* rsp) {
// return user info.
}
void Post(const ::trpc::ServerContextPtr& ctx, const ::trpc::http::HttpRequestPtr& req, ::trpc::http::Response* rsp) {
// create new user.
}
};
// ...
trpc::http::HttpHandlerGroups(routes).Path("/", TRPC_HTTP_ROUTE_HANDLER({
Path("/api", TRPC_HTTP_ROUTE_HANDLER({
Path("/user", TRPC_HTTP_ROUTE_HANDLER({
auto user_controller = std::make_shared<UserController>();
Get(TRPC_HTTP_HANDLER(user_controller, UserController::GetUser)); // Get - /api/user
Post(TRPC_HTTP_HANDLER(user_controller, UserController::CreateUser)); // Post - /api/user
});
});
});
// ...
如果需要,用户可以直接注册 lambda 函数到路由之中。
trpc::http::HttpHandlerGroups(routes).Path("/api", TRPC_HTTP_ROUTE_HANDLER({
Get("/teapot", [](auto ctx, auto req, auto rep) {
rep->SetStatus(::trpc::http::ResponseStatus::kIAmATeapot);
});
Get("<path>", handler);
});
为了降低网页传输时间,加快页面渲染速度,HTTP 服务可能会压缩响应消息体。 tRPC 当前不会自动压缩或者解压缩消息体,主要基于如下考虑:
- 通用性,这个操作交由用户自行处理会更灵活。
- 压缩、解压缩代码不是很复杂,tRPC 提供了压缩和解压缩工具,当前支持 gzip,lz4, snappy,zlib 等 compressor
HTTPS 是 HTTP over SSL 的简称,可以通过如下方式开启 SSL。
-
编译代码时开启 SSL 编译选项。
使用 bazel build 时,增加 --define trpc_include_ssl=true 编译参数。 提示:也可以加到 .bazelrc 文件中。
提示:tRPC 基于 OpenSSL 支持 HTTPS,请确保编译、运行环境正确安装 OpenSSL 。
// e.g. bazel build --define trpc_include_ssl=true //https:http_server
-
在配置文件中设置 SSL 相关配置,具体配置项如下:
名称 功能 取值范围 默认值 可选状态 说明 cert_path 证书路径 不限,xx/path/to/server.pem null required
在启用 SSL 时,如果不设置正确路径,服务会启动失败 private_key_path 私钥路径 不限,xx/path/to/server.key null required
在启用 SSL 时,如果不设置正确路径,服务会启动失败 ciphers 加密套件 不限 null required
在启用 SSL 时,如果不正确设置,服务会启动失败 enable 是否启用SSL {true, false} false optional 建议在配置项明确指定,明确意图 mutual_auth 是否启用双向认证 {true, false} false optional - ca_cert_path CA证书路径 不限,xx/path/to/ca.pem null optional 双向认证时开启有效 protocols SSL协议版本 {SSLv2, SSLv3, TLSv1, TLSv1.1, TLSv1.2} TLSv1.1 + TLSv1.2 optional - 举个例子:
# @file: trpc_cpp.yaml # ... server: service: - name: default_http_service network: tcp ip: 0.0.0.0 port: 24756 protocol: http # ... ## <-- 新增的 SSL 配置项 ssl: enable: true # 可选配置(默认为 false,表示禁用 SSL) cert_path: ./https/cert/server_cert.pem # 必选配置 private_key_path: ./https/cert/server_key.pem # 必选配置 ciphers: HIGH:!aNULL:!kRSA:!SRP:!PSK:!CAMELLIA:!RC4:!MD5:!DSS # 必选配置 # mutual_auth: true # 可选配置(默认为 false,表示不开启双向认证) # ca_cert_path: ./https/cert/xxops-com-chain.pem # 可选配置,双向认证的 CA 路径 # protocols: # 可选配置 # - SSLv2 # - SSLv3 # - TLSv1 # - TLSv1.1 # - TLSv1.2 ## --> 新增的SSL配置项 # ...
如果服务端处理 HTTP Request 的逻辑异步执行,然后用户期望自己主动回复响应给客户端,而不是让 tRPC 自动回复 HTTP 响应,可以采用如下方式:
-
关闭 tRPC 回包: context->SetResponse(false) 。
-
回包时,将 HTTP Response 序列化成非连续 Buffer,然后调用 context->SendResponse(buffer) 发送响应包。
// ServerContext 接口 void SetResponse(bool is_response); Status SendResponse(NoncontiguousBuffer&& buffer); // HTTP Response 接口 bool SerializeToString(NoncontiguousBuffer& buff) const;
-
代码片段:
// 使用示例 trpc::Status Get(const trpc::ServerContextPtr& context, const trpc::http::RequestPtr req, trpc::http::Response* rep) { // ... // 关闭 tRPC 主动回包 context->SetResponse(false); // 异步操作,比如 Future 模式异步调用 DoAsyncWork(context,req); // ... return trpc::kSucctStatus; } void DoAsyncWork(const trpc::ServerContextPtr& context,const trpc::http::RequestPtr& req){ // ... trpc::http::Response http_response; // 生成正常响应的函数签名 void GenerateCommonReply(HttpRequest* req); http_response.GenerateCommonReply(req.get()); // 设置响应内容,比如 http_response.SetContent("DoAsyncWork"); // 将 HTTP Response 序列化为非连续 Buffer trpc::NoncontiguousBuffer out; http_response.SerializeToString(out); // 使用 ServerContext 主动回包 context->SendResponse(std::move(out)); }
在 HTTP 服务中,有部分场景需要读取或者发送大文件的场景,将文件完整读入内存压力较大且效率较低,对于大文件的上传可行性不高。 tRPC 提供一套 HTTP 流式读取/写入数据分片的接口,可以分片接收/发送大文件。
- 对于已经知晓长度的大文件,设置
Content-Length: $length
后分块发送(当然也可以使用 chunked 分块发送,如果对端支持的话)。 - 对于未知长度的大文件,设置
Transfer-Encoding: chunked
后分块发送。
具体可以参考 http 上传 下载。
某些场景下,一些 RPC 服务可能需要对外支持 HTTP 客户端。即,通过 HTTP 协议传输 tRPC 协议头部和载荷。
可以通过修改配置项,无需修改代码,轻松实现:protocol: trpc
-> protocol: trpc_over_http
。
如果需要同时支持 tRPC 和 HTTP 客户端,可以在服务初始化阶段注册两个 RPC Service 实例。
-
代码片段:
class HelloWorldServer : public ::trpc::TrpcApp { public: int Initialize() override { // ... std::string service_name = "trpc.test.helloworld.Greeter"; RegisterService(service_name, std::make_shared<GreeterServiceImpl>()); std::string service_over_http_name = "trpc.test.helloworld.GreeterHTTP"; RegisterService(service_over_http_name, std::make_shared<GreeterServiceImpl>()); // ... return 0; } };
-
配置片段:
# @file: trpc_cpp.yaml # ... service: - name: trpc.test.helloworld.Greeter protocol: trpc network: tcp ip: 0.0.0.0 port: 12345 - name: trpc.test.helloworld.GreeterHTTP protocol: trpc_over_http network: tcp ip: 0.0.0.0 port: 23456 # ...
对于HTTP RPC 服务,框架在编解码时会对透传信息进行如下处理:
- 解码:框架在收到请求包时,会从HTTP头部中取出名称为
“trpc-trans-info”
的数据,将其内容按JSON格式进行解析,然后将解析到的键值对作为请求的透传信息。用户可以通过“ServerContext::GetPbReqTransInfo”
接口获取。 - 编码:框架在进行回包响应时,会将用户通过
“ServerContext::AddRspTransInfo”
接口设置的透传信息,转换成一个名称为“trpc-trans-info”
,值为JSON串的HTTP头部。
注意框架默认不会对透传信息中的值进行base64编解码,如果有需要,可以通过添加“trpc_enable_http_transinfo_base64”
编译选项来开启:
build --define trpc_enable_http_transinfo_base64=true
// e.g. :
r.Add(trpc::http::MethodType::POST, trpc::http::Path("").Remainder("path"), handler);
请确保 ssl 配置项中的证书、私钥等文件路径配置正确。
curl/libcurl 在上传数据时,当 body 大于 1K 时,会先询问下服务器是否允许发送大数据包(请求头中增加 "Expect: 100 continue")。 如果允许则回复一个 100 continue 码,然后libcurl 继续发送请求体。 如果服务端不响应 100 continue,等待1s后,会继续将请求体发给服务端。 当前 tRPC HTTP Server 侧没有响应 Expect: 100 continue,所以 curl 只能等待1s后发送数据。
规避办法:
- curl 测试时,发送一个带空值的 "Expect:" ,curl -H "Expect:" -X POST -T "ddd" $url
- 使用 tRPC HTTP 上传、下载相关接口实现相关服务接口,这类接口支持响应 100 Continue。