diff --git a/bench_test.go b/bench_test.go index c1e4c1ba..b16a5004 100644 --- a/bench_test.go +++ b/bench_test.go @@ -10,7 +10,6 @@ import ( "github.com/creachadair/jrpc2" "github.com/creachadair/jrpc2/handler" - "github.com/creachadair/jrpc2/jctx" "github.com/creachadair/jrpc2/server" "github.com/fortytw2/leaktest" ) @@ -23,37 +22,17 @@ func BenchmarkRoundTrip(b *testing.B) { return nil, nil }), } - ctxClient := &jrpc2.ClientOptions{EncodeContext: jctx.Encode} tests := []struct { desc string cli *jrpc2.ClientOptions srv *jrpc2.ServerOptions }{ - {"C01-CTX-B", nil, &jrpc2.ServerOptions{DisableBuiltin: true, Concurrency: 1}}, - {"C01-CTX+B", nil, &jrpc2.ServerOptions{Concurrency: 1}}, - {"C04-CTX-B", nil, &jrpc2.ServerOptions{DisableBuiltin: true, Concurrency: 4}}, - {"C04-CTX+B", nil, &jrpc2.ServerOptions{Concurrency: 4}}, - {"C12-CTX-B", nil, &jrpc2.ServerOptions{DisableBuiltin: true, Concurrency: 12}}, - {"C12-CTX+B", nil, &jrpc2.ServerOptions{Concurrency: 12}}, - - {"C01+CTX-B", ctxClient, - &jrpc2.ServerOptions{DecodeContext: jctx.Decode, DisableBuiltin: true, Concurrency: 1}, - }, - {"C01+CTX+B", ctxClient, - &jrpc2.ServerOptions{DecodeContext: jctx.Decode, Concurrency: 1}, - }, - {"C04+CTX-B", ctxClient, - &jrpc2.ServerOptions{DecodeContext: jctx.Decode, DisableBuiltin: true, Concurrency: 4}, - }, - {"C04+CTX+B", ctxClient, - &jrpc2.ServerOptions{DecodeContext: jctx.Decode, Concurrency: 4}, - }, - {"C12+CTX-B", ctxClient, - &jrpc2.ServerOptions{DecodeContext: jctx.Decode, DisableBuiltin: true, Concurrency: 4}, - }, - {"C12+CTX+B", ctxClient, - &jrpc2.ServerOptions{DecodeContext: jctx.Decode, Concurrency: 12}, - }, + {"C01-B", nil, &jrpc2.ServerOptions{DisableBuiltin: true, Concurrency: 1}}, + {"C01+B", nil, &jrpc2.ServerOptions{Concurrency: 1}}, + {"C04-B", nil, &jrpc2.ServerOptions{DisableBuiltin: true, Concurrency: 4}}, + {"C04+B", nil, &jrpc2.ServerOptions{Concurrency: 4}}, + {"C12-B", nil, &jrpc2.ServerOptions{DisableBuiltin: true, Concurrency: 12}}, + {"C12+B", nil, &jrpc2.ServerOptions{Concurrency: 12}}, } for _, test := range tests { b.Run(test.desc, func(b *testing.B) { diff --git a/client.go b/client.go index b0f66557..100b133c 100644 --- a/client.go +++ b/client.go @@ -20,7 +20,6 @@ type Client struct { done *sync.WaitGroup // done when the reader is finished at shutdown time log func(string, ...interface{}) // write debug logs here - enctx encoder snote func(*jmessage) scall func(context.Context, *jmessage) []byte chook func(*Client, *Response) @@ -41,7 +40,6 @@ func NewClient(ch channel.Channel, opts *ClientOptions) *Client { c := &Client{ done: new(sync.WaitGroup), log: opts.logFunc(), - enctx: opts.encodeContext(), snote: opts.handleNotification(), scall: opts.handleCallback(), chook: opts.handleCancel(), @@ -421,7 +419,7 @@ func (c *Client) stop(err error) { // value of params must be either nil or encodable as a JSON object or array. func (c *Client) marshalParams(ctx context.Context, method string, params interface{}) (json.RawMessage, error) { if params == nil { - return c.enctx(ctx, method, nil) // no parameters, that is OK + return nil, nil // no parameters, that is OK } pbits, err := json.Marshal(params) if err != nil { @@ -432,11 +430,7 @@ func (c *Client) marshalParams(ctx context.Context, method string, params interf // an array or an object. return nil, &Error{Code: code.InvalidRequest, Message: "invalid parameters: array or object required"} } - bits, err := c.enctx(ctx, method, pbits) - if err != nil { - return nil, err - } - return bits, err + return pbits, nil } func newPending(ctx context.Context, id string) (context.Context, *Response) { diff --git a/jctx/example_test.go b/jctx/example_test.go deleted file mode 100644 index 25ff5f6d..00000000 --- a/jctx/example_test.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (C) 2017 Michael J. Fromberger. All Rights Reserved. - -package jctx_test - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "log" - "time" - - "github.com/creachadair/jrpc2/jctx" -) - -func ExampleEncode_basic() { - 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]} -} - -func ExampleEncode_deadline() { - 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)) - // Output: - // { - // "jctx": "1", - // "deadline": "2018-06-09T20:45:33.000000001Z", - // "payload": { - // "A": "#1" - // } - // } -} - -func ExampleDecode() { - 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 ExampleWithMetadata() { - 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)) - // Output: - // { - // "jctx": "1", - // "meta": { - // "user": "Jon Snow", - // "uuid": "28EF40F5-77C9-4744-B5BD-3ADCD1C15141" - // } - // } -} - -func ExampleUnmarshalMetadata() { - // 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 pretty(v []byte) string { - var buf bytes.Buffer - if err := json.Indent(&buf, v, "", " "); err != nil { - log.Fatal(err) - } - return buf.String() -} diff --git a/jctx/jctx.go b/jctx/jctx.go deleted file mode 100644 index e1edb220..00000000 --- a/jctx/jctx.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright (C) 2017 Michael J. Fromberger. All Rights Reserved. - -// 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": , -// "deadline": , -// "meta": -// } -// -// 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. -// -package jctx - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "time" -) - -const wireVersion = "1" - -// wireContext is the encoded representation of a context value. It includes -// the deadline together with an underlying payload carrying the original -// request parameters. The resulting message replaces the parameters of the -// original JSON-RPC request. -type wireContext struct { - V *string `json:"jctx"` // must be wireVersion - - Deadline *time.Time `json:"deadline,omitempty"` // encoded in UTC - Payload json.RawMessage `json:"payload,omitempty"` - Metadata json.RawMessage `json:"meta,omitempty"` -} - -// 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. -func Encode(ctx context.Context, method string, params json.RawMessage) (json.RawMessage, error) { - v := wireVersion - c := wireContext{V: &v, Payload: params} - if dl, ok := ctx.Deadline(); ok { - utcdl := dl.UTC() - c.Deadline = &utcdl - } - - // If there are metadata in the context, attach them. - if v := ctx.Value(metadataKey{}); v != nil { - c.Metadata = v.(json.RawMessage) - } - - return json.Marshal(c) -} - -// 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. -func Decode(ctx context.Context, method string, req json.RawMessage) (context.Context, json.RawMessage, error) { - if len(req) == 0 || req[0] != '{' { - return ctx, req, nil // an empty message or non-object has no wrapper - } - var c wireContext - if err := json.Unmarshal(req, &c); err != nil || c.V == nil { - return ctx, req, nil // fall back assuming an un-wrapped message - } else if *c.V != wireVersion { - return nil, nil, fmt.Errorf("invalid context version %q", *c.V) - } - if c.Metadata != nil { - ctx = context.WithValue(ctx, metadataKey{}, c.Metadata) - } - if c.Deadline != nil && !c.Deadline.IsZero() { - var ignored context.CancelFunc - ctx, ignored = context.WithDeadline(ctx, (*c.Deadline).UTC()) - _ = ignored // the caller cannot use this value - } - - return ctx, c.Payload, nil -} - -type metadataKey struct{} - -// 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. -func WithMetadata(ctx context.Context, meta interface{}) (context.Context, error) { - if meta == nil { - // Note we explicitly attach a value even if meta == nil, since ctx might - // already have metadata so we need to mask it. - return context.WithValue(ctx, metadataKey{}, json.RawMessage(nil)), nil - } - bits, err := json.Marshal(meta) - if err != nil { - return ctx, err - } - return context.WithValue(ctx, metadataKey{}, json.RawMessage(bits)), nil -} - -// UnmarshalMetadata decodes the metadata value attached to ctx into meta, or -// returns ErrNoMetadata if ctx does not have metadata attached. -func UnmarshalMetadata(ctx context.Context, meta interface{}) error { - if v := ctx.Value(metadataKey{}); v != nil { - // If the metadata value is explicitly nil, we should report that there - // is no metadata message. - if msg := v.(json.RawMessage); msg != nil { - return json.Unmarshal(msg, meta) - } - } - return ErrNoMetadata -} - -// ErrNoMetadata is returned by the UnmarshalMetadata function if the context -// does not contain a metadata value. -var ErrNoMetadata = errors.New("context metadata not present") diff --git a/jctx/jctx_test.go b/jctx/jctx_test.go deleted file mode 100644 index 5f6161ae..00000000 --- a/jctx/jctx_test.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (C) 2017 Michael J. Fromberger. All Rights Reserved. - -package jctx - -import ( - "context" - "encoding/json" - "testing" - "time" -) - -var bicent = time.Date(1976, 7, 4, 1, 2, 3, 4, time.UTC) - -func TestEncoding(t *testing.T) { - tests := []struct { - desc string - deadline time.Time - params, want string - }{ - {"zero-void", time.Time{}, "", `{"jctx":"1"}`}, - - {"zero-payload", time.Time{}, - "[1,2,3]", `{"jctx":"1","payload":[1,2,3]}`}, - - {"bicentennial-void", bicent.In(time.Local), - "", `{"jctx":"1","deadline":"1976-07-04T01:02:03.000000004Z"}`, - }, - - {"bicentennial-payload", bicent, - `{"apple":"pear"}`, - `{"jctx":"1","deadline":"1976-07-04T01:02:03.000000004Z","payload":{"apple":"pear"}}`, - }, - } - for _, test := range tests { - t.Run(test.desc, func(t *testing.T) { - ctx := context.Background() - if !test.deadline.IsZero() { - var cancel context.CancelFunc - ctx, cancel = context.WithDeadline(ctx, test.deadline) - defer cancel() - } - raw, err := Encode(ctx, "dummy", json.RawMessage(test.params)) - if err != nil { - t.Errorf("Encoding %q failed: %v", test.params, err) - } else if got := string(raw); got != test.want { - t.Errorf("Encoding %q: got %#q, want %#q", test.params, got, test.want) - } - }) - } -} - -func TestDecoding(t *testing.T) { - tests := []struct { - desc, input string - deadline time.Time - want string - }{ - {"empty context", "", time.Time{}, ""}, - {"empty parameters", `{"jctx":"1"}`, time.Time{}, ""}, - {"non-object input", `[1, 5]`, time.Time{}, `[1, 5]`}, - {"non-context empty object", `{}`, time.Time{}, `{}`}, - {"non-context object", `{"kiss":"me"}`, time.Time{}, `{"kiss":"me"}`}, - {"invalid context", `{"jctx":2, "ok":true}`, time.Time{}, `{"jctx":2, "ok":true}`}, - - {"zero-payload", `{"jctx":"1","payload":["a","b","c"]}`, time.Time{}, `["a","b","c"]`}, - {"zero-payload-naked", `["a", "b", "c"]`, time.Time{}, `["a", "b", "c"]`}, - - {"bicentennial-void", `{"jctx":"1","deadline":"1976-07-04T01:02:03.000000004Z"}`, bicent, ""}, - - {"bicentennial-payload", `{ -"jctx":"1", -"deadline":"1976-07-04T01:02:03.000000004Z", -"payload":{"lhs":1,"rhs":2} -}`, bicent, `{"lhs":1,"rhs":2}`}, - } - for _, test := range tests { - t.Run(test.desc, func(t *testing.T) { - ctx := context.Background() - gotctx, params, err := Decode(ctx, "dummy", json.RawMessage(test.input)) - if err != nil { - t.Fatalf("Decode(_, %q): error: %v", test.input, err) - } - if !test.deadline.IsZero() { - dl, ok := gotctx.Deadline() - if !ok { - t.Error("Decode: missing expected deadline") - } else if !dl.Equal(test.deadline) { - t.Errorf("Decode deadline: got %v, want %v", dl, test.deadline) - } - } - if got := string(params); got != test.want { - t.Errorf("Decode params: got %#q, want %#q", got, test.want) - } - }) - } -} - -func TestMetadata(t *testing.T) { - type value struct { - Name string `json:"name,omitempty"` - Marbles int `json:"marbles,omitempty"` - } - input := value{Name: "Hieronymus Bosch", Marbles: 3} - - base := context.Background() - ctx, err := WithMetadata(base, input) - if err != nil { - t.Fatalf("WithMetadata(base, %+v) failed: %v", input, err) - } - - var output value - - // The base value does not contain the value. - if err := UnmarshalMetadata(base, &output); err != ErrNoMetadata { - t.Logf("Base metadata decoded value: %+v", output) - t.Errorf("UnmarshalMetadata(base): got error %v, want %v", err, ErrNoMetadata) - } - - // The attached context does contain the value (prior to transmission). - output = value{} - if err := UnmarshalMetadata(ctx, &output); err != nil { - t.Errorf("UnmarshalMetadata(ctx): unexpected error: %v", err) - } else if output != input { - t.Errorf("UnmarshalMetadata(ctx): got %+v, want %+v", output, input) - } - - // Simulate transmission -- encode, then decode. - var dec context.Context - if enc, err := Encode(ctx, "dummy", nil); err != nil { - t.Fatalf("Encoding context failed: %v", err) - } else { - t.Logf("Encoded context is: %#q", string(enc)) - if dec, _, err = Decode(base, "dummy", enc); err != nil { - t.Fatalf("Decoding context failed: %v", err) - } - } - - // The decoded context does contain the value (after receipt). - output = value{} - if err := UnmarshalMetadata(dec, &output); err != nil { - t.Errorf("Metadata(dec): unexpected error: %v", err) - } else if output != input { - t.Errorf("Metadata(dec): got %+v, want %+v", output, input) - } - - // "Attaching" nil removes the metadata. - clr, err := WithMetadata(ctx, nil) - if err != nil { - t.Fatalf("WithMetadata(ctx, nil): unexpected error: %v", err) - } - var bad interface{} - if err := UnmarshalMetadata(clr, &bad); err != ErrNoMetadata { - t.Errorf("Metadata(clr): got %+v, %v; want %v", bad, err, ErrNoMetadata) - } -} diff --git a/jhttp/getter_test.go b/jhttp/getter_test.go index 98ceb7dd..29e8fca3 100644 --- a/jhttp/getter_test.go +++ b/jhttp/getter_test.go @@ -12,7 +12,6 @@ import ( "strings" "testing" - "github.com/creachadair/jrpc2" "github.com/creachadair/jrpc2/handler" "github.com/creachadair/jrpc2/jhttp" "github.com/fortytw2/leaktest" @@ -28,9 +27,7 @@ func TestGetter(t *testing.T) { }, "first", "second"), } - g := jhttp.NewGetter(mux, &jhttp.GetterOptions{ - Client: &jrpc2.ClientOptions{EncodeContext: checkContext}, - }) + g := jhttp.NewGetter(mux, nil) defer checkClose(t, g) hsrv := httptest.NewServer(g) diff --git a/jhttp/jhttp_test.go b/jhttp/jhttp_test.go index ef9cbc47..e625391f 100644 --- a/jhttp/jhttp_test.go +++ b/jhttp/jhttp_test.go @@ -27,20 +27,11 @@ var testService = handler.Map{ }), } -func checkContext(ctx context.Context, _ string, p json.RawMessage) (json.RawMessage, error) { - if jhttp.HTTPRequest(ctx) == nil { - return nil, errors.New("no HTTP request in context") - } - return p, nil -} - func TestBridge(t *testing.T) { defer leaktest.Check(t)() // Set up a bridge with the test configuration. - b := jhttp.NewBridge(testService, &jhttp.BridgeOptions{ - Client: &jrpc2.ClientOptions{EncodeContext: checkContext}, - }) + b := jhttp.NewBridge(testService, nil) defer checkClose(t, b) // Create an HTTP test server to call into the bridge. diff --git a/jrpc2_test.go b/jrpc2_test.go index 061dd603..430d7ff3 100644 --- a/jrpc2_test.go +++ b/jrpc2_test.go @@ -17,7 +17,6 @@ import ( "github.com/creachadair/jrpc2/channel" "github.com/creachadair/jrpc2/code" "github.com/creachadair/jrpc2/handler" - "github.com/creachadair/jrpc2/jctx" "github.com/creachadair/jrpc2/server" "github.com/fortytw2/leaktest" "github.com/google/go-cmp/cmp" @@ -1061,36 +1060,6 @@ func TestClient_callbackUpCall(t *testing.T) { } } -// Verify that the context encoding/decoding hooks work. -func TestContextPlumbing(t *testing.T) { - defer leaktest.Check(t)() - - want := time.Now().Add(10 * time.Second) - ctx, cancel := context.WithDeadline(context.Background(), want) - defer cancel() - - loc := server.NewLocal(handler.Map{ - "X": handler.New(func(ctx context.Context) (bool, error) { - got, ok := ctx.Deadline() - if !ok { - return false, errors.New("no deadline was set") - } else if !got.Equal(want) { - return false, fmt.Errorf("deadline: got %v, want %v", got, want) - } - t.Logf("Got expected deadline: %v", got) - return true, nil - }), - }, &server.LocalOptions{ - Server: &jrpc2.ServerOptions{DecodeContext: jctx.Decode}, - Client: &jrpc2.ClientOptions{EncodeContext: jctx.Encode}, - }) - defer loc.Close() - - if _, err := loc.Client.Call(ctx, "X", nil); err != nil { - t.Errorf("Call X failed: %v", err) - } -} - // Verify that calling a wrapped method which takes no parameters, but in which // the caller provided parameters, will correctly report an error. func TestHandler_noParams(t *testing.T) { diff --git a/opts.go b/opts.go index 0793f8a8..2ec66fa5 100644 --- a/opts.go +++ b/opts.go @@ -46,13 +46,6 @@ type ServerOptions struct { // If unset, the server uses a background context. NewContext func() context.Context - // If set, this function is called with the method name and encoded request - // parameters received from the client, before they are delivered to the - // handler. Its return value replaces the context and argument values. This - // allows the server to decode context metadata sent by the client. - // If unset, context and parameters are used as given. - DecodeContext func(context.Context, string, json.RawMessage) (context.Context, json.RawMessage, error) - // If set, use this value to record server metrics. All servers created // from the same options will share the same metrics collector. If none is // set, an empty collector will be created for each new server. @@ -95,17 +88,6 @@ func (o *ServerOptions) newContext() func() context.Context { return o.NewContext } -type decoder = func(context.Context, string, json.RawMessage) (context.Context, json.RawMessage, error) - -func (s *ServerOptions) decodeContext() decoder { - if s == nil || s.DecodeContext == nil { - return func(ctx context.Context, method string, params json.RawMessage) (context.Context, json.RawMessage, error) { - return ctx, params, nil - } - } - return s.DecodeContext -} - func (s *ServerOptions) metrics() *metrics.M { if s == nil || s.Metrics == nil { return metrics.New() @@ -126,13 +108,6 @@ type ClientOptions struct { // If not nil, send debug text logs here. Logger Logger - // If set, this function is called with the context, method name, and - // encoded request parameters before the request is sent to the server. - // Its return value replaces the request parameters. This allows the client - // to send context metadata along with the request. If unset, the parameters - // are unchanged. - EncodeContext func(context.Context, string, json.RawMessage) (json.RawMessage, error) - // If set, this function is called if a notification is received from the // server. If unset, server notifications are logged and discarded. At // most one invocation of the callback will be active at a time. @@ -169,17 +144,6 @@ func (c *ClientOptions) logFunc() func(string, ...interface{}) { return c.Logger.Printf } -type encoder = func(context.Context, string, json.RawMessage) (json.RawMessage, error) - -func (c *ClientOptions) encodeContext() encoder { - if c == nil || c.EncodeContext == nil { - return func(_ context.Context, methods string, params json.RawMessage) (json.RawMessage, error) { - return params, nil - } - } - return c.EncodeContext -} - func (c *ClientOptions) handleNotification() func(*jmessage) { if c == nil || c.OnNotify == nil { return nil diff --git a/server.go b/server.go index f4d275a2..27367582 100644 --- a/server.go +++ b/server.go @@ -31,7 +31,6 @@ type Server struct { log func(string, ...interface{}) // write debug logs here rpcLog RPCLogger // log RPC requests and responses here newctx func() context.Context // create a new base request context - dectx decoder // decode context from request metrics *metrics.M // metrics collected during execution start time.Time // when Start was called builtin bool // whether built-in rpc.* methods are enabled @@ -72,7 +71,6 @@ func NewServer(mux Assigner, opts *ServerOptions) *Server { log: opts.logFunc(), rpcLog: opts.rpcLog(), newctx: opts.newContext(), - dectx: opts.decodeContext(), mu: new(sync.Mutex), metrics: opts.metrics(), start: opts.startTime(), @@ -314,7 +312,8 @@ func (s *Server) checkAndAssign(next jmessages) tasks { // deferred validation error } else if t.hreq.method == "" { t.err = errEmptyMethod - } else if s.setContext(t, id) { + } else { + s.setContext(t, id) t.m = s.assign(t.ctx, t.hreq.method) if t.m == nil { t.err = errNoSuchMethod.WithData(t.hreq.method) @@ -332,15 +331,8 @@ func (s *Server) checkAndAssign(next jmessages) tasks { // setContext constructs and attaches a request context to t, and reports // whether this succeeded. -func (s *Server) setContext(t *task, id string) bool { - base, params, err := s.dectx(s.newctx(), t.hreq.method, t.hreq.params) - t.hreq.params = params - if err != nil { - t.err = Errorf(code.InternalError, "invalid request context: %v", err) - return false - } - - t.ctx = context.WithValue(base, inboundRequestKey{}, t.hreq) +func (s *Server) setContext(t *task, id string) { + t.ctx = context.WithValue(s.newctx(), inboundRequestKey{}, t.hreq) // Store the cancellation for a request that needs a reply, so that we can // respond to cancellation requests. @@ -349,7 +341,6 @@ func (s *Server) setContext(t *task, id string) bool { s.used[id] = cancel t.ctx = ctx } - return true } // invoke invokes the handler m for the specified request type, and marshals diff --git a/tools/jcall/jcall.go b/tools/jcall/jcall.go index f4d64a78..9438d1f8 100644 --- a/tools/jcall/jcall.go +++ b/tools/jcall/jcall.go @@ -24,7 +24,6 @@ import ( "github.com/creachadair/jrpc2" "github.com/creachadair/jrpc2/channel" - "github.com/creachadair/jrpc2/jctx" "github.com/creachadair/jrpc2/jhttp" "github.com/creachadair/wschannel" ) @@ -33,7 +32,6 @@ var ( dialTimeout = flag.Duration("dial", 5*time.Second, "Timeout on dialing the server (0 for no timeout)") callTimeout = flag.Duration("timeout", 0, "Timeout on each call (0 for no timeout)") doNotify = flag.Bool("notify", false, "Send a notification") - withContext = flag.Bool("c", false, "Send context with request") chanFraming = flag.String("f", envOrDefault("JCALL_FRAMING", "line"), "Channel framing") doBatch = flag.Bool("batch", false, "Issue calls as a batch rather than sequentially") doErrors = flag.Bool("e", false, "Print error values to stdout") @@ -42,7 +40,6 @@ var ( doTiming = flag.Bool("T", false, "Print call timing stats") doWaitExit = flag.Bool("W", false, "Wait for interrupt at exit") withLogging = flag.Bool("v", false, "Enable verbose logging") - withMeta = flag.String("meta", "", "Attach this JSON value as request metadata (implies -c)") ) func init() { @@ -97,21 +94,8 @@ func main() { log.Fatal("Arguments are
{ }...") } - // Set up the context for the call, including timeouts and any metadata that - // are specified on the command line. Setting -meta also implicitly sets -c. + // Set up the context for the call, including a timeouts if specified. ctx := context.Background() - if *withMeta == "" { - *withMeta = os.Getenv("JCALL_META") - } - if *withMeta != "" { - mc, err := jctx.WithMetadata(ctx, json.RawMessage(*withMeta)) - if err != nil { - log.Fatalf("Invalid request metadata: %v", err) - } - ctx = mc - *withContext = true - } - if *callTimeout > 0 { var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, *callTimeout) @@ -180,9 +164,6 @@ func newClient(conn channel.Channel) *jrpc2.Client { fmt.Printf(`{"method":%q,"params":%s}`+"\n", req.Method(), string(p)) }, } - if *withContext { - opts.EncodeContext = jctx.Encode - } if *withLogging { opts.Logger = jrpc2.StdLogger(nil) }