Skip to content

Commit

Permalink
server: Remove top-level context handlers.
Browse files Browse the repository at this point in the history
  • Loading branch information
creachadair committed May 5, 2021
1 parent f220aee commit b203bbe
Show file tree
Hide file tree
Showing 6 changed files with 23 additions and 49 deletions.
2 changes: 1 addition & 1 deletion cmd/examples/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
36 changes: 0 additions & 36 deletions ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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")
4 changes: 2 additions & 2 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
16 changes: 8 additions & 8 deletions jrpc2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand All @@ -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)
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions regression_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}),

Expand Down Expand Up @@ -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) {
Expand Down
10 changes: 10 additions & 0 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"container/list"
"context"
"encoding/json"
"errors"
"io"
"strconv"
"strings"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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() {
Expand Down

0 comments on commit b203bbe

Please sign in to comment.