jsonrpc

package module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 18, 2025 License: MIT Imports: 13 Imported by: 0

README

JSON-RPC 2.0 Library for Go

Go Version

A comprehensive, production-ready JSON-RPC 2.0 implementation for Go with support for multiple transport protocols (HTTP, TCP, UDP).

Features

  • Full JSON-RPC 2.0 Specification: Complete implementation of JSON-RPC 2.0
  • Multiple Transports: HTTP, TCP, and UDP support
  • Client & Server: Both client and server implementations
  • Context Support: Full context.Context integration for timeouts and cancellation
  • Thread-Safe: All operations are thread-safe and concurrent-friendly
  • Type Safety: Strong typing with validation and json.RawMessage for flexible parameter handling
  • Flexible Parameter Parsing: Use json.RawMessage to parse parameters as arrays or objects based on your needs
  • Error Handling: Comprehensive error handling with standard JSON-RPC error codes
  • Production Ready: Battle-tested with extensive test coverage

Quick Start

Installation
go get ella.to/[email protected]
Basic HTTP Server
package main

import (
    "context"
    "encoding/json"
    "log"

    "ella.to/jsonrpc"
)

func main() {
    // Define your RPC handlers
    handlers := map[string]jsonrpc.Handler{
        "math.add": func(ctx context.Context, req *jsonrpc.Request) *jsonrpc.Response {
            // Parse parameters from json.RawMessage
            var params map[string]any
            if err := json.Unmarshal(req.Params, &params); err != nil {
                return jsonrpc.NewErrorResponse(jsonrpc.InvalidParams, "Invalid params", nil, req.ID)
            }

            a := params["a"].(float64)
            b := params["b"].(float64)
            return jsonrpc.NewResponse(a+b, req.ID)
        },
        "greet": func(ctx context.Context, req *jsonrpc.Request) *jsonrpc.Response {
            // Alternative: Parse into a specific struct
            var params struct {
                Name string `json:"name"`
            }
            if err := json.Unmarshal(req.Params, &params); err != nil {
                return jsonrpc.NewErrorResponse(jsonrpc.InvalidParams, "Invalid params", nil, req.ID)
            }

            return jsonrpc.NewResponse("Hello, "+params.Name+"!", req.ID)
        },
    }

    // Create and start HTTP server
    server := jsonrpc.NewHttpServer("127.0.0.1:8080", "/rpc", handlers)
    log.Println("JSON-RPC server starting on http://127.0.0.1:8080/rpc")
    log.Fatal(server.ListenAndServe())
}
Basic HTTP Client
package main

import (
    "context"
    "fmt"
    "log"
    "net/http"

    "ella.to/jsonrpc"
)

func main() {
    // Create HTTP client
    httpClient := jsonrpc.NewHttpClient(&http.Client{})
    client := jsonrpc.NewClient(httpClient)
    err := client.Connect(context.Background(), "http://127.0.0.1:8080/rpc")
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    // Make a simple call
    var result float64
    err = client.CallWithResult(context.Background(), "math.add",
        map[string]any{"a": 5, "b": 3}, &result)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("5 + 3 = %.0f\n", result) // Output: 5 + 3 = 8

    // Make another call
    var greeting string
    err = client.CallWithResult(context.Background(), "greet",
        map[string]any{"name": "World"}, &greeting)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(greeting) // Output: Hello, World!
}

Parameter Handling with json.RawMessage

This library uses json.RawMessage for Request.Params and Response.Result fields, providing flexibility in how you handle parameters and results.

Benefits
  • Type Flexibility: Parse parameters as arrays, objects, or specific structs
  • Performance: Avoid unnecessary conversions when you know the expected structure
  • Validation: Check parameter structure before parsing
  • Memory Efficiency: Delay parsing until needed
Parsing Parameters
As a Map (Dynamic)
func handler(ctx context.Context, req *jsonrpc.Request) *jsonrpc.Response {
    var params map[string]any
    if err := json.Unmarshal(req.Params, &params); err != nil {
        return jsonrpc.NewErrorResponse(jsonrpc.InvalidParams, "Invalid params", nil, req.ID)
    }

    name := params["name"].(string)
    age := int(params["age"].(float64)) // JSON numbers are float64

    return jsonrpc.NewResponse(fmt.Sprintf("%s is %d years old", name, age), req.ID)
}
As a Struct (Typed)
type GreetParams struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

