diff --git a/handler_with_error.go b/handler_with_error.go index 81c5546..6f056cc 100644 --- a/handler_with_error.go +++ b/handler_with_error.go @@ -7,11 +7,18 @@ import ( // HandlerWithError implements Handler by calling the func for each // request and handling returned errors and results. -type HandlerWithError func(context.Context, *Conn, *Request) (result interface{}, err error) +func HandlerWithError(handleFunc func(context.Context, *Conn, *Request) (result interface{}, err error)) *HandlerWithErrorConfigurer { + return &HandlerWithErrorConfigurer{handleFunc: handleFunc} +} + +type HandlerWithErrorConfigurer struct { + handleFunc func(context.Context, *Conn, *Request) (result interface{}, err error) + suppressErrClosed bool +} // Handle implements Handler. -func (h HandlerWithError) Handle(ctx context.Context, conn *Conn, req *Request) { - result, err := h(ctx, conn, req) +func (h *HandlerWithErrorConfigurer) Handle(ctx context.Context, conn *Conn, req *Request) { + result, err := h.handleFunc(ctx, conn, req) if req.Notif { if err != nil { log.Printf("jsonrpc2 handler: notification %q handling error: %s", req.Method, err) @@ -33,7 +40,25 @@ func (h HandlerWithError) Handle(ctx context.Context, conn *Conn, req *Request) if !req.Notif { if err := conn.SendResponse(ctx, resp); err != nil { - log.Printf("jsonrpc2 handler: sending response %s: %s", resp.ID, err) + if err != ErrClosed || !h.suppressErrClosed { + log.Printf("jsonrpc2 handler: sending response %s: %s", resp.ID, err) + } } } } + +// SuppressErrClosed makes the handler suppress jsonrpc2.ErrClosed errors from +// being logged. The original handler `h` is returned. +// +// This is optional because only in some cases is this behavior desired. For +// example, a handler that serves end-user connections may not want to log +// ErrClosed because it just indicates the end-user connection has gone away +// for any reason (they could have lost wifi connection, are no longer +// interested in the request and closed the connection, etc) and as such it +// would be log spam, whereas a handler that serves internal connections would +// never expect connections to go away unexpectedly (which could indicate +// service degradation, etc) and as such ErrClosed should always be logged. +func (h *HandlerWithErrorConfigurer) SuppressErrClosed() Handler { + h.suppressErrClosed = true + return h +}