Skip to content

Commit

Permalink
Update doc comments throughout the module. (#98)
Browse files Browse the repository at this point in the history
Updates #46.
  • Loading branch information
creachadair committed Mar 20, 2023
1 parent 2c3680d commit 9b22787
Show file tree
Hide file tree
Showing 12 changed files with 127 additions and 120 deletions.
18 changes: 10 additions & 8 deletions base.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import (
"strings"
)

// An Assigner assigns a Handler to handle the specified method name, or nil if
// no method is available to handle the request.
// An Assigner maps method names to Handler functions.
type Assigner interface {
// Assign returns the handler for the named method, or nil.
// Assign returns the handler for the named method, or returns nil to
// indicate that the method is not known.
//
// The implementation can obtain the complete request from ctx using the
// jrpc2.InboundRequest function.
Assign(ctx context.Context, method string) Handler
Expand All @@ -26,10 +27,11 @@ type Namer interface {
Names() []string
}

// A Handler function implements a method given a request. The response value
// must be JSON-marshalable or nil. In case of error, the handler can return a
// value of type *jrpc2.Error to control the response code sent back to the
// caller; otherwise the server will wrap the resulting value.
// A Handler function implements a method. The request contains the method
// name, request ID, and parameters sent by the client. The result value must
// be JSON-marshalable or nil. In case of error, the handler can return a value
// of type *jrpc2.Error to control the response code sent back to the caller;
// otherwise the server will wrap the resulting value.
//
// The context passed to the handler by a *jrpc2.Server includes two special
// values that the handler may extract.
Expand Down Expand Up @@ -153,7 +155,7 @@ func (r *Response) UnmarshalResult(v any) error {
return json.Unmarshal(r.result, v)
}

// ResultString returns the encoded result message of r as a string.
// ResultString returns the encoded result value of r as a string.
// If r has no result, for example if r is an error response, it returns "".
func (r *Response) ResultString() string { return string(r.result) }

Expand Down
10 changes: 5 additions & 5 deletions channel/channel.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Copyright (C) 2017 Michael J. Fromberger. All Rights Reserved.

// Package channel defines a basic communications channel.
// Package channel defines a communications channel.
//
// A Channel encodes/transmits and decodes/receives data records over an
// unstructured stream, using a configurable framing discipline. This package
// provides some basic framing implementations.
// A Channel encodes/transmits and decodes/receives data records. The types in
// this package support sending and receiving over an unstructured stream using
// a configurable framing discipline.
//
// # Channels
//
Expand Down Expand Up @@ -42,7 +42,7 @@ import (

// A Channel represents the ability to transmit and receive data records. A
// channel does not interpret the contents of a record, but may add and remove
// framing so that records can be embedded in higher-level protocols.
// framing so that records can be embedded in lower-level protocols.
//
// One sender and one receiver may use a Channel concurrently, but the methods
// of a Channel are not otherwise required to be safe for concurrent use. The
Expand Down
2 changes: 1 addition & 1 deletion channel/hdr.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"strings"
)

// StrictHeader defines a framing that transmits and receives messages using a
// StrictHeader defines a Framing that transmits and receives messages using a
// header prefix similar to HTTP, in which mimeType describes the content type.
//
// Specifically, each message is sent in the format:
Expand Down
52 changes: 26 additions & 26 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
)

// A Client is a JSON-RPC 2.0 client. The client sends requests and receives
// responses on a channel.Channel provided by the caller.
// responses on a channel.Channel provided by the constructor.
type Client struct {
done *sync.WaitGroup // done when the reader is finished at shutdown time

Expand All @@ -23,8 +23,8 @@ type Client struct {
scall func(context.Context, *jmessage) []byte
chook func(*Client, *Response)

cbctx context.Context // terminates when the client is closed
cbcancel func() // cancels cbctx
cbctx context.Context // terminates when the client is closed
cbcancel context.CancelFunc // cancels cbctx

mu sync.Mutex // protects the fields below
ch channel.Channel // channel to the server
Expand Down Expand Up @@ -82,7 +82,7 @@ func (c *Client) accept(ch receiver) error {
c.log("Decoding error: %v", err)
}
c.mu.Lock()
c.stop(err)
c.stopLocked(err)
c.mu.Unlock()
return err
}
Expand All @@ -94,16 +94,16 @@ func (c *Client) accept(ch receiver) error {
c.mu.Lock()
defer c.mu.Unlock()
for _, rsp := range in {
c.deliver(rsp)
c.deliverLocked(rsp)
}
}()
return nil
}

// handleRequest handles a callback or notification from the server. The
// handleRequestLocked handles a callback or notification from the server. The
// caller must hold c.mu. This function does not block for the handler.
// Precondition: msg is a request or notification, not a response or error.
func (c *Client) handleRequest(msg *jmessage) {
func (c *Client) handleRequestLocked(msg *jmessage) {
if msg.isNotification() {
if c.snote == nil {
c.log("Discarding notification: %v", msg)
Expand Down Expand Up @@ -134,14 +134,14 @@ func (c *Client) handleRequest(msg *jmessage) {
}
}

// For each response, find the request pending on its ID and deliver it. The
// caller must hold c.mu. Unknown response IDs are logged and discarded. As
// we are under the lock, we do not wait for the pending receiver to pick up
// the response; we just drop it in their channel. The channel is buffered so
// we don't need to rendezvous.
func (c *Client) deliver(rsp *jmessage) {
// deliverLocked delivers rsp to the request pending on its ID. The caller
// must hold c.mu. Unknown response IDs are logged and discarded. As we are
// under the lock, we do not wait for the pending receiver to pick up the
// response; we just drop it in their channel. The channel is buffered so we
// don't need to rendezvous.
func (c *Client) deliverLocked(rsp *jmessage) {
if rsp.isRequestOrNotification() {
c.handleRequest(rsp)
c.handleRequestLocked(rsp)
return
}

Expand Down Expand Up @@ -285,9 +285,9 @@ func (c *Client) waitComplete(pctx context.Context, id string, p *Response) {
}
}

// Call initiates a single request and blocks until the response returns.
// A successful call reports a nil error and a non-nil response. Errors from
// the server have concrete type *jrpc2.Error.
// Call initiates a single request and blocks until the response returns or ctx
// ends. A successful call reports a nil error and a non-nil response. Errors
// from the server have concrete type *jrpc2.Error.
//
// rsp, err := c.Call(ctx, method, params)
// if e, ok := err.(*jrpc2.Error); ok {
Expand Down Expand Up @@ -324,8 +324,8 @@ func (c *Client) CallResult(ctx context.Context, method string, params, result a
}

// Batch initiates a batch of concurrent requests, and blocks until all the
// responses return. The responses are returned in the same order as the
// original specs, omitting notifications.
// responses return or ctx ends. The responses are returned in the same order
// as the original specs, omitting notifications.
//
// Any error reported by Batch represents an error in encoding or sending the
// batch to the server. Errors reported by the server in response to requests
Expand Down Expand Up @@ -356,15 +356,15 @@ func (c *Client) Batch(ctx context.Context, specs []Spec) ([]*Response, error) {
}

// A Spec combines a method name and parameter value as part of a Batch. If
// the Notify field is true, the request is sent as a notification.
// the Notify flag is true, the request is sent as a notification.
type Spec struct {
Method string
Params any
Notify bool
}

// Notify transmits a notification to the specified method and parameters. It
// blocks until the notification has been sent.
// blocks until the notification has been sent or ctx ends.
func (c *Client) Notify(ctx context.Context, method string, params any) error {
req, err := c.note(ctx, method, params)
if err != nil {
Expand All @@ -377,7 +377,7 @@ func (c *Client) Notify(ctx context.Context, method string, params any) error {
// Close shuts down the client, terminating any pending in-flight requests.
func (c *Client) Close() error {
c.mu.Lock()
c.stop(errClientStopped)
c.stopLocked(errClientStopped)
c.mu.Unlock()
c.done.Wait()

Expand All @@ -392,10 +392,10 @@ func isUninteresting(err error) bool {
return err == io.EOF || channel.IsErrClosing(err) || err == errClientStopped
}

// stop closes down the reader for c and records err as its final state. The
// caller must hold c.mu. If multiple callers invoke stop, only the first will
// successfully record its error status.
func (c *Client) stop(err error) {
// stopLocked closes down the reader for c and records err as its final state.
// The caller must hold c.mu. If multiple callers invoke stop, only the first
// will successfully record its error status.
func (c *Client) stopLocked(err error) {
if c.ch == nil {
return // nothing is running
}
Expand Down
16 changes: 8 additions & 8 deletions code.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (C) 2017 Michael J. Fromberger. All Rights Reserved.

// Package code defines error code values used by the jrpc2 package.
package jrpc2

import (
Expand All @@ -9,9 +8,9 @@ import (
"fmt"
)

// A Code is an error response.
// A Code is an error code included in the JSON-RPC error object.
//
// Code values from and including -32768 to -32000 are reserved for pre-defined
// Code values from and including -32768 to -32000 are reserved for predefined
// JSON-RPC errors. Any code within this range, but not defined explicitly
// below is reserved for future use. The remainder of the space is available
// for application defined errors.
Expand Down Expand Up @@ -89,11 +88,12 @@ var stdError = map[Code]string{
}

// ErrorCode returns a Code to categorize the specified error.
// If err == nil, it returns jrpc2.NoError.
// If err is (or wraps) an ErrCoder, it returns the reported code value.
// If err is context.Canceled, it returns jrpc2.Cancelled.
// If err is context.DeadlineExceeded, it returns jrpc2.DeadlineExceeded.
// Otherwise it returns jrpc2.SystemError.
//
// - If err == nil, it returns jrpc2.NoError.
// - If err is (or wraps) an ErrCoder, it returns the reported code value.
// - If err is context.Canceled, it returns jrpc2.Cancelled.
// - If err is context.DeadlineExceeded, it returns jrpc2.DeadlineExceeded.
// - Otherwise it returns jrpc2.SystemError.
func ErrorCode(err error) Code {
if err == nil {
return NoError
Expand Down
16 changes: 8 additions & 8 deletions ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import (
"context"
)

// 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.
// InboundRequest returns the inbound request associated with the context
// passed to a Handler, or nil if ctx does not have an inbound request.
// A *jrpc2.Server populates this value for handler contexts.
//
// This is mainly useful to wrapped server methods that do not have the request
// as an explicit parameter; for direct implementations of the Handler type the
Expand All @@ -23,9 +23,8 @@ func InboundRequest(ctx context.Context) *Request {

type inboundRequestKey struct{}

// 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.
// ServerFromContext returns the server associated with the context passed to a
// Handler by a *jrpc2.Server. It will panic for a non-handler context.
//
// It is safe to retain the server and invoke its methods beyond the lifetime
// of the context from which it was extracted; however, a handler must not
Expand All @@ -36,9 +35,10 @@ func ServerFromContext(ctx context.Context) *Server { return ctx.Value(serverKey
type serverKey struct{}

// ClientFromContext returns the client associated with the given context.
// This will be populated on the context passed to callback handlers.
// This will be populated on the context passed by a *jrpc2.Client to a
// client-side callback handler.
//
// A callback handler must not close the client, as the close will deadlock
// A callback handler MUST NOT close the client, as the close will deadlock
// waiting for the callback to return.
func ClientFromContext(ctx context.Context) *Client { return ctx.Value(clientKey{}).(*Client) }

Expand Down
3 changes: 2 additions & 1 deletion error.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ import (
)

// Error is the concrete type of errors returned from RPC calls.
// It also represents the JSON encoding of the JSON-RPC error object.
type Error struct {
Code Code `json:"code"` // the machine-readable error code
Message string `json:"message,omitempty"` // the human-readable error message
Data json.RawMessage `json:"data,omitempty"` // optional ancillary error data
}

// Error renders e to a human-readable string for the error interface.
// Error returns a human-readable description of e.
func (e Error) Error() string { return fmt.Sprintf("[%d] %s", e.Code, e.Message) }

// ErrCode trivially satisfies the ErrCoder interface for an *Error.
Expand Down
16 changes: 8 additions & 8 deletions internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,12 +227,12 @@ func TestServer_specialMethods(t *testing.T) {
}, nil)
ctx := context.Background()
for _, name := range []string{rpcServerInfo, "donkeybait"} {
if got := s.assign(ctx, name); got == nil {
t.Errorf("s.assign(%s): no method assigned", name)
if got := s.assignLocked(ctx, name); got == nil {
t.Errorf("s.assignLocked(%s): no method assigned", name)
}
}
if got := s.assign(ctx, "rpc.nonesuch"); got != nil {
t.Errorf("s.assign(rpc.nonesuch): got %p, want nil", got)
if got := s.assignLocked(ctx, "rpc.nonesuch"); got != nil {
t.Errorf("s.assignLocked(rpc.nonesuch): got %p, want nil", got)
}
}

Expand All @@ -250,14 +250,14 @@ func TestServer_disableBuiltinHook(t *testing.T) {

// With builtins disabled, the default rpc.* methods should not get assigned.
for _, name := range []string{rpcServerInfo} {
if got := s.assign(ctx, name); got != nil {
t.Errorf("s.assign(%s): got %p, wanted nil", name, got)
if got := s.assignLocked(ctx, name); got != nil {
t.Errorf("s.assignLocked(%s): got %p, wanted nil", name, got)
}
}

// However, user-assigned methods with this prefix should now work.
if got := s.assign(ctx, "rpc.nonesuch"); got == nil {
t.Error("s.assign(rpc.nonesuch): missing assignment")
if got := s.assignLocked(ctx, "rpc.nonesuch"); got == nil {
t.Error("s.assignLocked(rpc.nonesuch): missing assignment")
}
}

Expand Down
7 changes: 4 additions & 3 deletions json.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import (
"encoding/json"
)

// ParseRequests parses a single request or a batch of requests from JSON.
// This function reports an error only if msg is not valid JSON. The caller
// must check the individual results for their validity.
// ParseRequests parses either a single request or a batch of requests from
// JSON. It reports an error only if msg is not valid JSON. The caller must
// check the Error field results to determine whether the individual requests
// are valid.
func ParseRequests(msg []byte) ([]*ParsedRequest, error) {
var reqs jmessages
if err := reqs.parseJSON(msg); err != nil {
Expand Down
4 changes: 2 additions & 2 deletions opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
)

// ServerOptions control the behaviour of a server created by NewServer.
// A nil *ServerOptions provides sensible defaults.
// A nil *ServerOptions is valid and provides sensible defaults.
// It is safe to share server options among multiple server instances.
type ServerOptions struct {
// If not nil, send debug text logs here.
Expand Down Expand Up @@ -88,7 +88,7 @@ func (s *ServerOptions) rpcLog() RPCLogger {
}

// ClientOptions control the behaviour of a client created by NewClient.
// A nil *ClientOptions provides sensible defaults.
// A nil *ClientOptions is valid and provides sensible defaults.
type ClientOptions struct {
// If not nil, send debug text logs here.
Logger Logger
Expand Down

0 comments on commit 9b22787

Please sign in to comment.