func handler(ctx context.Context, req *jsonrpc.Request) *jsonrpc.Response {
    var params GreetParams
    if err := json.Unmarshal(req.Params, &params); err != nil {
        return jsonrpc.NewErrorResponse(jsonrpc.InvalidParams, "Invalid params", nil, req.ID)
    }

    return jsonrpc.NewResponse(fmt.Sprintf("%s is %d years old", params.Name, params.Age), req.ID)
}
As an Array (Positional Parameters)
func mathHandler(ctx context.Context, req *jsonrpc.Request) *jsonrpc.Response {
    var params []float64
    if err := json.Unmarshal(req.Params, &params); err != nil {
        return jsonrpc.NewErrorResponse(jsonrpc.InvalidParams, "Invalid params", nil, req.ID)
    }

    if len(params) < 2 {
        return jsonrpc.NewErrorResponse(jsonrpc.InvalidParams, "Need at least 2 numbers", nil, req.ID)
    }

    return jsonrpc.NewResponse(params[0] + params[1], req.ID)
}
Checking Parameter Type Before Parsing
func flexibleHandler(ctx context.Context, req *jsonrpc.Request) *jsonrpc.Response {
    // Check if params is an array or object
    if len(req.Params) > 0 && req.Params[0] == '[' {
        // Handle array parameters
        var params []any
        if err := json.Unmarshal(req.Params, &params); err != nil {
            return jsonrpc.NewErrorResponse(jsonrpc.InvalidParams, "Invalid array params", nil, req.ID)
        }
        // Process array...
    } else {
        // Handle object parameters
        var params map[string]any
        if err := json.Unmarshal(req.Params, &params); err != nil {
            return jsonrpc.NewErrorResponse(jsonrpc.InvalidParams, "Invalid object params", nil, req.ID)
        }
        // Process object...
    }

    return jsonrpc.NewResponse("processed", req.ID)
}
Working with Results

When making client calls, results are also json.RawMessage:

// Using CallWithResult (automatic unmarshaling)
var result string
err := client.CallWithResult(ctx, "greet", params, &result)

// Using Call (manual unmarshaling)
resp, err := client.Call(ctx, "greet", params)
if err != nil {
    log.Fatal(err)
}

var result string
if err := json.Unmarshal(resp.Result, &result); err != nil {
    log.Fatal(err)
}
Migration from any to json.RawMessage

If you're migrating from a version that used any for parameters:

Before (with any)
func handler(ctx context.Context, req *jsonrpc.Request) *jsonrpc.Response {
    params := req.Params.(map[string]any)  // Direct type assertion
    name := params["name"].(string)
    return jsonrpc.NewResponse("Hello "+name, req.ID)
}
After (with json.RawMessage)
func handler(ctx context.Context, req *jsonrpc.Request) *jsonrpc.Response {
    var params map[string]any
    if err := json.Unmarshal(req.Params, &params); err != nil {
        return jsonrpc.NewErrorResponse(jsonrpc.InvalidParams, "Invalid params", nil, req.ID)
    }
    name := params["name"].(string)
    return jsonrpc.NewResponse("Hello "+name, req.ID)
}

The new approach provides:

  • Better error handling for invalid parameters
  • Type safety at parse time
  • Flexibility to parse as different types
  • No runtime panics from failed type assertions

Transport Protocols

HTTP Transport

Best for web applications and REST-like services.

// Server
server := jsonrpc.NewHttpServer("127.0.0.1:8080", "/rpc", handlers)
server.ListenAndServe()

// Client
client := jsonrpc.NewHttpClient(&http.Client{})
client.Connect(context.Background(), "http://127.0.0.1:8080/rpc")
TCP Transport

Best for persistent connections and high-performance applications.

// Server
server, err := jsonrpc.NewTcpServer("127.0.0.1:8080")
if err != nil {
    log.Fatal(err)
}
defer server.Close()

for {
    codec, err := server.Accept(context.Background())
    if err != nil {
        continue
    }

    go handleConnection(codec) // Handle each connection in a goroutine
}

// Client
client := jsonrpc.NewTcpClient()
err := client.Connect(context.Background(), "127.0.0.1:8080")
if err != nil {
    log.Fatal(err)
}
defer client.Close()
UDP Transport

Best for low-latency, stateless communication.

// Server
server, err := jsonrpc.NewUdpServer("127.0.0.1:8080")
if err != nil {
    log.Fatal(err)
}
defer server.Close()

for {
    codec, err := server.Accept(context.Background())
    if err != nil {
        continue
    }

    go handleUDPRequest(codec) // Handle each request
}

// Client
client := jsonrpc.NewUdpClient()
err := client.Connect(context.Background(), "127.0.0.1:8080")
if err != nil {
    log.Fatal(err)
}
defer client.Close()

Advanced Usage

