A sample of JSON RPC service

发布时间 2023-12-09 22:00:59作者: inthelight

This is a sample service program which show how to implement a JSON RPC. The RPC service included two functions which used for RSA sign and verify. If you want to build the source code, you need install or build three opensoruce libraries: Libevent, cJSON and OpenSSL.

There followed explain how to work the program.

Firstly, Let's create a HTTP serveice use Libevent.

/* Create an event_base */
base = event_base_new();

/* Create a new evhttp object to handle requests. */
http_obj = evhttp_new(base);

/* Set a callback for all requests */
evhttp_set_gencb(http_obj, generic_handler, NULL);

/* Now we tell the evhttp what port to listen on */
handle = evhttp_bind_socket_with_handle(http_obj, http_addr, http_port);

/* Event dispatching loop */
event_base_dispatch(base);

The generic_handler() function is callback which to handle a http request. Its prototype like is: 

void generic_handler(struct evhttp_request *req, void *arg)

Mostly you need create a response buffer for write something, then send the response to a client. Follow code showed that how to get a remote IP from a client connection.

void generic_handler(struct evhttp_request *req, void *arg)
{
    const char *uri = evhttp_request_get_uri(req);
    printf("Got a GET request for <%s>\n",  uri);

    /* Create an evbuffer */
    struct evbuffer *buf = evbuffer_new();
    if (!buf) {
        fprintf(stderr, "failed to create response buffer \n");
        return;
    }

    /* Returns the connection object associated with the request */
    struct evhttp_connection *evcon = evhttp_request_get_connection(req);
    if (evcon) {
        char* addr = 0;
        unsigned short port = 0;

        /* Get the remote address and port associated with this connection. */
        evhttp_connection_get_peer(evcon, &addr, &port);
        evbuffer_add_printf(buf, " remote address: %s:%d\n", addr, port);
        evhttp_send_reply(req, HTTP_OK, "OK", buf);
    }
    evbuffer_free(buf);
}

Next, We want to process a request which json format send from client. The request like is :

{
    "jsonrpc" : "2.0", 
    "method" : "rsaSign", 
    "params" : {
        "hashAlg" : "SHA256",
        "privateKey" : "...",
        "plainText" : "hello world",
    }, 
    "id" : 1,
}

 Client will send the message using HTTP POST method . Follow code used to parse the request from ours service.

cJSON *req;
char* version, *method;

req = cJSON_Parse(content);

version = cJSON_GetStringValue(cJSON_GetObjectItem(js_req, "jsonrpc"));

method = cJSON_GetStringValue(cJSON_GetObjectItem(js_req, "method"));

/* ...  */

cJSON_Delete(req);

When the json message was parsed correct, the service calls proper function to handle the request.The response like is:

{
    "jsonrpc" : "2.0",
    "result"  : "...",
    "id" : 1,
}

or when a error occurs.

{
    "jsonrpc" : "2.0",
    "error" : {
        "code" : -32602,
        "message" : "Invalid params",
    },
    "id" : 1,
}

In this sample we have two RPC routines which names "rsaSign" and "rsaVerify".  The inner rsa_sign_b64() used be sign a plain text by RSA algorithm and convert the sign to base64 code.  Another rsa_verify_b64()  verifies a base64 encode of sgined.

int rsa_sign_b64(
    const char *hash_alg,     /* SHA256 or SHA1  */
    const char *private_key,  /* Private key of RSA*/
    const char *plain_text,   /* Plain text want to sign */
    int plain_length,         /* Length of the plain text */
    char *sign_text,          /* Signed text */
    int sign_length);         /* Length of signed text */

int rsa_verify_b64(
    const char *hash_alg,     /* SHA256 or SHA1  */
    const char *public_key,   /* Public key of RSA*/
    const char *sign_text,    /* Signed text */
    int sign_length,          /* Length of signed text */
    const char *plain_text,   /* Plain text for sign */
    int plain_length);        /* Length of plain text */

Finaly, We need a client to test ours serivce. So we write a python script to do this.

import requests
import json

with open('private_key.pem', 'r') as f:
    privkey = f.read()
    
with open('public_key.pem', 'r') as f:
    pubkey = f.read()

url = "http://127.0.0.1:8081"
req = {
    'jsonrpc' : '2.0',
    'method' : 'rsaSign',
    'params' : {
        'hashAlg' : 'SHA256',
        'privateKey' : privkey,
        'plainText'  : '0123456789',
    },
    'id' : 1,
}

resp = requests.post(url, json=req)
s = json.loads(resp.text)
sign = s['result']

req['method'] = 'rsaVerify'
req['params'] = {
    'hashAlg'   : 'SHA256',
    'publicKey' : pubkey,
    'signText'  : sign,
    'plainText' : '0123456789'
}

resp = requests.post(url, json=req)
s = json.loads(resp.text)
print(s['result'])

 Source code