### Example: Using ServerInfo Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/types.md Demonstrates how to retrieve and log server information, such as available methods and the server's start time. ```go info := srv.ServerInfo() log.Printf("Methods: %v", info.Methods) log.Printf("Start time: %v", info.StartTime) ``` -------------------------------- ### Getting Started Commands Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/README.md Commands to read various documentation files for the jrpc2 project, including index, quick reference, API details, configuration, error handling, and type references. ```bash # Read the index first cat INDEX.md ``` ```bash # For quick reference cat quick-reference.md ``` ```bash # For API details cat api-reference-server.md cat api-reference-client.md cat api-reference-handler.md ``` ```bash # For configuration cat configuration.md ``` ```bash # For error handling cat errors.md ``` ```bash # For complete type reference cat types.md ``` -------------------------------- ### Example: Using ErrorCode() Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/types.md Demonstrates how to use the ErrorCode function to get the appropriate error code for a given error and log it. ```go code := jrpc2.ErrorCode(err) log.Printf("Error code: %d", code) ``` -------------------------------- ### Basic HTTP Server Setup Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-jhttp.md Sets up a basic HTTP server using jhttp.NewBridge to expose JRPC2 handlers. This example demonstrates how to serve JSON-RPC requests over HTTP. ```go package main import ( "context" "net/http" "github.com/creachadair/jrpc2" "github.com/creachadair/jrpc2/handler" "github.com/creachadair/jrpc2/jhttp" "github.com/creachadair/jrpc2/server" ) func Add(ctx context.Context, args []int) int { sum := 0 for _, v := range args { sum += v } return sum } func main() { assigner := handler.Map{ "Add": handler.New(Add), } local := server.NewLocal(assigner, nil) defer local.Close() http.Handle("/rpc", jhttp.NewBridge(&local, nil)) http.ListenAndServe(":8080", nil) } ``` -------------------------------- ### Create a New Server Instance Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-server.md Instantiate a new jrpc2 server with a given assigner and optional configuration. The server is not started until Start() is called. ```go import ( "context" "github.com/creachadair/jrpc2" "github.com/creachadair/jrpc2/channel" "github.com/creachadair/jrpc2/handler" ) func Add(ctx context.Context, values []int) int { sum := 0 for _, v := range values { sum += v } return sum } assigner := handler.Map{ "Add": handler.New(Add), } srv := jrpc2.NewServer(assigner, nil) ``` -------------------------------- ### jrpc2 Server Usage Example Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/quick-reference.md Shows how to send a JSON-RPC request to the basic server example. This demonstrates the expected input format for the 'Add' method. ```bash $ echo '{"jsonrpc":"2.0","id":1,"method":"Add","params":[1,2,3]}' | server {"jsonrpc":"2.0","id":1,"result":6} ``` -------------------------------- ### Example: Configure and Wrap a Handler Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-handler.md Demonstrates chaining configuration options like SetStrict and AllowArray before wrapping the handler for use with jrpc2. ```go fi, _ := handler.Check(Add) h := fi.SetStrict(true).AllowArray(false).Wrap() ``` -------------------------------- ### Basic HTTP Server Example Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-jhttp.md Demonstrates how to set up a basic HTTP server using jhttp to expose JSON-RPC 2.0 methods. ```APIDOC ## Basic HTTP Server This example shows how to create an HTTP server that exposes JSON-RPC 2.0 methods. ### Usage 1. Define your JSON-RPC methods (e.g., `Add` function). 2. Create a `handler.Map` to register your methods. 3. Initialize a `server.Local` instance with the handler map. 4. Create an HTTP handler using `jhttp.NewBridge`. 5. Start the HTTP server using `http.ListenAndServe`. ```go package main import ( "context" "net/http" "github.com/creachadair/jrpc2" "github.com/creachadair/jrpc2/handler" "github.com/creachadair/jrpc2/jhttp" "github.com/creachadair/jrpc2/server" ) func Add(ctx context.Context, args []int) int { sum := 0 for _, v := range args { sum += v } return sum } func main() { assigner := handler.Map{ "Add": handler.New(Add), } local := server.NewLocal(assigner, nil) defer local.Close() http.Handle("/rpc", jhttp.NewBridge(&local, nil)) http.ListenAndServe(":8080", nil) } ``` **Client Usage Example (curl):** ```bash curl -X POST http://localhost:8080/rpc \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"Add","params":[1,2,3]}' ``` ``` -------------------------------- ### Manually Set Server Start Time Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/configuration.md Sets the StartTime option to a specific time. Useful for testing or ensuring multiple servers share a consistent start time. ```go startTime := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC) opts := &jrpc2.ServerOptions{ StartTime: startTime, } ``` -------------------------------- ### Example: Configuring Local Pair Options Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-server-package.md Demonstrates how to create LocalOptions to customize client and server behavior, such as setting loggers, enabling push notifications, and adjusting concurrency. ```go import ( "log" "os" "github.com/creachadair/jrpc2" "github.com/creachadair/jrpc2/server" ) opts := &server.LocalOptions{ Client: &jrpc2.ClientOptions{ Logger: jrpc2.StdLogger( log.New(os.Stderr, "[client] ", 0), ), }, Server: &jrpc2.ServerOptions{ Logger: jrpc2.StdLogger( log.New(os.Stderr, "[server] ", 0), ), AllowPush: true, Concurrency: 2, }, } local := server.NewLocal(assigner, opts) ``` -------------------------------- ### Example: Using Error.WithData() Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/types.md Demonstrates how to create an error and attach additional data using the WithData method. ```go err := &jrpc2.Error{Code: jrpc2.InvalidParams, Message: "invalid"} err = err.WithData(map[string]string{"field": "name", "reason": "too long"}) ``` -------------------------------- ### Example: Using Errorf() Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/types.md Shows how to use the Errorf function to create and return a formatted error for invalid parameters. ```go return nil, jrpc2.Errorf(jrpc2.InvalidParams, "invalid parameter: %s", name) ``` -------------------------------- ### Example: Basic Add Functionality with Local Pair Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-server-package.md Demonstrates how to set up a Local client/server pair and make a remote procedure call. The Add function is registered with the server and called by the client. ```go import ( "context" "log" "github.com/creachadair/jrpc2" "github.com/creachadair/jrpc2/handler" "github.com/creachadair/jrpc2/server" ) func Add(ctx context.Context, values []int) int { sum := 0 for _, v := range values { sum += v } return sum } // In tests or main func main() { assigner := handler.Map{ "Add": handler.New(Add), } local := server.NewLocal(assigner, nil) defer local.Close() var result int err := local.Client.CallResult( context.Background(), "Add", []int{1, 2, 3}, &result, ) if err != nil { log.Fatal(err) } log.Printf("Result: %d", result) } ``` -------------------------------- ### Custom GET Request Parser Example Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-jhttp.md Illustrates how to configure custom parsing for GET requests when using jhttp.NewBridge. ```APIDOC ## Custom GET Request Parser This example demonstrates how to provide a custom function to `jhttp.NewBridge` to handle the parsing of GET requests. ### Usage 1. Define a `jhttp.BridgeOptions` struct. 2. Implement the `ParseGETRequest` field with a function that extracts method and parameters from the request's query string. 3. Pass the options struct to `jhttp.NewBridge`. ```go opts := &jhttp.BridgeOptions{ ParseGETRequest: func(w http.ResponseWriter, req *http.Request) { method := req.URL.Query().Get("method") params := req.URL.Query().Get("params") // Parse params as JSON and construct request // Then handle through bridge }, } bridge := jhttp.NewBridge(&local, opts) ``` ``` -------------------------------- ### NewServer Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-server.md Creates a new jrpc2 Server instance. It requires an Assigner to map method names to handler functions and accepts optional ServerOptions for configuration. The server is not started until Start() is called. ```APIDOC ## NewServer ### Description Creates a new jrpc2 Server instance. It requires an Assigner to map method names to handler functions and accepts optional ServerOptions for configuration. The server is not started until Start() is called. ### Signature ```go func NewServer(mux Assigner, opts *ServerOptions) *Server ``` ### Parameters #### Path Parameters - **mux** (Assigner) - Required - Maps method names to handler functions - **opts** (*ServerOptions) - Optional - Server configuration options ### Returns - ***Server** - A new unstarted server instance. ### Panics If mux is nil. ### Notes The server does not start until `Start()` is called. It is not safe to modify mux after the server starts unless mux itself is thread-safe. ### Example ```go import ( "context" "github.com/creachadair/jrpc2" "github.com/creachadair/jrpc2/channel" "github.com/creachadair/jrpc2/handler" ) func Add(ctx context.Context, values []int) int { sum := 0 for _, v := range values { sum += v } return sum } assigner := handler.Map{ "Add": handler.New(Add), } srv := jrpc2.NewServer(assigner, nil) ``` ``` -------------------------------- ### MethodNotFound Example Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/errors.md Shows a client request for a non-existent method and the corresponding server response. ```json {"jsonrpc":"2.0","id":1,"method":"NonExistent"} ``` ```json { "jsonrpc": "2.0", "id": 1, "error": { "code": -32601, "message": "method not found", "data": "NonExistent" } } ``` -------------------------------- ### Start Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-server.md Starts the jrpc2 Server, enabling it to communicate with clients over the provided channel. This method is non-blocking and returns immediately, with the server running in background goroutines. Use Wait() to block until shutdown. ```APIDOC ## Start ### Description Starts the jrpc2 Server, enabling it to communicate with clients over the provided channel. This method is non-blocking and returns immediately, with the server running in background goroutines. Use Wait() to block until shutdown. ### Signature ```go func (s *Server) Start(c channel.Channel) *Server ``` ### Parameters #### Path Parameters - **c** (channel.Channel) - Required - Channel to communicate with client ### Returns - ***Server** - The server itself (for method chaining) ### Panics If the server is already running. ### Notes Start does not block. Returns immediately while the server runs in background goroutines. Call `Wait()` to block until shutdown. ### Example ```go ch := channel.Line(os.Stdin, os.Stdout) srv := jrpc2.NewServer(assigner, nil).Start(ch) err := srv.Wait() ``` ``` -------------------------------- ### HTTP Client Example Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-jhttp.md Demonstrates how to create an HTTP client using jhttp to call remote JSON-RPC 2.0 services. ```APIDOC ## HTTP Client This example shows how to create an HTTP client to communicate with a JSON-RPC 2.0 service exposed over HTTP. ### Usage 1. Create a communication channel using `jhttp.NewChannel` pointing to the RPC endpoint. 2. Initialize a `jrpc2.Client` with the channel. 3. Use client methods like `CallResult` to invoke remote procedures. ```go package main import ( "context" "log" "github.com/creachadair/jrpc2" "github.com/creachadair/jrpc2/jhttp" ) func main() { ch := jhttp.NewChannel("http://localhost:8080/rpc", nil) cli := jrpc2.NewClient(ch, nil) defer cli.Close() var result int err := cli.CallResult(context.Background(), "Add", []int{1, 2, 3}, &result) if err != nil { log.Fatal(err) } log.Printf("Result: %d", result) } ``` ``` -------------------------------- ### Get Server Information Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-server.md Retrieve an atomic snapshot of the server's current state, including its methods, metrics, and startup time. ```go serverInfo := srv.ServerInfo() ``` -------------------------------- ### Example: Using ParsedRequest.ToRequest() Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/types.md Illustrates iterating through parsed requests and converting valid ones to server Requests for further processing. ```go reqs, _ := jrpc2.ParseRequests(data) for _, parsed := range reqs { if req := parsed.ToRequest(); req != nil { // Use req... } } ``` -------------------------------- ### Example: Handling Positional Parameters with Args Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-handler.md Shows how to use the Args wrapper to unmarshal positional parameters from a request into separate variables (x, y, s). ```go func Handler(ctx context.Context, req *jrpc2.Request) (any, error) { var x, y int var s string if err := req.UnmarshalParams(&handler.Args{&x, &y, &s}); err != nil { return nil, err } // x, y, s are populated from array elements return x + y, nil } ``` -------------------------------- ### ParseError Example Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/errors.md Demonstrates a server response to a request with invalid JSON syntax. ```json {"jsonrpc":"2.0","id":1,"method":"test","params":invalid} ``` ```json { "jsonrpc": "2.0", "id": null, "error": { "code": -32700, "message": "parse error" } } ``` -------------------------------- ### SystemError Example Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/errors.md Shows how a Go error that doesn't implement ErrCoder is wrapped as a SystemError. ```go func FileHandler(ctx context.Context, req *jrpc2.Request) (any, error) { return ioutil.ReadFile("/nonexistent") // os.ErrNotExist becomes SystemError } ``` -------------------------------- ### InvalidRequest Examples Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/errors.md Illustrates various malformed requests that result in an InvalidRequest error. ```go {"jsonrpc":"2.0","id":[],"method":"test"} ``` ```go {"jsonrpc":"2.0","id":1,"method":"test","unknown":"field"} ``` ```go [] ``` -------------------------------- ### Create ServiceMap Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-handler.md Example of creating a handler.ServiceMap to organize handlers into services like 'Math' and 'Status'. Clients call methods using the 'Service.Method' format. ```go assigner := handler.ServiceMap{ "Math": handler.Map{ "Add": handler.New(Add), "Mul": handler.New(Mul), }, "Status": handler.Map{ "Get": handler.New(GetStatus), }, } // Clients call "Math.Add", "Math.Mul", "Status.Get" srv := jrpc2.NewServer(assigner, nil) ``` -------------------------------- ### InternalError Handler Panic Example Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/errors.md Demonstrates a handler function that panics, leading to an InternalError. ```go func BadHandler(ctx context.Context, req *jrpc2.Request) (any, error) { panic("something went wrong") } ``` -------------------------------- ### Create New Local Server/Client Pair Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-server-package.md Creates and initializes a new Local client/server pair. The server starts automatically and both client and server are immediately ready for use. Call Close() to shut down the pair. ```go func NewLocal(assigner jrpc2.Assigner, opts *LocalOptions) Local ``` -------------------------------- ### Start the Server on a Channel Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-server.md Begin serving requests on the provided channel. This method returns immediately, and the server runs in background goroutines. Use Wait() to block until shutdown. ```go ch := channel.Line(os.Stdin, os.Stdout) srv := jrpc2.NewServer(assigner, nil).Start(ch) err := srv.Wait() ``` -------------------------------- ### Example: Handling Object Parameters with Obj Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-handler.md Demonstrates using the Obj wrapper to unmarshal named parameters ('x', 'y') from a request into specific variables. ```go func Handler(ctx context.Context, req *jrpc2.Request) (any, error) { var x, y int if err := req.UnmarshalParams(&handler.Obj{ "x": &x, "y": &y, }); err != nil { return nil, err } return x + y, nil } // Client calls: {method: "Add", params: {x: 10, y: 20}} ``` -------------------------------- ### Example: Using Code.Err() Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/types.md Demonstrates how to use the Err method of the Code type to check for specific JSON-RPC errors and log them. ```go if err := jrpc2.MethodNotFound.Err(); err != nil { log.Printf("Error: %v", err) } ``` -------------------------------- ### ServerInfo Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-server.md Retrieves current server information, including methods, metrics, and start time, as an atomic snapshot. ```APIDOC ## ServerInfo ### Description Retrieves current server information, including methods, metrics, and start time, as an atomic snapshot. ### Signature ```go func (s *Server) ServerInfo() *ServerInfo ``` ### Returns - ** *ServerInfo** - Current server information ### Notes Returns atomic snapshot of server state including methods, metrics, and start time. ``` -------------------------------- ### Cancelled Error Example Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/errors.md Illustrates how a cancelled context on the client side results in a Cancelled error. ```go ctx, cancel := context.WithCancel(context.Background()) cancel() _, err := cli.Call(ctx, "SlowMethod", nil) // err will be Cancelled ``` -------------------------------- ### Batch Request Error Handling Example in Go Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/errors.md Shows how to construct a batch of JRPC2 requests, including some that are expected to fail, and how to process the responses. ```go batch := []jrpc2.Spec{ {Method: "Valid", Params: []int{1, 2}}, // OK {Method: "BadParams", Params: "string"}, // InvalidParams {Method: "NotFound", Params: nil}, // MethodNotFound } rsps, err := cli.Batch(ctx, batch) // rsps contains 3 responses; check each for errors ``` -------------------------------- ### Create Handler Map Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-handler.md Example of creating a handler.Map to register methods like Add and Mul. This map can then be used to initialize a jrpc2.Server. ```go math := handler.Map{ "Add": handler.New(Add), "Mul": handler.New(Mul), } srv := jrpc2.NewServer(math, nil) ``` -------------------------------- ### DeadlineExceeded Error Example Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/errors.md Demonstrates how a request exceeding its context deadline results in a DeadlineExceeded error. ```go ctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond) defer cancel() _, err := cli.Call(ctx, "SlowMethod", nil) // err will be DeadlineExceeded ``` -------------------------------- ### Implement Custom RPCLogger Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/types.md Provides an example implementation of the RPCLogger interface to log incoming requests and outgoing responses. This demonstrates how to capture and format request/response data. ```go type MyRPCLogger struct{} func (l *MyRPCLogger) LogRequest(ctx context.Context, req *jrpc2.Request) { log.Printf(">>> %s %s", req.Method(), req.ParamString()) } func (l *MyRPCLogger) LogResponse(ctx context.Context, rsp *jrpc2.Response) { if err := rsp.Error(); err != nil { log.Printf("<<< %s (error: %v)", rsp.ID(), err) } else { log.Printf("<<< %s %s", rsp.ID(), rsp.ResultString()) } } opts := &jrpc2.ServerOptions{ RPCLog: &MyRPCLogger{}, } ``` -------------------------------- ### Example: Deferring Close for Local Pair Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-server-package.md Shows the typical pattern of creating a Local pair and deferring its closure to ensure resources are released. ```go local := server.NewLocal(assigner, nil) defer local.Close() // Use local.Client and local.Server... ``` -------------------------------- ### Basic HTTP Server Usage Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-jhttp.md Example cURL command to send a JSON-RPC request to the basic HTTP server. ```bash curl -X POST http://localhost:8080/rpc \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"Add","params":[1,2,3]}' ``` -------------------------------- ### HTTP Client Setup Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-jhttp.md Sets up an HTTP client using jhttp.NewChannel to connect to a remote JRPC2 HTTP endpoint. This allows making JSON-RPC calls from a client application. ```go package main import ( "context" "log" "github.com/creachadair/jrpc2" "github.com/creachadair/jrpc2/jhttp" ) func main() { ch := jhttp.NewChannel("http://localhost:8080/rpc", nil) cli := jrpc2.NewClient(ch, nil) defer cli.Close() var result int err := cli.CallResult(context.Background(), "Add", []int{1, 2, 3}, &result) if err != nil { log.Fatal(err) } log.Printf("Result: %d", result) } ``` -------------------------------- ### InvalidParams Example Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/errors.md Illustrates a request with parameters that cannot be unmarshaled into the expected type, resulting in an InvalidParams error. ```go request := `{"jsonrpc":"2.0","id":1,"method":"Add","params":{"x":"not a number"}}` ``` ```json { "jsonrpc": "2.0", "id": 1, "error": { "code": -32602, "message": "invalid parameters", "data": "json: cannot unmarshal string into Go value of type int" } } ``` -------------------------------- ### jrpc2 Client Batch Request Example Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/quick-reference.md Shows how to send multiple JSON-RPC requests concurrently using the `Batch` method on a jrpc2 client. It also demonstrates iterating over the responses and handling individual errors. ```go rsps, err := cli.Batch(ctx, []jrpc2.Spec{ {Method: "Add", Params: []int{1, 2, 3}}, {Method: "Mul", Params: []int{4, 5, 6}}, {Method: "Alert", Params: "Fire!", Notify: true}, }) for i, rsp := range rsps { if err := rsp.Error(); err != nil { log.Printf("Request %d error: %v", i, err) } else { var result int rsp.UnmarshalResult(&result) log.Printf("Request %d result: %d", i, result) } } ``` -------------------------------- ### Implement EchoChannel for Testing Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-channel.md An example implementation of a custom channel that echoes sent messages. Useful for testing channel logic without a real network transport. ```go type EchoChannel struct { messages [][]byte index int } func (e *EchoChannel) Send(msg []byte) error { e.messages = append(e.messages, append([]byte{}, msg...)) return nil } func (e *EchoChannel) Recv() ([]byte, error) { if e.index >= len(e.messages) { return nil, io.EOF } msg := e.messages[e.index] e.index++ return msg, nil } func (e *EchoChannel) Close() error { return nil } ``` -------------------------------- ### Define ServerInfo Struct Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/types.md The ServerInfo struct describes the current state and capabilities of a server, including available methods, metrics, and its start time. ```go type ServerInfo struct { Methods []string Metrics map[string]any StartTime time.Time } ``` -------------------------------- ### TCP Server Transport Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/quick-reference.md Set up a TCP server to listen for incoming connections. After accepting a connection, create a jrpc2 channel and start the server. ```go import "net" listener, _ := net.Listen("tcp", ":8080") conn, _ := listener.Accept() ch := channel.Line(conn, conn) srv := jrpc2.NewServer(assigner, nil).Start(ch) ``` -------------------------------- ### Testing Example: Multiply Function with Local Pair Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-server-package.md Provides a comprehensive test case for a Multiply function using the server package's Local type. It sets up a Local pair, defines test cases, and asserts the results of remote calls. ```go package mypackage import ( "context" "testing" "github.com/creachadair/jrpc2" "github.com/creachadair/jrpc2/handler" "github.com/creachadair/jrpc2/server" ) func Multiply(ctx context.Context, args []int) int { result := 1 for _, v := range args { result *= v } return result } func TestMultiply(t *testing.T) { assigner := handler.Map{ "Multiply": handler.New(Multiply), } local := server.NewLocal(assigner, nil) defer local.Close() ctx := context.Background() tests := []struct { name string args []int want int }{ {"basic", []int{2, 3}, 6}, {"single", []int{5}, 5}, {"zero", []int{2, 0, 3}, 0}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var result int err := local.Client.CallResult(ctx, "Multiply", tt.args, &result) if err != nil { t.Fatalf("Call failed: %v", err) } if result != tt.want { t.Errorf("got %d, want %d", result, tt.want) } }) } } ``` -------------------------------- ### NewLocal Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-server-package.md Creates a new Local client/server pair connected by an in-memory pipe. This is useful for testing server implementations without network overhead. The server starts automatically and both client and server are ready immediately. ```APIDOC ## NewLocal ### Description Creates a new Local client/server pair connected by an in-memory pipe. This is useful for testing server implementations without network overhead. The server starts automatically and both client and server are ready immediately. ### Method func ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Returns `Local` - Client and server connected via in-memory pipe ### Notes: - Server is started automatically - Both client and server are ready to use immediately - Call `Close()` to shut down ### Example: ```go import ( "context" "log" "github.com/creachadair/jrpc2" "github.com/creachadair/jrpc2/handler" "github.com/creachadair/jrpc2/server" ) func Add(ctx context.Context, values []int) int { sum := 0 for _, v := range values { sum += v } return sum } // In tests or main func main() { assigner := handler.Map{ "Add": handler.New(Add), } local := server.NewLocal(assigner, nil) defer local.Close() var result int err := local.Client.CallResult( context.Background(), "Add", []int{1, 2, 3}, &result, ) if err != nil { log.Fatal(err) } log.Printf("Result: %d", result) } ``` ``` -------------------------------- ### Custom ParseGETRequest Function Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/configuration.md Example of implementing a custom ParseGETRequest function for BridgeOptions to handle GET requests. ```go opts := &jhttp.BridgeOptions{ ParseGETRequest: func(w http.ResponseWriter, req *http.Request) { method := req.URL.Query().Get("method") params := req.URL.Query().Get("params") // Parse and handle request // Write response }, } ``` -------------------------------- ### Get Server from Context Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/utilities-and-helpers.md Retrieves the jrpc2 server instance associated with the current handler context. This function should not be called outside of a handler context, as it will panic. Handlers must not call `Wait()` or `WaitStatus()` on the retrieved server to avoid deadlocks. ```go func StatusHandler(ctx context.Context, req *jrpc2.Request) (any, error) { srv := jrpc2.ServerFromContext(ctx) info := srv.ServerInfo() return map[string]any{ "methods": info.Methods, "started": info.StartTime, }, nil } ``` -------------------------------- ### Default Server Options Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/quick-reference.md Use default minimal server options. ```go // Default (minimal) opts := &jrpc2.ServerOptions{} ``` ```go // or opts := (*jrpc2.ServerOptions)(nil) ``` -------------------------------- ### Custom GET Request Parser Options Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-jhttp.md Configures custom options for the jhttp.Bridge, specifically for handling GET requests by defining a ParseGETRequest function. ```go opts := &jhttp.BridgeOptions{ ParseGETRequest: func(w http.ResponseWriter, req *http.Request) { method := req.URL.Query().Get("method") params := req.URL.Query().Get("params") // Parse params as JSON and construct request // Then handle through bridge }, } bridge := jhttp.NewBridge(&local, opts) ``` -------------------------------- ### Server Options with Logging Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/quick-reference.md Configure server options to include standard logging. ```go // With logging opts := &jrpc2.ServerOptions{ Logger: jrpc2.StdLogger(log.New(os.Stderr, "[srv] ", 0)), } ``` -------------------------------- ### Default Client Options Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/quick-reference.md Use default client options. ```go // Default opts := &jrpc2.ClientOptions{} ``` -------------------------------- ### Client Options with Logging Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/quick-reference.md Configure client options to include standard logging. ```go // With logging opts := &jrpc2.ClientOptions{ Logger: jrpc2.StdLogger(log.New(os.Stderr, "[cli] ", 0)), } ``` -------------------------------- ### Create a Local Server for Testing Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/INDEX.md Use `server.NewLocal` to create an in-memory server instance for integration testing. Ensure to close the local server when done. ```go local := server.NewLocal(assigner, opts) defer local.Close() // Use local.Client and local.Server ``` -------------------------------- ### Request.Method Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-server.md Gets the method name associated with the JRPC2 request. ```APIDOC ## Request.Method ### Description Gets the method name associated with the JRPC2 request. ### Signature ```go func (r *Request) Method() string ``` ### Returns - **string** - The method name for the request ``` -------------------------------- ### Get Response ID Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-client.md Retrieves the request identifier for the response. ```go func (r *Response) ID() string ``` -------------------------------- ### Basic jrpc2 Server Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/quick-reference.md Sets up a basic jrpc2 server that listens for incoming requests on standard input/output and handles an 'Add' method. This is useful for simple RPC services. ```go package main import ( "context" "log" "os" "github.com/creachadair/jrpc2" "github.com/creachadair/jrpc2/channel" "github.com/creachadair/jrpc2/handler" ) // Handler function func Add(ctx context.Context, values []int) int { sum := 0 for _, v := range values { sum += v } return sum } func main() { // Create assigner with handlers assigner := handler.Map{ "Add": handler.New(Add), } // Create and start server srv := jrpc2.NewServer(assigner, nil) ch := channel.Line(os.Stdin, os.Stdout) srv.Start(ch) // Wait for shutdown log.Fatal(srv.Wait()) } ``` -------------------------------- ### Simple Server with Logging Configuration Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/configuration.md Configures a jrpc2 server with standard logging, allowing push notifications, and setting concurrency. ```go import ( "log" "os" "github.com/creachadair/jrpc2" ) opts := &jrpc2.ServerOptions{ Logger: jrpc2.StdLogger(log.New(os.Stderr, "[server] ", log.LstdFlags)), AllowPush: true, Concurrency: 4, } srv := jrpc2.NewServer(assigner, opts) ``` -------------------------------- ### Define Local Server/Client Options Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/configuration.md Configure both client and server options for a local jrpc2 connection pair. ```go opts := &server.LocalOptions{ Client: &jrpc2.ClientOptions{ Logger: jrpc2.StdLogger(log.New(os.Stderr, "[cli] ", 0)), }, Server: &jrpc2.ServerOptions{ Logger: jrpc2.StdLogger(log.New(os.Stderr, "[srv] ", 0)), AllowPush: true, Concurrency: 4, }, } local := server.NewLocal(assigner, opts) ``` -------------------------------- ### Custom ParseRequest Function Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/configuration.md Example of implementing a custom ParseRequest function for BridgeOptions to handle request body parsing. ```go opts := &jhttp.BridgeOptions{ ParseRequest: func(req *http.Request) ([]*jrpc2.ParsedRequest, error) { // Custom parsing logic data, _ := io.ReadAll(req.Body) return jrpc2.ParseRequests(data) }, } ``` -------------------------------- ### Server Options with RPC Logging Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/quick-reference.md Configure server options to use a custom RPC logger. ```go // With RPC logging opts := &jrpc2.ServerOptions{ RPCLog: &MyRPCLogger{}, } ``` -------------------------------- ### NewGetter Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-jhttp.md Constructs a new Getter, which handles HTTP GET requests by parsing URL query parameters into JSON-RPC requests. ```APIDOC ## NewGetter ```go func NewGetter(parse func(http.ResponseWriter, *http.Request) ([]*jrpc2.ParsedRequest, error)) *Getter ``` ### Description Constructs a new Getter that handles HTTP GET requests. It uses a provided parsing function to convert URL query parameters into JSON-RPC requests. ### Parameters #### Function Parameter `parse` - **parse** (func(http.ResponseWriter, *http.Request) ([]*jrpc2.ParsedRequest, error)) - Required - Function to parse GET request into JSON-RPC requests. ### Returns - `*Getter` - A pointer to the created Getter, which implements `http.Handler`. ``` -------------------------------- ### Example JRPC2 Error Response with Data Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/errors.md Illustrates the JSON structure of an error response that includes the optional 'data' field. ```json { "jsonrpc": "2.0", "id": 1, "error": { "code": -32602, "message": "invalid parameters", "data": { "field": "count", "reason": "must be non-negative" } } } ``` -------------------------------- ### Basic jrpc2 Client Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/quick-reference.md Demonstrates how to create a basic jrpc2 client that connects to a TCP server on localhost:8080 and makes an 'Add' RPC call. Ensure the server is running before executing. ```go package main import ( "context" "log" "net" "github.com/creachadair/jrpc2" "github.com/creachadair/jrpc2/channel" ) func main() { conn, _ := net.Dial("tcp", "localhost:8080") defer conn.Close() // Connect to server ch := channel.Line(conn, conn) cli := jrpc2.NewClient(ch, nil) defer cli.Close() // Make a call ctx := context.Background() var result int err := cli.CallResult(ctx, "Add", []int{1, 2, 3}, &result) if err != nil { log.Fatal(err) } log.Printf("Result: %d", result) } ``` -------------------------------- ### In-Process Testing Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/quick-reference.md For testing, you can create an in-process server and client pair. This avoids network overhead and simplifies testing logic. ```go local := server.NewLocal(assigner, nil) def local.Close() var result int err := local.Client.CallResult(ctx, "Add", []int{1, 2, 3}, &result) ``` -------------------------------- ### NewGetter Function Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-jhttp.md Constructs a new Getter instance. It accepts a custom parsing function to convert HTTP GET requests into JSON-RPC requests. ```go func NewGetter(parse func(http.ResponseWriter, *http.Request) ([]*jrpc2.ParsedRequest, error)) *Getter ``` -------------------------------- ### Server Options Allowing Push Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/quick-reference.md Enable server push functionality by setting AllowPush to true. ```go // Allow server push opts := &jrpc2.ServerOptions{ AllowPush: true, } ``` -------------------------------- ### Channel.Send Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-jhttp.md Sends a JSON-RPC message over the HTTP channel. The message is sent as the body of an HTTP POST request, and a goroutine is started to await the response. ```APIDOC ## Send ### Description Sends a JSON-RPC message over the HTTP channel. The message is sent as the body of an HTTP POST request, and a goroutine is started to await the response. ### Method `func (c *Channel) Send(msg []byte) error` ### Parameters #### Path Parameters - **msg** ([]byte) - Required - The JSON-RPC message to send. ### Returns - **error** - An error if the HTTP request fails or there's a communication issue. ### Notes Forwards message as HTTP POST request body. Starts goroutine to wait for response. ``` -------------------------------- ### Implement and Configure Custom RPCLogger Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/configuration.md Provides a custom implementation of the RPCLogger interface and configures the server to use it. ```go type MyRPCLogger struct{} func (l *MyRPCLogger) LogRequest(ctx context.Context, req *jrpc2.Request) { log.Printf("Request: %s %s", req.Method(), req.ParamString()) } func (l *MyRPCLogger) LogResponse(ctx context.Context, rsp *jrpc2.Response) { log.Printf("Response ID: %s", rsp.ID()) } opts := &jrpc2.ServerOptions{ RPCLog: &MyRPCLogger{}, } ``` -------------------------------- ### Server Options with Custom Context Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/quick-reference.md Provide a custom function to create a new context for server requests, useful for setting timeouts. ```go // Custom context opts := &jrpc2.ServerOptions{ NewContext: func() context.Context { ctx, _ := context.WithTimeout(context.Background(), 30*time.Second) return ctx }, } ``` -------------------------------- ### ServeHTTP Method Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-jhttp.md Implements the http.Handler interface for the Getter, allowing it to serve HTTP requests. ```go func (g *Getter) ServeHTTP(w http.ResponseWriter, req *http.Request) ``` -------------------------------- ### Define Getter Type Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-jhttp.md Defines the Getter struct used for handling HTTP GET requests by constructing JSON-RPC requests from URL query parameters. ```go type Getter struct { // internal fields } ``` -------------------------------- ### Get Response Result as String Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-client.md Returns the encoded result value of a successful response as a string. Returns an empty string if the response contains an error. ```go func (r *Response) ResultString() string ``` -------------------------------- ### Client-Side Callbacks Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/quick-reference.md Configure the client with an `OnCallback` function to handle incoming calls from the server. This function should identify the method and return the appropriate result or an error. ```go opts := &jrpc2.ClientOptions{ OnCallback: func(ctx context.Context, req *jrpc2.Request) (any, error) { if req.Method() == "getUserPreferences" { return map[string]any{ "theme": "dark", "language": "en", }, nil } return nil, jrpc2.Errorf(jrpc2.MethodNotFound, "unknown method") }, } cli := jrpc2.NewClient(ch, opts) ``` -------------------------------- ### Configure Server Logger with StdLogger Helper Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/configuration.md Uses the StdLogger helper to configure the server's Logger option with a standard log.Logger instance. ```go opts := &jrpc2.ServerOptions{ Logger: jrpc2.StdLogger(log.New(os.Stderr, "[server] ", 0)), } ``` -------------------------------- ### Get Server Metrics Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/utilities-and-helpers.md Retrieves the shared map of server metrics for all jrpc2 server instances. This map can be used to access and publish server performance data. ```go func ServerMetrics() *expvar.Map ``` ```go import ( "expvar" "github.com/creachadair/jrpc2" ) // Publish metrics metrics := jrpc2.ServerMetrics() expvar.Publish("jrpc2", metrics) // Access metrics active := metrics.Get("servers_active").(expvar.Int) log.Printf("Active servers: %v", active) ``` -------------------------------- ### Get Client From Context Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/utilities-and-helpers.md Retrieves the jrpc2 client associated with a callback handler context. This is only available within OnCallback handlers and the handler must not close the client. ```go func ClientFromContext(ctx context.Context) *Client ``` ```go opts := &jrpc2.ClientOptions{ OnCallback: func(ctx context.Context, req *jrpc2.Request) (any, error) { cli := jrpc2.ClientFromContext(ctx) // Can use cli to make outbound calls var result int cli.CallResult(ctx, "AnotherMethod", nil, &result) return result, nil }, } ``` -------------------------------- ### Create a New Client Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-client.md Instantiate a new jrpc2 client. The client manages a background goroutine for reading responses and must be closed when no longer needed. ```go import ( "context" "net" "github.com/creachadair/jrpc2" "github.com/creachadair/jrpc2/channel" ) conn, _ := net.Dial("tcp", "localhost:8080") ch := channel.Line(conn, conn) cli := jrpc2.NewClient(ch, nil) deferr cli.Close() // Use the client... ``` -------------------------------- ### ServeHTTP Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-jhttp.md Handles incoming HTTP requests for the Bridge. It processes requests according to JSON-RPC over HTTP standards, with specific behaviors for POST, GET, and content types. ```APIDOC ## ServeHTTP ### Description Handles incoming HTTP requests for the Bridge. It processes requests according to JSON-RPC over HTTP standards, with specific behaviors for POST, GET, and content types. ### Method `func (b Bridge) ServeHTTP(w http.ResponseWriter, req *http.Request)` ### Implements `http.Handler` ### Notes - Default behavior: POST requests only with Content-Type application/json - If ParseRequest hook is set, these restrictions are disabled - If ParseGETRequest hook is set, GET requests are handled separately - Responses: 200 OK for normal requests, 204 No Content for notifications - 405 Method Not Allowed for non-POST requests - 415 Unsupported Media Type for non-application/json content-type ``` -------------------------------- ### NewBridge Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/api-reference-jhttp.md Creates a new http.Handler (Bridge) that forwards HTTP requests to a JSON-RPC server. It can be configured with options for custom request parsing or GET request handling. ```APIDOC ## NewBridge ### Description Creates a new http.Handler (Bridge) that forwards HTTP requests to a JSON-RPC server. It can be configured with options for custom request parsing or GET request handling. ### Method `func NewBridge(local *server.Local, opts *BridgeOptions) Bridge` ### Parameters #### Path Parameters - **local** (*server.Local) - Required - The local server/client pair to bridge to. - **opts** (*BridgeOptions) - Optional - Bridge configuration options. ### Returns - **Bridge** - An http.Handler that bridges HTTP requests to the JSON-RPC server. ### Example ```go import ( "net/http" "github.com/creachadair/jrpc2" "github.com/creachadair/jrpc2/handler" "github.com/creachadair/jrpc2/server" ) assigner := handler.Map{ "Add": handler.New(Add), } local := server.NewLocal(assigner, nil) bridge := jhttp.NewBridge(&local, nil) http.Handle("/rpc", bridge) http.ListenAndServe(":8080", nil) ``` ``` -------------------------------- ### Building JRPC2 Errors with Data in Go Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/errors.md Demonstrates how to attach structured data to a JRPC2 error before returning it. ```go fields := map[string]string{ "field": "name", "reason": "too long", } err := jrpc2.Errorf(jrpc2.InvalidParams, "invalid field") err = err.WithData(fields) return nil, err ``` -------------------------------- ### Get Inbound Request from Context Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/utilities-and-helpers.md Retrieves the inbound request object associated with the current handler context. This is primarily useful in wrapped handlers where the request is not explicitly passed as an argument. ```go func MyWrappedHandler(ctx context.Context) string { req := jrpc2.InboundRequest(ctx) return fmt.Sprintf("Method: %s", req.Method()) } h := handler.New(MyWrappedHandler) ``` -------------------------------- ### Use handler.New for Automatic Conversion Source: https://github.com/creachadair/jrpc2/blob/main/_autodocs/quick-reference.md Demonstrates using handler.New to automatically convert parameters and results for a jrpc2 handler function. This simplifies method registration when types match. ```go func Add(ctx context.Context, values []int) int { sum := 0 for _, v := range values { sum += v } return sum } assigner := handler.Map{ "Add": handler.New(Add), // Automatic parameter/result conversion } ```