Custom Error Handling
func mathDivide(ctx context.Context, req *jsonrpc.Request) *jsonrpc.Response {
    // Parse parameters with error handling
    var params map[string]any
    if err := json.Unmarshal(req.Params, &params); err != nil {
        return jsonrpc.NewErrorResponse(
            jsonrpc.InvalidParams,
            "Invalid parameters",
            "Parameters must be a JSON object",
            req.ID,
        )
    }

    a, ok1 := params["a"].(float64)
    b, ok2 := params["b"].(float64)

    if !ok1 || !ok2 {
        return jsonrpc.NewErrorResponse(
            jsonrpc.InvalidParams,
            "Invalid parameter types",
            "Parameters 'a' and 'b' must be numbers",
            req.ID,
        )
    }

    if b == 0 {
        return jsonrpc.NewErrorResponse(
            jsonrpc.InvalidParams,
            "Division by zero",
            "Cannot divide by zero",
            req.ID,
        )
    }

    return jsonrpc.NewResponse(a/b, req.ID)
}
Notifications (Fire-and-Forget)
// Client sends a notification (no response expected)
err := client.Notify(context.Background(), "log.info",
    map[string]any{"message": "User logged in", "userID": 123})
Batch Requests
// Send multiple requests at once
requests := []*jsonrpc.Request{
    jsonrpc.NewRequest("math.add", map[string]any{"a": 1, "b": 2}, 1),
    jsonrpc.NewRequest("math.add", map[string]any{"a": 3, "b": 4}, 2),
}

responses, err := client.CallBatch(context.Background(), requests)
if err != nil {
    log.Fatal(err)
}

for _, resp := range responses {
    fmt.Printf("ID %v: Result = %v\n", resp.ID, resp.Result)
}
Context with Timeout
// Set a timeout for operations
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

var result string
err := client.CallWithResult(ctx, "slow.operation", nil, &result)
if err != nil {
    if err == context.DeadlineExceeded {
        fmt.Println("Operation timed out")
    } else {
        log.Fatal(err)
    }
}
Middleware and Interceptors
func loggingHandler(next jsonrpc.Handler) jsonrpc.Handler {
    return func(ctx context.Context, req *jsonrpc.Request) *jsonrpc.Response {
        log.Printf("Calling method: %s", req.Method)

        result := next(ctx, req)

        log.Printf("Method %s completed", req.Method)
        return result
    }
}

// Wrap your handlers
handlers := map[string]jsonrpc.Handler{
    "greet": loggingHandler(greetHandler),
}

Error Codes

The library includes standard JSON-RPC 2.0 error codes:

const (
    ParseError     = -32700 // Invalid JSON was received
    InvalidRequest = -32600 // The JSON sent is not a valid Request object
    MethodNotFound = -32601 // The method does not exist / is not available
    InvalidParams  = -32602 // Invalid method parameter(s)
    InternalError  = -32603 // Internal JSON-RPC error
)

Configuration

HTTP Transport Options
// Custom HTTP client with timeout
httpClient := &http.Client{
    Timeout: 30 * time.Second,
    Transport: &http.Transport{
        MaxIdleConns:        100,
        IdleConnTimeout:     90 * time.Second,
        DisableCompression:  true,
    },
}

client := jsonrpc.NewHttpClient(httpClient)
UDP Transport Options
// UDP client with custom timeout
client := jsonrpc.NewUdpClientWithTimeout(10 * time.Second)

// UDP server with custom timeout
server, err := jsonrpc.NewUdpServerWithTimeout("127.0.0.1:8080", 15 * time.Second)

Testing

The library comes with comprehensive test coverage. Run tests with:

go test ./...

For verbose output:

go test -v ./...

Performance Considerations

Transport Selection
  • HTTP: Best for web integration, supports load balancers, easy debugging
  • TCP: Best for persistent connections, lower overhead than HTTP
  • UDP: Best for low-latency scenarios, but no guarantee of delivery
Connection Pooling

For HTTP clients, configure connection pooling:

transport := &http.Transport{
    MaxIdleConns:        100,
    MaxIdleConnsPerHost: 10,
    IdleConnTimeout:     90 * time.Second,
}

client := &http.Client{Transport: transport}
jsonrpcClient := jsonrpc.NewHttpClient(client)
Concurrent Connections

All transports support concurrent operations:

// Safe to use from multiple goroutines
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
    wg.Add(1)
    go func() {
        defer wg.Done()
        var result float64
        client.CallWithResult(ctx, "math.add", params, &result)
    }()
}
wg.Wait()

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Overview

Package http provides HTTP transport implementation for JSON-RPC communication.

Example Usage:

Server:

handlers := map[string]Handler{
    "greet": func(ctx context.Context, req *jsonrpc.Request) *jsonrpc.Response {
        var params map[string]any
        if err := json.Unmarshal(req.Params, &params); err != nil {
            return NewErrorResponse(jsonrpc.InvalidParams, "Invalid params", nil, req.ID)
        }
        name := params["name"].(string)
        return NewResponse("Hello "+name, req.ID)
    },
}

