Documentation ¶
Overview ¶
Package jctx implements an encoder and decoder for request context values, allowing context metadata to be propagated through JSON-RPC.
A context.Context value carries request-scoped values across API boundaries and between processes. The jrpc2 package has hooks to allow clients and servers to propagate context values transparently through JSON-RPC calls. The jctx package provides functions that implement these hooks.
The jrpc2 context plumbing works by injecting a wrapper message around the request parameters. The client adds this wrapper during the call, and the server removes it. The actual client parameters are embedded inside the wrapper unmodified.
The format of the wrapper generated by this package is:
{ "jctx": "1", "payload": <original-params>, "deadline": <rfc-3339-timestamp>, "meta": <json-value> }
Of these, only the "jctx" marker is required; the others are assumed to be empty if they do not appear in the message.
Deadlines and Timeouts ¶
If the parent context contains a deadline, it is encoded into the wrapper as an RFC 3339 timestamp in UTC, for example "2009-11-10T23:00:00.00000015Z".
Metadata ¶
The jctx.WithMetadata function allows the caller to attach an arbitrary JSON-encoded value to a context. This value will be transmitted over the wire during a JSON-RPC call. The recipient can decode this value from the context using the jctx.UnmarshalMetadata function.
Index ¶
- Variables
- func Decode(ctx context.Context, method string, req json.RawMessage) (context.Context, json.RawMessage, error)
- func Encode(ctx context.Context, method string, params json.RawMessage) (json.RawMessage, error)
- func UnmarshalMetadata(ctx context.Context, meta interface{}) error
- func WithMetadata(ctx context.Context, meta interface{}) (context.Context, error)
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNoMetadata = errors.New("context metadata not present")
ErrNoMetadata is returned by the UnmarshalMetadata function if the context does not contain a metadata value.
Functions ¶
func Decode ¶
func Decode(ctx context.Context, method string, req json.RawMessage) (context.Context, json.RawMessage, error)
Decode decodes the specified request message as a context-wrapped request, and returns the updated context (based on ctx) and the embedded parameters. If the request does not have a context wrapper, it is returned as-is.
If the encoded request specifies a deadline, that deadline is set in the context value returned.
If the request includes context metadata, they are attached and can be recovered using jctx.UnmarshalMetadata.
Example ¶
package main import ( "context" "encoding/json" "fmt" "log" "github.com/yinfei8/jrpc2/jctx" ) func main() { const input = `{"jctx":"1","deadline":"2018-06-09T20:45:33.000000001Z","payload":["a", "b", "c"]}` ctx, param, err := jctx.Decode(context.Background(), "methodName", json.RawMessage(input)) if err != nil { log.Fatalln("Decode:", err) } dl, ok := ctx.Deadline() fmt.Println("params:", string(param)) fmt.Println("deadline:", ok, dl) }
Output: params: ["a", "b", "c"] deadline: true 2018-06-09 20:45:33.000000001 +0000 UTC
func Encode ¶
func Encode(ctx context.Context, method string, params json.RawMessage) (json.RawMessage, error)
Encode encodes the specified context and request parameters for transmission. If a deadline is set on ctx, it is converted to UTC before encoding. If metadata are set on ctx (see jctx.WithMetadata), they are included.
Example (Basic) ¶
package main import ( "context" "encoding/json" "fmt" "log" "github.com/yinfei8/jrpc2/jctx" ) func main() { ctx := context.Background() enc, err := jctx.Encode(ctx, "methodName", json.RawMessage(`[1,2,3]`)) if err != nil { log.Fatalln("Encode:", err) } fmt.Println(string(enc)) }
Output: {"jctx":"1","payload":[1,2,3]}
Example (Deadline) ¶
package main import ( "bytes" "context" "encoding/json" "fmt" "log" "time" "github.com/yinfei8/jrpc2/jctx" ) func main() { deadline := time.Date(2018, 6, 9, 20, 45, 33, 1, time.UTC) ctx, cancel := context.WithDeadline(context.Background(), deadline) defer cancel() enc, err := jctx.Encode(ctx, "methodName", json.RawMessage(`{"A":"#1"}`)) if err != nil { log.Fatalln("Encode:", err) } fmt.Println(pretty(enc)) } func pretty(v []byte) string { var buf bytes.Buffer if err := json.Indent(&buf, v, "", " "); err != nil { log.Fatal(err) } return buf.String() }
Output: { "jctx": "1", "deadline": "2018-06-09T20:45:33.000000001Z", "payload": { "A": "#1" } }
func UnmarshalMetadata ¶
UnmarshalMetadata decodes the metadata value attached to ctx into meta, or returns ErrNoMetadata if ctx does not have metadata attached.
Example ¶
package main import ( "context" "encoding/json" "fmt" "log" "github.com/yinfei8/jrpc2/jctx" ) func main() { // Setup for the example... const input = `{"user":"Jon Snow","info":"MjhFRjQwRjUtNzdDOS00NzQ0LUI1QkQtM0FEQ0QxQzE1MTQx"}` ctx, err := jctx.WithMetadata(context.Background(), json.RawMessage(input)) if err != nil { log.Fatalln("Setup:", err) } // Demonstrates how to decode the value back. var meta struct { User string `json:"user"` Info []byte `json:"info"` } if err := jctx.UnmarshalMetadata(ctx, &meta); err != nil { log.Fatalln("UnmarshalMetadata:", err) } fmt.Println("user:", meta.User) fmt.Println("info:", string(meta.Info)) }
Output: user: Jon Snow info: 28EF40F5-77C9-4744-B5BD-3ADCD1C15141
func WithMetadata ¶
WithMetadata attaches the specified metadata value to the context. The meta value must support encoding to JSON. In case of error, the original value of ctx is returned along with the error. If meta == nil, the resulting context has no metadata attached; this can be used to remove metadata from a context that has it.
Example ¶
package main import ( "bytes" "context" "encoding/json" "fmt" "log" "github.com/yinfei8/jrpc2/jctx" ) func main() { type Meta struct { User string `json:"user"` UUID string `json:"uuid"` } ctx, err := jctx.WithMetadata(context.Background(), &Meta{ User: "Jon Snow", UUID: "28EF40F5-77C9-4744-B5BD-3ADCD1C15141", }) if err != nil { log.Fatalln("WithMetadata:", err) } enc, err := jctx.Encode(ctx, "methodName", nil) if err != nil { log.Fatal("Encode:", err) } fmt.Println(pretty(enc)) } func pretty(v []byte) string { var buf bytes.Buffer if err := json.Indent(&buf, v, "", " "); err != nil { log.Fatal(err) } return buf.String() }
Output: { "jctx": "1", "meta": { "user": "Jon Snow", "uuid": "28EF40F5-77C9-4744-B5BD-3ADCD1C15141" } }
Types ¶
This section is empty.