Project: SRPC (Orchestration & Management / Remote Procedure Call)
File: docs-07-srpc-http.md

srpc supports HTTP protocol, which make it convenient to communicate to other language. Just fill the IDL content into HTTP body, and fill the IDL type(json/protobuf/thrift) into Http header , we communicate with other frameworks through the HTTP protocol. SRPCHttpServer, TRPCHttpServer and ThriftHttpServer can receive HTTP requests from client implemented by any language. SRPCHttpClient, TRPCHttpClient and ThriftHttpClient can send HTTP requests to servers implemented by any language. HTTP header: `Content-Type` needs to be set as `application/json` when body is json, or set as `application/x-protobuf` when body is protobuf, or set as `application/x-thrift` when body is thrift; HTTP body: If there is bytes type in body, json needs to be encoded with base64. To implement SRPCHttpClient is so simple that we just need to refer to or and change the `SRPCClient` into `SRPCHttpClient`. demonstrates how to use curl to send a request to SRPCHttpServer, and below we give an example to demonstrate how to use python as a client to send a request to TRPCHttpServer. proto file : ```proto syntax="proto3"; // proto2 or proto3 are both supported package trpc.test.helloworld; message AddRequest { string message = 1; string name = 2; bytes info = 3; int32 error = 4; }; message AddResponse { string message = 1; }; service Batch { rpc Add(AddRequest) returns (AddResponse); }; ``` python client : ```py import json import requests from base64 import b64encode class Base64Encoder(json.JSONEncoder): def default(self, o): if isinstance(o, bytes): return b64encode(o).decode() return json.JSONEncoder.default(self, o) headers = {'Content-Type': 'application/json'} req = { 'message': 'hello', 'name': 'k', 'info': b'i am binary' } print(json.dumps(req, cls=Base64Encoder)) ret = requests.post(url = "http://localhost:8800/trpc.test.helloworld.Batch/Add", headers = headers, data = json.dumps(req, cls=Base64Encoder)) print(ret.json()) ``` shows that the path is concatenated by the service name and the rpc name. Moreover, for the above proto file example with the package name `package trpc.test.helloworld;`, the package name also needs to be spliced into the path. The splicing paths of SRPCHttp and TRPCHttpClient are different. For ThriftHttp, multi service is not supported in SRPC thrift, so no path is required. Let's take curl as an example: Request to SRPCHttpServer: ```sh curl 127.0.0.1:8811/trpc/test/helloworld/Batch/Add -H 'Content-Type: application/json' -d '{...}' ``` Request to TRPCHttpServer: ```sh curl 127.0.0.1:8811/trpc.test.helloworld.Batch/Add -H 'Content-Type: application/json' -d '{...}' ``` Request to ThriftHttpServer: ```sh curl 127.0.0.1:8811 -H 'Content-Type: application/json' -d '{...}' ``` SRPC supports server to set HTTP status code in `process()`, the interface is `sethttpcode(int code)` on

This error code is only valid if the framework can handle the request correctly, otherwise it will be set to the SRPC framework-level error code. Usage : ~~~cpp class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest req, EchoResponse resp, RPCContext *ctx) override { if (req->name() != "workflow") ctx->sethttpcode(404); // set HTTP status code as 404 else resp->set_message("Hi back"); } }; ~~~ CURL command : ~~~sh curl -i 127.0.0.1:1412/Example/Echo -H 'Content-Type: application/json' -d '{message:"from curl",name:"CURL"}' ~~~ Result : ~~~sh HTTP/1.1 404 Not Found SRPC-Status: 1 SRPC-Error: 0 Content-Type: application/json Content-Encoding: identity Content-Length: 21 Connection: Keep-Alive ~~~ Notice : We can still distinguish this request is correct at framework-level by `SRPC-Status: 1` in the header of the returned result, and `404` is the status code from the server. Use the following three interfaces to get or set HTTP header: ~~~cpp bool gethttpheader(const std::string& name, std::string& value) const; bool sethttpheader(const std::string& name, const std::string& value); bool addhttpheader(const std::string& name, const std::string& value); ~~~ For server, these interfaces are on `RPCContext`. For client, we need to set the HTTP header on the request through `RPCClientTask`, and get the HTTP header on the response on the `RPCContext` of the callback function. The usage is as follows: ~~~cpp int main() { Example::SRPCHttpClient client("127.0.0.1", 80); EchoRequest req; req.set_message("Hello, srpc!"); auto *task = client.createEchotask( { if (ctx->success()) { std::string value; ctx->gethttpheader("server_key", value); // get the HTTP header on response } }); task->serialize_input(&req); task->sethttpheader("clientkey", "clientvalue"); // set the HTTP headers on request task->start(); wait_group.wait(); return 0; } ~~~ When IDL is protobuf, by default proto3 primitive fields with default values will be omitted in JSON output because there is no optional and required distinction. For example, an int32 field set to 0 will be omitted.So proto3 cannot be recognized as being set, and it cannot be emitted when serialized. JsonPrintOptions can use a flag to override the default behavior and print primitive fields regardless of their values. SRPC provides some interfaces on RPCContext to implement this and other flags on . The specific interface and usage description can be refered in: Exmaple : ```cpp class ExampleServiceImpl : public Example::Service { public: void Echo(EchoRequest req, EchoResponse resp, RPCContext *ctx) override { resp->set_message("Hi back"); resp->set_error(0); // the type of error is int32 and 0 is the default value of int32 ctx->setjsonalwaysprintprimitive_fields(true); // with all primitive fields ctx->setjsonadd_whitespace(true); // add spaces, line breaks and indentation } }; ``` Origin output : ```sh {"message":"Hi back"} ``` After set json options on RPCContext: ```sh { "message": "Hi back", "error": 0 } ```