server := NewHttpServer("127.0.0.1:8080", "/rpc", handlers)
log.Println("Server starting on :8080/rpc")
if err := server.ListenAndServe(); err != nil {
    log.Fatal(err)
}

Client:

client := NewHttpClient(&http.Client{})
if err := client.Connect(context.Background(), "http://127.0.0.1:8080/rpc"); err != nil {
    log.Fatal(err)
}
defer client.Close()

req := &Request{
    JSONRPC: "2.0",
    Method:  "greet",
    Params:  map[string]any{"name": "World"},
    ID:      1,
}

if err := client.WriteRequest(context.Background(), req); err != nil {
    log.Fatal(err)
}

resp, err := client.ReadResponse(context.Background())
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Response: %v\n", resp.Result)

Alternative: Using the high-level Client interface:

client := NewClient(NewHttpCodec(&http.Client{}))
client.Connect(context.Background(), "http://127.0.0.1:8080/rpc")
defer client.Close()

var result string
err := client.CallWithResult(context.Background(), "greet", map[string]any{"name": "World"}, &result)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Result: %s\n", result)

Package tcp provides TCP transport implementation for JSON-RPC communication.

Example Usage:

Server:

server, err := NewTcpServer("127.0.0.1:8080")
if err != nil {
    log.Fatal(err)
}
defer server.Close()

for {
    codec, err := server.Accept(context.Background())
    if err != nil {
        log.Printf("Accept error: %v", err)
        continue
    }

    go func() {
        defer codec.Close()
        for {
            req, err := codec.ReadRequest(context.Background())
            if err != nil {
                log.Printf("Read error: %v", err)
                return
            }

            // Process request and create response
            var params map[string]any
            if err := json.Unmarshal(req.Params, &params); err != nil {
                // Handle error
                continue
            }
            resp := &Response{
                JSONRPC: "2.0",
                Result:  json.RawMessage(`"Hello ` + params["name"].(string) + `"`),
                ID:      req.ID,
            }

            if err := codec.WriteResponse(context.Background(), resp); err != nil {
                log.Printf("Write error: %v", err)
                return
            }
        }
    }()
}

Client:

client := NewTcpClient()
if err := client.Connect(context.Background(), "127.0.0.1:8080"); err != nil {
    log.Fatal(err)
}
defer client.Close()

req := &Request{
    JSONRPC: "2.0",
    Method:  "greet",
    Params:  map[string]any{"name": "World"},
    ID:      1,
}

if err := client.WriteRequest(context.Background(), req); err != nil {
    log.Fatal(err)
}

resp, err := client.ReadResponse(context.Background())
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Response: %v\n", resp.Result)

Package udp provides UDP transport implementation for JSON-RPC communication.

Example Usage:

Server:

server, err := NewUdpServer("127.0.0.1:8080")
if err != nil {
    log.Fatal(err)
}
defer server.Close()

for {
    codec, err := server.Accept(context.Background())
    if err != nil {
        log.Printf("Accept error: %v", err)
        continue
    }

    go func() {
        defer codec.Close()
        req, err := codec.ReadRequest(context.Background())
        if err != nil {
            log.Printf("Read error: %v", err)
            return
        }

        // Process request and create response
        var params map[string]any
        if err := json.Unmarshal(req.Params, &params); err != nil {
            // Handle error
            return
        }
        resp := &Response{
            JSONRPC: "2.0",
            Result:  json.RawMessage(`"Hello ` + params["name"].(string) + `"`),
            ID:      req.ID,
        }

        if err := codec.WriteResponse(context.Background(), resp); err != nil {
            log.Printf("Write error: %v", err)
        }
    }()
}

Client:

client := NewUdpClient()
if err := client.Connect(context.Background(), "127.0.0.1:8080"); err != nil {
    log.Fatal(err)
}
defer client.Close()

req := &Request{
    JSONRPC: "2.0",
    Method:  "greet",
    Params:  map[string]any{"name": "World"},
    ID:      1,
}

if err := client.WriteRequest(context.Background(), req); err != nil {
    log.Fatal(err)
}

resp, err := client.ReadResponse(context.Background())
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Response: %v\n", resp.Result)

Index

Constants

View Source
const (
	// MaxUDPPacketSize is the maximum size for UDP packets (safe size for most networks)
	MaxUDPPacketSize = 1400
	// DefaultTimeout is the default timeout for UDP operations
	DefaultTimeout = 30 * time.Second
)
View Source
const (
	ParseError     = -32700
	InvalidRequest = -32600
	MethodNotFound = -32601
	InvalidParams  = -32602
	InternalError  = -32603
)

