jctx

package
v0.0.61 Latest Latest
Warning

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

Go to latest
Published: Apr 21, 2019 License: BSD-3-Clause Imports: 5 Imported by: 0

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

Examples

Constants

This section is empty.

Variables

View Source
var ErrNoMetadata = errors.New("context metadata not present")

ErrNoMetadata is returned by the Metadata 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"

	"bitbucket.org/creachadair/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"

	"bitbucket.org/creachadair/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"

	"bitbucket.org/creachadair/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

func UnmarshalMetadata(ctx context.Context, meta interface{}) error

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"

	"bitbucket.org/creachadair/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

func WithMetadata(ctx context.Context, meta interface{}) (context.Context, error)

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.

Example
package main

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

	"bitbucket.org/creachadair/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.

Jump to

Keyboard shortcuts

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