Jsonrpc: don't let remote endpoint requests go unanswered

Previously, 'quit' could cause remote endpoints to never get a
reply and thus sometimes hang.  Ensure we always reply.  Also,
give the application a chance to signal jsonrpc-error with the
served code=32000, meaning "no error".

* doc/lispref/text.texi (JSONRPC Overview): Rework section on
request dispatchers.

* lisp/jsonrpc.el (jsonrpc-connection-receive): Rework.
This commit is contained in:
João Távora 2026-01-14 10:46:24 +00:00
parent 7ce09a741a
commit adb605716f
2 changed files with 38 additions and 22 deletions

View file

@ -6014,12 +6014,21 @@ must be a Lisp object that can be serialized as JSON (@pxref{Parsing
JSON}). The result is forwarded to the server as the JSONRPC
@code{result} object. A non-local return, achieved by calling the
function @code{jsonrpc-error}, causes an error response to be sent to
the server. The details of the accompanying JSONRPC @code{error}
object are filled out with whatever was passed to
@code{jsonrpc-error}. A non-local return triggered by an unexpected
error of any other type also causes an error response to be sent
(unless you have set @code{debug-on-error}, in which case this calls
the Lisp debugger, @pxref{Error Debugging}).
the server. A non-local return triggered by an unexpected error of any
other type also causes a response to be sent. The debugger is never
called (unless you have set @code{debug-on-error}, in which case the
Lisp debugger may be called, @pxref{Error Debugging}).
The details of the accompanying JSONRPC @code{error} object are filled
out automatically (in the case of unexpected errors) or with whatever
was passed to @code{jsonrpc-error} (in the case of explicit calls).
Exceptionally, an explicit call to @code{jsonrpc-error} which sets
@code{:code} to 32000 and @code{:data} to any JSON object has the
meaning of ``no error'' and triggers a normal response to the remote
endpoint with @code{result} being set to @code{:data}. This is useful
if the application wants to treat some non-local exits such as user
quits as benign.
@findex jsonrpc-convert-to-endpoint
@findex jsonrpc-convert-from-endpoint

View file

@ -327,22 +327,29 @@ dispatcher in CONN."
(and method id)
(let* ((debug-on-error (and debug-on-error
(not jsonrpc-inhibit-debug-on-error)))
(reply
(condition-case-unless-debug _ignore
reply)
(unwind-protect
(setq
reply
(condition-case oops
`(:result ,(funcall rdispatcher conn (intern method)
params))
`(:result
,(funcall rdispatcher conn (intern method) params))
(jsonrpc-error
(let* ((data (cdr oops))
(code (alist-get 'jsonrpc-error-code data))
(msg (alist-get 'jsonrpc-error-message
(cdr oops))))
(if (eq code 32000) ;; This means 'no error'
(when-let* ((d (alist-get 'jsonrpc-error-data
data)))
`(:result ,d))
`(:error
(:code
,(or (alist-get 'jsonrpc-error-code (cdr oops))
-32603)
:message ,(or (alist-get 'jsonrpc-error-message
(cdr oops))
"Internal error")))))
(error
'(:error (:code -32603 :message "Internal error"))))))
(apply #'jsonrpc--reply conn id method reply)))
(:code ,(or code -32603)
:message ,(or msg "Internal error"))))))))
(unless reply
(setq reply
`(:error (:code -32603 :message "Internal error"))))
(apply #'jsonrpc--reply conn id method reply))))
(;; A remote notification
method
(funcall ndispatcher conn (intern method) params))