Standard error codes as defined in JSON-RPC 2.0 specification

View Source
const Version = "2.0"

Version represents the JSON-RPC version string

Variables

View Source
var (
	// ErrHttpClientNotAvailable indicates the HTTP client is not available
	ErrHttpClientNotAvailable = errors.New("http: client not available")
	// ErrHttpResponseWriterNotAvailable indicates the HTTP response writer is not available
	ErrHttpResponseWriterNotAvailable = errors.New("http: response writer not available")
	// ErrHttpRequestNotAvailable indicates the HTTP request is not available
	ErrHttpRequestNotAvailable = errors.New("http: request not available")
	// ErrHttpNotSupported indicates an operation is not supported
	ErrHttpNotSupported = errors.New("http: operation not supported")
)
View Source
var (
	// ErrTcpConnectionClosed indicates the connection has been closed
	ErrTcpConnectionClosed = errors.New("tcp: connection closed")
	// ErrTcpInvalidLengthPrefix indicates an invalid length prefix in the message
	ErrTcpInvalidLengthPrefix = errors.New("tcp: invalid length prefix")
	// ErrTcpNotSupported indicates an operation is not supported
	ErrTcpNotSupported = errors.New("tcp: operation not supported")
)
View Source
var (
	// ErrUdpConnectionClosed indicates the connection has been closed
	ErrUdpConnectionClosed = errors.New("udp: connection closed")
	// ErrUdpNotSupported indicates an operation is not supported
	ErrUdpNotSupported = errors.New("udp: operation not supported")
	// ErrUdpMessageTooLarge indicates the message exceeds UDP packet size limits
	ErrUdpMessageTooLarge = errors.New("udp: message too large for UDP packet")
	// ErrUdpTimeout indicates a timeout occurred
	ErrUdpTimeout = errors.New("udp: operation timed out")
)

Functions

This section is empty.

Types

type BatchCall

type BatchCall struct {
	Method string
	Params any
}

BatchCall represents a single call in a batch

type BatchResponse

type BatchResponse struct {
	Response *Response
	Error    error
	Index    int
}

BatchResponse represents a response in a batch

type BufferedUdpCodec

type BufferedUdpCodec struct {
	*UdpCodec
	// contains filtered or unexported fields
}

BufferedUdpCodec wraps UdpCodec to handle the first message received during Accept

func (*BufferedUdpCodec) ReadRequest

func (c *BufferedUdpCodec) ReadRequest(ctx context.Context) (*Request, error)

ReadRequest reads the buffered request first, then normal requests

func (*BufferedUdpCodec) ReadResponse

func (c *BufferedUdpCodec) ReadResponse(ctx context.Context) (*Response, error)

ReadResponse reads the buffered response first, then normal responses

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client represents a JSON-RPC client that can make requests using any codec

func NewClient

func NewClient(codec ClientCodec) *Client

NewClient creates a new JSON-RPC client

func NewHTTPClient

func NewHTTPClient(httpClient *http.Client) *Client

NewHTTPClient creates a new HTTP JSON-RPC client

func NewSimpleClient

func NewSimpleClient(transport string) (*Client, error)

SimpleClient provides a simple way to create clients

func NewTCPClient

func NewTCPClient() *Client

NewTCPClient creates a new TCP JSON-RPC client

func (*Client) BatchCall

func (c *Client) BatchCall(ctx context.Context, calls []BatchCall) ([]BatchResponse, error)

BatchCall makes multiple calls in a single batch (for HTTP transport)

func (*Client) Call

func (c *Client) Call(ctx context.Context, method string, params any) (*Response, error)

Call makes a synchronous JSON-RPC call

func (*Client) CallWithResult

func (c *Client) CallWithResult(ctx context.Context, method string, params any, result any) error

CallWithResult makes a call and unmarshals the result into the provided variable

func (*Client) Close

func (c *Client) Close() error

Close closes the client connection

func (*Client) Connect

func (c *Client) Connect(ctx context.Context, address string) error

Connect connects the client to the specified address

func (*Client) IsConnected

func (c *Client) IsConnected() bool

IsConnected returns true if the client is connected

func (*Client) Notify

func (c *Client) Notify(ctx context.Context, method string, params any) error

Notify sends a notification (request without expecting a response)

type ClientCodec

type ClientCodec interface {
	Codec
	// Connect establishes a connection to the server
	Connect(ctx context.Context, address string) error
}

Client defines additional methods needed for client-side codec operations

type Codec

