I have "hello world" rpc service written in golang. It works fine and go jsonrpc client is working. But I need to send request with curl and this example doesn't work:
curl \
-X POST \
-H "Content-Type: application/json" \
-d '{"id": 1, "method": "Test.Say", "params": [{"greet": "world"}]}' \
http://localhost:1999/_goRPC_
Go accept connection but produce absolutely no result:
curl: (52) Empty reply from server 
Here my go code:
package main
import (
  "log"
  "os"
  "time"
  "net"
  "net/rpc"
  "net/rpc/jsonrpc"
)
// RPC Api structure
type Test struct {}
// Greet method arguments
type GreetArgs struct {
  Name string
}
// Grret message accept object with single param Name
func (test *Test) Greet(args *GreetArgs, result *string) (error) {
  *result = "Hello " + args.Name
  return nil
}
// Start server with Test instance as a service
func startServer(ch chan<- bool, port string) {
  test := new(Test)
  server := rpc.NewServer()
  server.Register(test)
  listener, err := net.Listen("tcp", ":" + port)
  if err != nil {
      log.Fatal("listen error:", err)
  }
  defer listener.Close()
  for {
      conn, err := listener.Accept()
      if err != nil {
          log.Fatal(err)
      }
      go server.ServeCodec(jsonrpc.NewServerCodec(conn))
      ch <- true
  }
}
// Start client and call Test.Greet method
func startClient(port string) {
  conn, err := net.Dial("tcp", ":" + port)
  if err != nil {
      panic(err)
  }
  defer conn.Close()
  c := jsonrpc.NewClient(conn)
  var reply string
  var args = GreetArgs{"world"}
  err = c.Call("Test.Greet", args, &reply)
  if err != nil {
      log.Fatal("arith error:", err)
  }
  log.Println("Result: ", reply)
}
func main() {
  if len(os.Args) < 2 {
    log.Fatal("port not specified")
  }
  port := os.Args[1]
  ch := make(chan bool)
  go startServer(ch, port)
  time.Sleep(500 * time.Millisecond)
  go startClient(port)
  // Produce log message each time connection closes
  for {
    <-ch
    log.Println("Closed")
  }
}
                All the functionalities of grpc-curl are also accessible through the NuGet package DynamicGrpc that is part of this repository. This tool is the .NET equivalent of the popular gRPCurl written in Golang. NOTE: grpc-curl doesn't not support yet all the features that gRPCurl is providing.
So, you can't call jsonrpc with curl. If you really want to do that, you can make a HTTP handler that adapts the HTTP request/response to a ServerCodec. For example:
Golang registers rpc service with an Object with a method that satisfies following conditions: 1- The method is exported. 2- The method has two arguments, both exported (or builtin) types. 3- The method's second argument is a pointer. 4- The method has return type error. Name of the service is object’s type. Let’s define such object.
Gorilla kit has rpc package to simplify default net/rpc/jsonrpc package. Slight difference form standard golang net/rpc is that it requires method signature to accept *Request object as first argument and changes Args parameter to pointer *Args. In net/rpc our Multiply method looks like func (t *Arith) Multiply (args Args, result *Result) error.
The jsonrpc package doesn't support json-rpc over HTTP currently. So, you can't call jsonrpc with curl. If you really want to do that, you can make a HTTP handler that adapts the HTTP request/response to a ServerCodec. For example:
package main
import (
    "io"
    "log"
    "net"
    "net/http"
    "net/rpc"
    "net/rpc/jsonrpc"
    "os"
)
type HttpConn struct {
    in  io.Reader
    out io.Writer
}
func (c *HttpConn) Read(p []byte) (n int, err error)  { return c.in.Read(p) }
func (c *HttpConn) Write(d []byte) (n int, err error) { return c.out.Write(d) }
func (c *HttpConn) Close() error                      { return nil }
// RPC Api structure
type Test struct{}
// Greet method arguments
type GreetArgs struct {
    Name string
}
// Grret message accept object with single param Name
func (test *Test) Greet(args *GreetArgs, result *string) error {
    *result = "Hello " + args.Name
    return nil
}
// Start server with Test instance as a service
func startServer(port string) {
    test := new(Test)
    server := rpc.NewServer()
    server.Register(test)
    listener, err := net.Listen("tcp", ":"+port)
    if err != nil {
        log.Fatal("listen error:", err)
    }
    defer listener.Close()
    http.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if r.URL.Path == "/test" {
            serverCodec := jsonrpc.NewServerCodec(&HttpConn{in: r.Body, out: w})
            w.Header().Set("Content-type", "application/json")
            w.WriteHeader(200)
            err := server.ServeRequest(serverCodec)
            if err != nil {
                log.Printf("Error while serving JSON request: %v", err)
                http.Error(w, "Error while serving JSON request, details have been logged.", 500)
                return
            }
        }
    }))
}
func main() {
    if len(os.Args) < 2 {
        log.Fatal("port not specified")
    }
    port := os.Args[1]
    startServer(port)
}
Now you can call it with curl -X POST -H "Content-Type: application/json" -d '{"id": 1, "method": "Test.Greet", "params": [{"name":"world"}]}' http://localhost:port/test
Part of the code is from this post
@jfly has a nifty solution.
Another option, if you still wanted to test with something besides the go jsonrpc cient (probably the easiest option), or use @jfly's answer, is you can use telnet to send raw data:
computer:~ User$ telnet 127.0.0.1 8888
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
{"method":"Test.Greet","params":[{"Name":"world"}],"id":0}
{"id":0,"result":"Hello world","error":null}
{"method":"Test.Greet","params":[{"Name":"world"}],"id":0}
{"id":0,"result":"Hello world","error":null}
{"method":"Test.Greet","params":[{"Name":"world"}],"id":0}
{"id":0,"result":"Hello world","error":null}
The above is the output including payload I typed in and your server's responses.
tcpdump was my friend when I was figuring out the right payload to send.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With