From d4361e1dd2a81105aad982ca1f7b66277c822d9b Mon Sep 17 00:00:00 2001 From: "M. J. Fromberger" Date: Wed, 5 May 2021 00:43:52 -0400 Subject: [PATCH] server: Remove top-level context handlers. https://github.com/creachadair/jrpc2/issues/46#issuecomment-832349441 --- cmd/examples/server/server.go | 2 +- ctx.go | 36 ----------------------------------- doc.go | 4 ++-- jrpc2_test.go | 16 ++++++++-------- regression_test.go | 4 ++-- server.go | 10 ++++++++++ 6 files changed, 23 insertions(+), 49 deletions(-) diff --git a/cmd/examples/server/server.go b/cmd/examples/server/server.go index 5e74f0d..cd526ce 100644 --- a/cmd/examples/server/server.go +++ b/cmd/examples/server/server.go @@ -58,7 +58,7 @@ func Div(ctx context.Context, arg binop) (float64, error) { // Status simulates a health check, reporting "OK" to all callers. It also // demonstrates the use of server-side push. func Status(ctx context.Context) (string, error) { - if err := jrpc2.PushNotify(ctx, "pushback", []string{"hello, friend"}); err != nil { + if err := jrpc2.ServerFromContext(ctx).Notify(ctx, "pushback", []string{"hello, friend"}); err != nil { return "BAD", err } return "OK", nil diff --git a/ctx.go b/ctx.go index da69d09..f172a58 100644 --- a/ctx.go +++ b/ctx.go @@ -2,17 +2,8 @@ package jrpc2 import ( "context" - "errors" - - "github.com/creachadair/jrpc2/metrics" ) -// ServerMetrics returns the server metrics collector. If the server does not -// have a metrics collector, it returns nil, which is ready for use but -// discards all posted metrics. This function is for use by handlers, and will -// panic for a non-handler context. -func ServerMetrics(ctx context.Context) *metrics.M { return ServerFromContext(ctx).metrics } - // InboundRequest returns the inbound request associated with the given // context, or nil if ctx does not have an inbound request. The context passed // to the handler by *jrpc2.Server will include this value. @@ -30,29 +21,6 @@ func InboundRequest(ctx context.Context) *Request { type inboundRequestKey struct{} -// PushNotify posts a server notification to the client. If the server does not -// have push enabled (via the AllowPush option), it reports ErrPushUnsupported. -// This function is for use by handlers, and will panic for a non-handler context. -func PushNotify(ctx context.Context, method string, params interface{}) error { - return ServerFromContext(ctx).Notify(ctx, method, params) -} - -// PushCall posts a server call to the client. If the server does not have push -// enabled (via the AllowPush option), it reports ErrPushUnsupported. -// This function is for use by handlers, and will panic for a non-handler context. -// -// A successful callback reports a nil error and a non-nil response. Errors -// reported by the client have concrete type *jrpc2.Error. -func PushCall(ctx context.Context, method string, params interface{}) (*Response, error) { - return ServerFromContext(ctx).Callback(ctx, method, params) -} - -// CancelRequest requests the server associated with ctx to cancel the pending -// or in-flight request with the specified ID. If no request exists with that -// ID, this is a no-op without error. -// This function is for use by handlers, and will panic for a non-handler context. -func CancelRequest(ctx context.Context, id string) { ServerFromContext(ctx).CancelRequest(id) } - // ServerFromContext returns the server associated with the given context. // This will be populated on the context passed to request handlers. // This function is for use by handlers, and will panic for a non-handler context. @@ -64,7 +32,3 @@ func CancelRequest(ctx context.Context, id string) { ServerFromContext(ctx).Canc func ServerFromContext(ctx context.Context) *Server { return ctx.Value(serverKey{}).(*Server) } type serverKey struct{} - -// ErrPushUnsupported is returned by PushNotify and PushCall if server pushes -// are not enabled in the specified context. -var ErrPushUnsupported = errors.New("server push is not enabled") diff --git a/doc.go b/doc.go index 0d80bfd..892c634 100644 --- a/doc.go +++ b/doc.go @@ -233,8 +233,8 @@ client. Otherwise, those methods will report an error: // server push is not enabled } -A method handler may use jrpc2.PushNotify and jrpc2.PushCall functions to -access these methods from its context. +A method handler may use jrpc2.ServerFromContext to access the server from its +context, and then invoke these methods on it. On the client side, the OnNotify and OnCallback options in jrpc2.ClientOptions provide hooks to which any server requests are delivered, if they are set. diff --git a/jrpc2_test.go b/jrpc2_test.go index 0519dda..83dbdc5 100644 --- a/jrpc2_test.go +++ b/jrpc2_test.go @@ -377,7 +377,7 @@ func TestServerStopCancellation(t *testing.T) { } } -// Test that a handler can cancel an in-flight request with jrpc2.CancelRequest. +// Test that a handler can cancel an in-flight request. func TestHandlerCancel(t *testing.T) { ready := make(chan struct{}) loc := server.NewLocal(handler.Map{ @@ -393,7 +393,7 @@ func TestHandlerCancel(t *testing.T) { return err } t.Logf("Test handler: cancelling %q...", id) - jrpc2.CancelRequest(ctx, id) + jrpc2.ServerFromContext(ctx).CancelRequest(id) return nil }), }, nil) @@ -438,7 +438,7 @@ func TestErrors(t *testing.T) { return 17, jrpc2.DataErrorf(errCode, json.RawMessage(errData), errMessage) }), "Push": handler.New(func(ctx context.Context) (bool, error) { - return false, jrpc2.PushNotify(ctx, "PushBack", nil) + return false, jrpc2.ServerFromContext(ctx).Notify(ctx, "PushBack", nil) }), "Code": handler.New(func(ctx context.Context) error { return code.Code(12345).Err() @@ -508,7 +508,7 @@ func TestBadCallParams(t *testing.T) { func TestServerInfo(t *testing.T) { loc := server.NewLocal(handler.Map{ "Metricize": handler.New(func(ctx context.Context) (bool, error) { - m := jrpc2.ServerMetrics(ctx) + m := jrpc2.ServerFromContext(ctx).Metrics() if m == nil { t.Error("Request context does not contain a metrics writer") return false, nil @@ -683,8 +683,8 @@ func TestPushNotify(t *testing.T) { "NoteMe": handler.New(func(ctx context.Context) (bool, error) { // When this method is called, it posts a notification back to the // client before returning. - if err := jrpc2.PushNotify(ctx, "method", nil); err != nil { - t.Errorf("PushNotify unexpectedly failed: %v", err) + if err := jrpc2.ServerFromContext(ctx).Notify(ctx, "method", nil); err != nil { + t.Errorf("Push Notify unexpectedly failed: %v", err) return false, err } return true, nil @@ -728,13 +728,13 @@ func TestPushNotify(t *testing.T) { func TestPushCall(t *testing.T) { loc := server.NewLocal(handler.Map{ "CallMeMaybe": handler.New(func(ctx context.Context) error { - if rsp, err := jrpc2.PushCall(ctx, "succeed", nil); err != nil { + if rsp, err := jrpc2.ServerFromContext(ctx).Callback(ctx, "succeed", nil); err != nil { t.Errorf("Callback failed: %v", err) } else { t.Logf("Callback succeeded: %v", rsp.ResultString()) } - if rsp, err := jrpc2.PushCall(ctx, "fail", nil); err == nil { + if rsp, err := jrpc2.ServerFromContext(ctx).Callback(ctx, "fail", nil); err == nil { t.Errorf("Callback did not fail: got %v, want error", rsp) } return nil diff --git a/regression_test.go b/regression_test.go index bd62a26..c35f3e8 100644 --- a/regression_test.go +++ b/regression_test.go @@ -25,7 +25,7 @@ func TestLockRaceRegression(t *testing.T) { if err := req.UnmarshalParams(&handler.Args{&id}); err != nil { return err } - jrpc2.CancelRequest(ctx, id) + jrpc2.ServerFromContext(ctx).CancelRequest(id) return nil }), @@ -57,7 +57,7 @@ func TestOnCallbackPanicRegression(t *testing.T) { loc := server.NewLocal(handler.Map{ "Test": handler.New(func(ctx context.Context) error { - rsp, err := jrpc2.PushCall(ctx, "Poke", nil) + rsp, err := jrpc2.ServerFromContext(ctx).Callback(ctx, "Poke", nil) if err == nil { t.Errorf("Callback unexpectedly succeeded: %#q", rsp.ResultString()) } else if !strings.HasSuffix(err.Error(), panicString) { diff --git a/server.go b/server.go index 38222d1..e1add7e 100644 --- a/server.go +++ b/server.go @@ -4,6 +4,7 @@ import ( "container/list" "context" "encoding/json" + "errors" "io" "strconv" "strings" @@ -369,6 +370,10 @@ func (s *Server) ServerInfo() *ServerInfo { return info } +// ErrPushUnsupported is returned by the Notify and Call methods if server +// pushes are not enabled. +var ErrPushUnsupported = errors.New("server push is not enabled") + // Notify posts a single server-side notification to the client. // // This is a non-standard extension of JSON-RPC, and may not be supported by @@ -452,6 +457,11 @@ func (s *Server) pushReq(ctx context.Context, wantID bool, method string, params return rsp, err } +// Metrics returns the server metrics collector for s. If s does not define a +// collector, this method returns nil, which is ready for use but discards all +// metrics. +func (s *Server) Metrics() *metrics.M { return s.metrics } + // Stop shuts down the server. It is safe to call this method multiple times or // from concurrent goroutines; it will only take effect once. func (s *Server) Stop() {