type Codec interface {
	// WriteRequest writes a JSON-RPC request to the underlying transport
	WriteRequest(ctx context.Context, req *Request) error

	// ReadRequest reads a JSON-RPC request from the underlying transport
	ReadRequest(ctx context.Context) (*Request, error)

	// WriteResponse writes a JSON-RPC response to the underlying transport
	WriteResponse(ctx context.Context, resp *Response) error

	// ReadResponse reads a JSON-RPC response from the underlying transport
	ReadResponse(ctx context.Context) (*Response, error)

	// Close closes the underlying transport connection
	Close() error

	// RemoteAddr returns the remote address if available
	RemoteAddr() string
}

type Error

type Error struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Data    any    `json:"data,omitempty"`
}

Error represents a JSON-RPC 2.0 error

func (*Error) Error

func (e *Error) Error() string

Error implements the error interface

type Handler

type Handler func(ctx context.Context, req *Request) *Response

Handler represents a function that handles JSON-RPC requests We use any to avoid circular imports - this should be cast to appropriate jsonrpc

type HttpClient

type HttpClient struct {
	*HttpCodec
	// contains filtered or unexported fields
}

Client implements Client for HTTP clients

func NewHttpClient

func NewHttpClient(client *http.Client) *HttpClient

NewClient creates a new HTTP client codec

func (*HttpClient) Connect

func (c *HttpClient) Connect(ctx context.Context, address string) error

Connect sets the server address for HTTP requests

func (*HttpClient) GetAddress

func (c *HttpClient) GetAddress() string

GetAddress returns the connected address

func (*HttpClient) IsConnected

func (c *HttpClient) IsConnected() bool

IsConnected returns true if the client has an address set

type HttpCodec

type HttpCodec struct {
	// contains filtered or unexported fields
}

Codec implements the Codec interface for HTTP connections

func NewHttpCodec

func NewHttpCodec(client *http.Client) *HttpCodec

New creates a new HTTP codec for client use

func NewRequestHttpCodec

func NewRequestHttpCodec(w http.ResponseWriter, r *http.Request) *HttpCodec

NewRequestHttpCodec creates a new HTTP codec for handling a single request/response

func (*HttpCodec) Close

func (c *HttpCodec) Close() error

Close closes the HTTP codec (no-op for HTTP)

func (*HttpCodec) ReadRequest

func (c *HttpCodec) ReadRequest(ctx context.Context) (*Request, error)

ReadRequest reads a JSON-RPC request from HTTP request body

func (*HttpCodec) ReadResponse

func (c *HttpCodec) ReadResponse(ctx context.Context) (*Response, error)

ReadResponse reads a JSON-RPC response from HTTP response

func (*HttpCodec) RemoteAddr

func (c *HttpCodec) RemoteAddr() string

RemoteAddr returns the remote address from the HTTP request

func (*HttpCodec) WriteRequest

func (c *HttpCodec) WriteRequest(ctx context.Context, req *Request) error

WriteRequest writes a JSON-RPC request via HTTP POST

func (*HttpCodec) WriteResponse

func (c *HttpCodec) WriteResponse(ctx context.Context, resp *Response) error

WriteResponse writes a JSON-RPC response via HTTP response

type HttpServer

type HttpServer struct {
	// contains filtered or unexported fields
}

HttpServer implements Server for HTTP servers

func NewHttpServer

func NewHttpServer(address, path string, handlers map[string]Handler) *HttpServer

HttpServer creates a new HTTP server codec

func (*HttpServer) Accept

func (c *HttpServer) Accept(ctx context.Context) (Codec, error)

Accept starts the HTTP server and waits for connections

func (*HttpServer) Close

func (c *HttpServer) Close() error

Close closes the HTTP server

func (*HttpServer) ListenAndServe

func (c *HttpServer) ListenAndServe() error

ListenAndServe starts the HTTP server

func (*HttpServer) ListenAndServeContext

func (c *HttpServer) ListenAndServeContext(ctx context.Context) error

ListenAndServeContext starts the HTTP server with context

func (*HttpServer) ReadRequest

func (c *HttpServer) ReadRequest(ctx context.Context) (any, error)

func (*HttpServer) ReadResponse

func (c *HttpServer) ReadResponse(ctx context.Context) (any, error)

func (*HttpServer) RemoteAddr

func (c *HttpServer) RemoteAddr() string

RemoteAddr returns the server address

func (*HttpServer) WriteRequest

func (c *HttpServer) WriteRequest(ctx context.Context, req any) error

WriteRequest, ReadRequest, WriteResponse, ReadResponse are not used in server mode

func (*HttpServer) WriteResponse

func (c *HttpServer) WriteResponse(ctx context.Context, resp any) error

type Request

type Request struct {
	JSONRPC string          `json:"jsonrpc"`
	Method  string          `json:"method"`
	Params  json.RawMessage `json:"params,omitempty"`
	ID      any             `json:"id,omitempty"`
}

