A versatile and lightweight JSON-RPC 2.0 server implementation in Go, designed to handle JSON-RPC requests over TCP, Unix sockets, CGI, and HTTP. It provides a full set of helper functions to register commands, handle middlewares, and work with JSON-RPC data in a flexible and customizable way.
- Multiple transport protocols: TCP, Unix sockets, CGI, and HTTP.
- Middleware support: Easily add global or command-specific middlewares.
- Helper functions: Simplify request handling, parameter retrieval, and response management.
- Flexible configuration: Customize logging, error handling, and server options.
To install this package, use:
go get github.com/pablolagos/go-jsonrpc
package main
import (
"errors"
"log"
"os"
"github.com/pablolagos/go-jsonrpc"
)
func main() {
options := &go_jsonrpc.Options{
CGI: false,
Logger: log.New(os.Stdout, "JSON-RPC TCP Server: ", log.LstdFlags),
}
jsrpc := go_jsonrpc.New(options)
// Register a command
jsrpc.RegisterCommand("add", func(ctx *go_jsonrpc.Context) error {
a := ctx.GetParamFloat("a", 0.0)
b := ctx.GetParamFloat("b", 0.0)
result := a + b
return ctx.JSON(result)
})
// Start the server on a TCP port
address := ":12345"
log.Printf("Starting server on %s\n", address)
if err := jsrpc.StartServer(address, false); err != nil {
log.Fatalf("Failed to start server: %v\n", err)
}
}
package main
import (
"errors"
"log"
"os"
"github.com/pablolagos/go-jsonrpc"
)
func main() {
options := &go_jsonrpc.Options{
CGI: false,
Logger: log.New(os.Stdout, "JSON-RPC Unix Socket Server: ", log.LstdFlags),
}
jsrpc := go_jsonrpc.New(options)
jsrpc.UseGlobalMiddleware(func(ctx *go_jsonrpc.Context) error {
authToken := ctx.GetParamString("authToken", "")
if authToken != "secret" {
return errors.New("unauthorized access")
}
return nil
})
jsrpc.RegisterCommand("multiply", func(ctx *go_jsonrpc.Context) error {
a := ctx.GetParamFloat("a", 1.0)
b := ctx.GetParamFloat("b", 1.0)
result := a * b
ctx.JSON(result)
return nil
})
socketPath := "/tmp/jsonrpc.sock"
if _, err := os.Stat(socketPath); err == nil {
os.Remove(socketPath)
}
log.Printf("Starting server on Unix socket %s\n", socketPath)
if err := jsrpc.StartServer(socketPath, true); err != nil {
log.Fatalf("Failed to start server: %v\n", err)
}
}
When running in a CGI environment, the library can automatically handle JSON-RPC requests by reading from os.Stdin
and writing responses to os.Stdout
.
package main
import (
"log"
"os"
"github.com/pablolagos/go-jsonrpc"
)
func main() {
options := &go_jsonrpc.Options{
CGI: true, // Enables CGI headers
Logger: log.New(os.Stdout, "JSON-RPC CGI Server: ", log.LstdFlags),
}
jsrpc := go_jsonrpc.New(options)
jsrpc.RegisterCommand("subtract", func(ctx *go_jsonrpc.Context) error {
a := ctx.GetParamFloat("a", 0.0)
b := ctx.GetParamFloat("b", 0.0)
result := a - b
return ctx.JSON(result)
})
if err := jsrpc.ExecuteCommand(os.Stdin, os.Stdout); err != nil {
log.Fatalf("Error processing CGI request: %v", err)
}
}
To run the server over HTTP, use Go's net/http
package alongside the JSON-RPC library.
package main
import (
"log"
"net/http"
"github.com/pablolagos/go-jsonrpc"
)
func main() {
options := &go_jsonrpc.Options{
CGI: false,
Logger: log.New(os.Stdout, "JSON-RPC HTTP Server: ", log.LstdFlags),
}
jsrpc := go_jsonrpc.New(options)
jsrpc.RegisterCommand("divide", func(ctx *go_jsonrpc.Context) error {
a := ctx.GetParamFloat("a", 1.0)
b := ctx.GetParamFloat("b", 1.0)
if b == 0 {
return ctx.ErrorString(400,"division by zero")
}
result := a / b
return ctx.JSON(result)
})
http.HandleFunc("/rpc", func(w http.ResponseWriter, r *http.Request) {
jsrpc.ExecuteCommand(r.Body, w)
})
log.Println("Starting HTTP server on :8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatalf("Failed to start HTTP server: %v", err)
}
}
Middlewares can be applied globally or specifically for individual commands. Note: If a middleware encounters an error, it is responsible for handling client responses as needed.
package main
import (
"errors"
"log"
"os"
"github.com/pablolagos/go-jsonrpc"
)
func main() {
options := &go_jsonrpc.Options{
Logger: log.New(os.Stdout, "JSON-RPC Server with Middleware: ", log.LstdFlags),
}
jsrpc := go_jsonrpc.New(options)
jsrpc.UseGlobalMiddleware(func(ctx *go_jsonrpc.Context) error {
token := ctx.GetParamString("token", "")
if token != "valid_token" {
ctx.ErrorString(412, "unauthorized access")
return errors.New("unauthorized access") // Return an error to stop command execution
}
return nil
})
jsrpc.RegisterCommand("echo", func(ctx *go_jsonrpc.Context) error {
message := ctx.GetParamString("message", "")
return ctx.JSON(message)
})
address := ":12345"
log.Printf("Starting server on %s\n", address)
if err := jsrpc.StartServer(address, false); err != nil {
log.Fatalf("Failed to start server: %v\n", err)
}
}
See Interceptor for more details on how to use handler interceptors to modify request handling, validate requests, or force responses.
package main
import (
"fmt"
"time"
"github.com/pablolagos/go-jsonrpc/jclient"
)
func main() {
client := jclient.NewHTTPClient("https://localhost:8443/api", jclient.HTTPClientOptions{Insecure: false, Timeout: 15 * time.Second}) // true = skip TLS verify
var result map[string]interface{}
err := client.Call("ping", map[string]string{"msg": "hello"}, &result)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Response:", result)
}
This project is licensed under the MIT License - see the LICENSE file for details.