0

I am trying to learn how JSON-RPC works and am testing it in Go language (golang). The Go program works fine. It does what it should do. But when I try to make a raw request via telnet, it gives an error.

The working and super simple JSON-RPC server is described here:

    // rpc_json_server.go

    package main

    import (
        "log"
        "net"
        "net/http"
        "net/rpc"
        "net/rpc/jsonrpc"
    )

    //------------------------------------------------------------------------------
    // Types
    //------------------------------------------------------------------------------

    type Arithmetic int // Used as RPC Service called 'Arithmetic'

    type Arguments struct {
        A int
        B int
    }

    type Result int

    //------------------------------------------------------------------------------
    // Methods
    //------------------------------------------------------------------------------

    func (t *Arithmetic) Multiply(args *Arguments, res *Result) error {

        *res = Result(args.A * args.B)

        return nil
    }

    //------------------------------------------------------------------------------

    func main() {

        var srv *rpc.Server
        var err error
        var arith *Arithmetic
        var listener net.Listener
        var codec rpc.ServerCodec
        var srv_conntype, srv_host, srv_port, srv_addr, srv_path string
        var srv_debugPath string
        var connection net.Conn

        srv_conntype = "tcp"
        srv_host = "0.0.0.0"
        srv_port = "3000"
        srv_addr = srv_host + ":" + srv_port
        srv_path = "/"
        srv_debugPath = "/debug"

        // Create Server, register Service
        srv = rpc.NewServer()
        arith = new(Arithmetic)
        err = srv.Register(arith)
        if err != nil {
            log.Fatalf("Error. Service Format is not correct. %s\r\n", err) //dbg
        }

        // Handle, listen
        srv.HandleHTTP(srv_path, srv_debugPath)
        listener, err = net.Listen(srv_conntype, srv_addr)
        if err != nil {
            log.Fatalf("Error. Can not listen on %s. %s\r\n", srv_addr, err) //dbg
        }
        log.Printf("Started RPC Handler at %s.\r\n", srv_addr) //dbg

        // Serve
        for {

            connection, err = listener.Accept()
            if err != nil {
                log.Fatal(err)
            }

            codec = jsonrpc.NewServerCodec(connection)

            go srv.ServeCodec(codec)
        }

        err = http.Serve(listener, nil)
        if err != nil {
            log.Fatalf("Serve Error. %s\r\n", err) //dbg
        }
    }

    //------------------------------------------------------------------------------

The working and super simple JSON-RPC client's code is following:

    // rpc_json_client.go

    package main

    import (
        "fmt"
        "log"
        "net"
        "net/rpc"
        "net/rpc/jsonrpc"
    )

    //------------------------------------------------------------------------------
    // Types
    //------------------------------------------------------------------------------

    type Arithmetic int // Used as RPC Service called 'Arithmetic'

    type Arguments struct {
        A int
        B int
    }

    type Result int

    //------------------------------------------------------------------------------
    // Methods
    //------------------------------------------------------------------------------

    func main() {

        var err error
        var srv_conntype, srv_host, srv_port, srv_addr string
        //var srv_path string
        var client *rpc.Client
        var args Arguments
        var result Result
        var serviceName, methodName, funcName string
        var connection net.Conn

        srv_conntype = "tcp"
        srv_host = "0.0.0.0"
        srv_port = "3000"
        srv_addr = srv_host + ":" + srv_port
        //srv_path = "/"

        // Connect to RPC Server
        connection, err = net.Dial(srv_conntype, srv_addr)
        if err != nil {
            log.Fatalf("Error. Can not connect to %s. %s\r\n", srv_addr, err) //dbg
        }
        defer connection.Close()

        // Client
        client = jsonrpc.NewClient(connection)

        // Prepare Call
        serviceName = "Arithmetic"
        methodName = "Multiply"
        funcName = serviceName + "." + methodName
        args.A = 7
        args.B = 8

        // Call remote Procedure
        err = client.Call(funcName, args, &result)
        if err != nil {
            log.Fatalf("Error. %s\r\n", err) //dbg
        }

        // Show Results
        fmt.Printf("[%d; %d] -> [%d].\r\n", args.A, args.B, result) //dbg
    }

Once again. This golang program works fine.

But the next thing I cannot understand.

    telnet localhost 3000
    Trying 127.0.0.1...
    Connected to localhost.
    Escape character is '^]'.
    {
    "jsonrpc":"2.0", 
    "method":"Arithmetic.Multiply", 
    "params": { "A": 5, "B": 6 },
    "id":1
    }
    {"id":1,"result":null,"error":"json: cannot unmarshal object into Go value of type [1]interface {}"}

Please, give me some advice or the reason for such an error. What is wrong in the raw request?

Thanks in advance!

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
  • Current implementation of net/rpc/jsonrpc take 1 argument for struct argument. https://github.com/golang/go/blob/5fea2ccc77eb50a9704fa04b7c61755fe34e1d95/src/net/rpc/jsonrpc/server.go#L91-L97 If you want to pass named arguments, please pass as slice of struct. – mattn Jul 03 '17 at 19:26

1 Answers1

0

Your code seems fine.

But in the request, params is expected to be an array containing the actual parameters.

Try with the following payload and it should work:

{ 
  "jsonrpc":"2.0",
  "method":"Arithmetic.Multiply", 
  "params": [ { "A": 5, "B": 6 } ], 
  "id":1 
} 

(Note the "[" and "]" enclosing the actual param)

eugenioy
  • 11,825
  • 28
  • 35