Request represents a JSON-RPC 2.0 request

func NewNotification

func NewNotification(method string, params any) *Request

NewNotification creates a new JSON-RPC notification (request without ID)

func NewRequest

func NewRequest(method string, params any, id any) *Request

NewRequest creates a new JSON-RPC request

func (*Request) IsNotification

func (r *Request) IsNotification() bool

IsNotification returns true if the request is a notification (has no ID)

func (*Request) Validate

func (r *Request) Validate() error

Validate validates the request according to JSON-RPC 2.0 specification

type Response

type Response struct {
	JSONRPC string          `json:"jsonrpc"`
	Result  json.RawMessage `json:"result,omitempty"`
	Error   *Error          `json:"error,omitempty"`
	ID      any             `json:"id"`
}

Response represents a JSON-RPC 2.0 response

func NewErrorResponse

func NewErrorResponse(code int, message string, data any, id any) *Response

NewErrorResponse creates a new JSON-RPC error response

func NewResponse

func NewResponse(result any, id any) *Response

NewResponse creates a new JSON-RPC response

func (*Response) Validate

func (r *Response) Validate() error

Validate validates the response according to JSON-RPC 2.0 specification

type ServerCodec

type ServerCodec interface {
	Codec
	// Accept accepts new connections (for connection-oriented protocols)
	Accept(ctx context.Context) (Codec, error)
}

Server defines additional methods needed for server-side codec operations

type TcpClient

type TcpClient struct {
	*TcpCodec
	// contains filtered or unexported fields
}

Client implements Client for TCP clients

func NewTcpClient

func NewTcpClient() *TcpClient

NewClient creates a new TCP client codec

func (*TcpClient) Connect

func (c *TcpClient) Connect(ctx context.Context, address string) error

Connect establishes a TCP connection to the specified address

func (*TcpClient) GetAddress

func (c *TcpClient) GetAddress() string

GetAddress returns the connected address

func (*TcpClient) IsConnected

func (c *TcpClient) IsConnected() bool

IsConnected returns true if the client is connected

type TcpCodec

type TcpCodec struct {
	// contains filtered or unexported fields
}

Codec implements the Codec interface for TCP connections

func NewTcpCodec

func NewTcpCodec(conn net.Conn) *TcpCodec

New creates a new TCP codec

func (*TcpCodec) Close

func (c *TcpCodec) Close() error

Close closes the TCP connection

func (*TcpCodec) ReadRequest

func (c *TcpCodec) ReadRequest(ctx context.Context) (*Request, error)

ReadRequest reads a JSON-RPC request from the TCP connection

func (*TcpCodec) ReadResponse

func (c *TcpCodec) ReadResponse(ctx context.Context) (*Response, error)

ReadResponse reads a JSON-RPC response from the TCP connection

func (*TcpCodec) RemoteAddr

func (c *TcpCodec) RemoteAddr() string

RemoteAddr returns the remote address of the TCP connection

func (*TcpCodec) WriteRequest

func (c *TcpCodec) WriteRequest(ctx context.Context, req *Request) error

WriteRequest writes a JSON-RPC request to the TCP connection

func (*TcpCodec) WriteResponse

func (c *TcpCodec) WriteResponse(ctx context.Context, resp *Response) error

WriteResponse writes a JSON-RPC response to the TCP connection

type TcpServer

type TcpServer struct {
	// contains filtered or unexported fields
}

Server implements Server for TCP servers

func NewTcpServer

func NewTcpServer(address string) (*TcpServer, error)

NewServer creates a new TCP server codec

func (*TcpServer) Accept

func (s *TcpServer) Accept(ctx context.Context) (Codec, error)

Accept accepts a new TCP connection and returns a codec for it

func (*TcpServer) Close

func (s *TcpServer) Close() error

Close closes the TCP listener

func (*TcpServer) ReadRequest

func (s *TcpServer) ReadRequest(ctx context.Context) (*Request, error)

func (*TcpServer) ReadResponse

func (s *TcpServer) ReadResponse(ctx context.Context) (*Response, error)

func (*TcpServer) RemoteAddr

func (s *TcpServer) RemoteAddr() string

RemoteAddr returns the listener address

func (*TcpServer) WriteRequest

func (s *TcpServer) WriteRequest(ctx context.Context, req *Request) error

WriteRequest, ReadRequest, WriteResponse, ReadResponse are not used in server mode but need to be implemented to satisfy the Server interface

func (*TcpServer) WriteResponse

func (s *TcpServer) WriteResponse(ctx context.Context, resp *Response) error

type UdpClient

type UdpClient struct {
	*UdpCodec
	// contains filtered or unexported fields
}

