xref: /spdk/go/rpc/clientIntegration.go (revision 640c10cedc3eb94510690010ee88c38f3cca06dc)
1/*   SPDX-License-Identifier: BSD-3-Clause
2 *   Copyright (C) 2023 Intel Corporation.
3 *   All rights reserved.
4 */
5
6package main
7
8//#cgo CFLAGS: -I../../include
9//#include "spdk/stdinc.h"
10import "C"
11import (
12	"encoding/json"
13	"github.com/spdk/spdk/go/rpc/client"
14	"log"
15	"reflect"
16	"strings"
17	"unsafe"
18)
19
20const (
21	InvalidParameterError = iota + 1
22	ConnectionError
23	JsonRpcCallError
24	InvalidResponseError
25)
26
27func main() {
28}
29
30//export spdk_gorpc_call
31func spdk_gorpc_call(jsonPtr *C.char, location *C.char) (*C.char, C.int) {
32	var jsonMap map[string]any
33	jsonStr := C.GoString(jsonPtr)
34	jsonDecoder := json.NewDecoder(strings.NewReader(jsonStr))
35	jsonDecoder.UseNumber()
36	err := jsonDecoder.Decode(&jsonMap)
37	if err != nil {
38		log.Printf("error when decoding function arguments, err: %s", err.Error())
39		return nil, InvalidParameterError
40	}
41
42	socketLocation := C.GoString(location)
43
44	rpcClient, err := client.CreateClientWithJsonCodec(client.Unix, socketLocation)
45	if err != nil {
46		log.Printf("error on client creation, err: %s", err.Error())
47		return nil, ConnectionError
48	}
49	defer rpcClient.Close()
50
51	method := jsonMap["method"].(string)
52	params := jsonMap["params"].(map[string]any)
53	// Force Go client to skip 'params' parameter in JSON-RPC call.
54	if len(params) == 0 {
55		params = nil
56	}
57
58	resp, err := rpcClient.Call(method, params)
59	if err != nil {
60		log.Printf("error on JSON-RPC call, method: %s, params: %s, err: %s", method, params, err.Error())
61		return nil, JsonRpcCallError
62	}
63
64	var respToEncode any
65	// This is a special case where inside JSON-RPC response 'Result' field is null ("result": null).
66	// Due to json tag 'omitempty' in *client.Response.Result this field is ignored during
67	// serialization when its value is null. General idea is to create a map containing values
68	// from response and serialize that map instead of a *client.Response we got from
69	// a Client.Call() method.
70	if resp.Result == nil && resp.Error == nil {
71		jsonMap := make(map[string]any)
72		// Get all *client.Response fields.
73		types := reflect.TypeOf(resp).Elem()
74		// Get all *client.Response values.
75		values := reflect.ValueOf(resp).Elem()
76		// Iterate over fields.
77		for i := 0; i < types.NumField(); i++ {
78			// Name of a field.
79			fieldName := types.Field(i).Name
80			// Name of *client.Response.Error field.
81			errorFieldName := reflect.TypeOf(resp.Error).Elem().Name()
82			// Add all fields with values to a map except *client.Response.Error
83			if fieldName != errorFieldName {
84				jsonMap[strings.ToLower(fieldName)] = values.Field(i).Interface()
85			}
86		}
87		respToEncode = jsonMap
88	} else {
89		respToEncode = resp
90	}
91
92	encodedResp, err := json.Marshal(respToEncode)
93	if err != nil {
94		log.Printf("error on creating json representation of response, err: %s", err.Error())
95		return nil, InvalidResponseError
96	}
97
98	return C.CString(string(encodedResp)), 0
99}
100
101//export spdk_gorpc_free_response
102func spdk_gorpc_free_response(p *C.char) {
103	C.free(unsafe.Pointer(p))
104}
105