UdpClient implements ClientCodec for UDP clients

func NewUdpClient

func NewUdpClient() *UdpClient

NewUdpClient creates a new UDP client codec

func NewUdpClientWithTimeout

func NewUdpClientWithTimeout(timeout time.Duration) *UdpClient

NewUdpClientWithTimeout creates a new UDP client codec with custom timeout

func (*UdpClient) Connect

func (c *UdpClient) Connect(ctx context.Context, address string) error

Connect establishes a UDP connection to the specified address

func (*UdpClient) GetAddress

func (c *UdpClient) GetAddress() string

GetAddress returns the connected address

func (*UdpClient) IsConnected

func (c *UdpClient) IsConnected() bool

IsConnected returns true if the client is connected

func (*UdpClient) SetTimeout

func (c *UdpClient) SetTimeout(timeout time.Duration)

SetTimeout sets the timeout for client operations

type UdpCodec

type UdpCodec struct {
	// contains filtered or unexported fields
}

UdpCodec implements the Codec interface for UDP connections

func NewUdpCodec

func NewUdpCodec(conn *net.UDPConn, remoteAddr *net.UDPAddr) *UdpCodec

NewUdpCodec creates a new UDP codec with a UDP connection

func NewUdpCodecWithTimeout

func NewUdpCodecWithTimeout(conn *net.UDPConn, remoteAddr *net.UDPAddr, timeout time.Duration) *UdpCodec

NewUdpCodecWithTimeout creates a new UDP codec with a custom timeout

func (*UdpCodec) Close

func (c *UdpCodec) Close() error

Close closes the UDP connection

func (*UdpCodec) LocalAddr

func (c *UdpCodec) LocalAddr() string

LocalAddr returns the local address of the UDP connection

func (*UdpCodec) ReadRequest

func (c *UdpCodec) ReadRequest(ctx context.Context) (*Request, error)

ReadRequest reads a JSON-RPC request from the UDP connection

func (*UdpCodec) ReadResponse

func (c *UdpCodec) ReadResponse(ctx context.Context) (*Response, error)

ReadResponse reads a JSON-RPC response from the UDP connection

func (*UdpCodec) RemoteAddr

func (c *UdpCodec) RemoteAddr() string

RemoteAddr returns the remote address of the UDP connection

func (*UdpCodec) SetTimeout

func (c *UdpCodec) SetTimeout(timeout time.Duration)

SetTimeout sets the timeout for UDP operations

func (*UdpCodec) WriteRequest

func (c *UdpCodec) WriteRequest(ctx context.Context, req *Request) error

WriteRequest writes a JSON-RPC request to the UDP connection

func (*UdpCodec) WriteResponse

func (c *UdpCodec) WriteResponse(ctx context.Context, resp *Response) error

WriteResponse writes a JSON-RPC response to the UDP connection

type UdpServer

type UdpServer struct {
	// contains filtered or unexported fields
}

UdpServer implements ServerCodec for UDP servers

func NewUdpServer

func NewUdpServer(address string) (*UdpServer, error)

NewUdpServer creates a new UDP server codec

func NewUdpServerWithTimeout

func NewUdpServerWithTimeout(address string, timeout time.Duration) (*UdpServer, error)

NewUdpServerWithTimeout creates a new UDP server codec with custom timeout

func (*UdpServer) Accept

func (s *UdpServer) Accept(ctx context.Context) (Codec, error)

Accept waits for and returns a codec for handling a UDP message Since UDP is connectionless, this creates a new codec for each message

func (*UdpServer) Close

func (s *UdpServer) Close() error

Close closes the UDP server

func (*UdpServer) ReadRequest

func (s *UdpServer) ReadRequest(ctx context.Context) (*Request, error)

func (*UdpServer) ReadResponse

func (s *UdpServer) ReadResponse(ctx context.Context) (*Response, error)

func (*UdpServer) RemoteAddr

func (s *UdpServer) RemoteAddr() string

RemoteAddr returns the server address (for UDP server, this is the local listening address)

func (*UdpServer) SetTimeout

func (s *UdpServer) SetTimeout(timeout time.Duration)

SetTimeout sets the timeout for server operations

func (*UdpServer) WriteRequest

func (s *UdpServer) WriteRequest(ctx context.Context, req *Request) error

WriteRequest, ReadRequest, WriteResponse, ReadResponse are not used in server mode but need to be implemented to satisfy the ServerCodec interface

func (*UdpServer) WriteResponse

func (s *UdpServer) WriteResponse(ctx context.Context, resp *Response) error

Directories

Path Synopsis
Package main demonstrates a simple HTTP JSON-RPC server and client.
Package main demonstrates a simple HTTP JSON-RPC server and client.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL