[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/eglot e906d25 14/69: Overhaul JSON and JSRONRPC error h
From: |
João Távora |
Subject: |
[elpa] externals/eglot e906d25 14/69: Overhaul JSON and JSRONRPC error handling |
Date: |
Fri, 22 Jun 2018 11:54:55 -0400 (EDT) |
branch: externals/eglot
commit e906d25d6eeb5e75eb7fab8b45d9f88f9a6567c4
Author: João Távora <address@hidden>
Commit: João Távora <address@hidden>
Overhaul JSON and JSRONRPC error handling
Also fix some bugs.
* jrpc.el (pcase, array): Require it.
(jrpc--connect): Default error function properly logs error event.
(jrpc--process-filter): Protect against JSON errors.
(jrpc--process-receive): Protect against JSONRPC errors.
(jrpc-reply): Check if both result and error.
(jrpc--process-send): Ensure json-object-type is plist.
(jrpc--process-sentinel): Correctly call error handler. Use #'ignore,
not identity. Use pcase-let instead of cl-dbind
---
jrpc.el | 102 ++++++++++++++++++++++++++++++++++++++--------------------------
1 file changed, 60 insertions(+), 42 deletions(-)
diff --git a/jrpc.el b/jrpc.el
index d314c36..eff941a 100644
--- a/jrpc.el
+++ b/jrpc.el
@@ -117,6 +117,8 @@
(require 'json)
(require 'subr-x)
(require 'warnings)
+(require 'pcase)
+(require 'array) ; xor
(defgroup jrpc nil
"Interaction between JSONRPC endpoints"
@@ -294,17 +296,17 @@ type.
(insert "\n----------b---y---e---b---y---e----------\n")))
;; Cancel outstanding timers
(maphash (lambda (_id triplet)
- (cl-destructuring-bind (_success _error timeout) triplet
+ (pcase-let ((`(,_success ,_error ,timeout) triplet))
(cancel-timer timeout)))
(jrpc--request-continuations proc))
(unwind-protect
;; Call all outstanding error handlers
(maphash (lambda (_id triplet)
- (cl-destructuring-bind (_success error _timeout) triplet
- (funcall error :code -1 :message (format "Server died"))))
+ (pcase-let ((`(,_success ,error ,_timeout) triplet))
+ (funcall error `(:code -1 :message "Server died"))))
(jrpc--request-continuations proc))
(jrpc-message "Server exited with status %s" (process-exit-status proc))
- (funcall (or (jrpc--on-shutdown proc) #'identity) proc)
+ (funcall (or (jrpc--on-shutdown proc) #'ignore) proc)
(delete-process proc))))
(defun jrpc--process-filter (proc string)
@@ -352,12 +354,19 @@ type.
(save-restriction
(narrow-to-region (point) message-end)
(let* ((json-object-type 'plist)
- (json-message (json-read)))
- ;; Process content in another buffer,
- ;; shielding buffer from tamper
- ;;
- (with-temp-buffer
- (jrpc--process-receive proc json-message))))
+ (json-message
+ (condition-case-unless-debug oops
+ (json-read)
+ (error
+ (jrpc-warn "Invalid JSON: %s %s"
+ (cdr oops) (buffer-string))
+ nil))))
+ (when json-message
+ ;; Process content in another
+ ;; buffer, shielding proc buffer from
+ ;; tamper
+ (with-temp-buffer
+ (jrpc--process-receive proc
json-message)))))
(goto-char message-end)
(delete-region (point-min) (point))
(setq expected-bytes nil))))
@@ -412,36 +421,43 @@ is a symbol saying if this is a client or server
originated."
(defun jrpc--process-receive (proc message)
"Process MESSAGE from PROC."
- (cl-destructuring-bind (&key method id error params result _jsonrpc) message
- (let* ((continuations (and id
- (not method)
- (gethash id (jrpc--request-continuations
proc)))))
- (jrpc-log-event proc message 'server)
- (when error (setf (jrpc-status proc) `(,error t)))
- (cond (method
- (unwind-protect
- (funcall (jrpc--dispatcher proc) proc method id params)
- (unless (or (not id)
- (member id (jrpc--server-request-ids proc)))
- (jrpc-reply
- proc id
- :error (jrpc-obj :code -32603 :message "Internal error")))
- (setf (jrpc--server-request-ids proc)
- (delete id (jrpc--server-request-ids proc)))))
- (continuations
- (cancel-timer (cl-third continuations))
- (remhash id (jrpc--request-continuations proc))
- (if error
- (funcall (cl-second continuations) error)
- (funcall (cl-first continuations) result)))
- (id
- (jrpc-warn "Ooops no continuation for id %s" id)))
- (jrpc--call-deferred proc)
- (force-mode-line-update t))))
+ (pcase-let ((`(,method ,id ,error ,params ,result)
+ (condition-case-unless-debug oops
+ (cl-destructuring-bind
+ (&rest args &key method id error params result _jsonrpc)
+ message (list method id error params result))
+ (error (jrpc-warn "Invalid JSONRPC message %s: %s" message
+ (cdr oops))
+ nil)))
+ (continuations))
+ (jrpc-log-event proc message 'server)
+ (when error (setf (jrpc-status proc) `(,error t)))
+ (cond (method
+ (unwind-protect
+ (funcall (jrpc--dispatcher proc) proc method id params)
+ (unless (or (not id)
+ (member id (jrpc--server-request-ids proc)))
+ (jrpc-reply
+ proc id
+ :error (jrpc-obj :code -32603 :message "Internal error")))
+ (setf (jrpc--server-request-ids proc)
+ (delete id (jrpc--server-request-ids proc)))))
+ ((setq continuations
+ (and id (gethash id (jrpc--request-continuations proc))))
+ (let ((timer (nth 2 continuations)))
+ (when timer (cancel-timer timer)))
+ (remhash id (jrpc--request-continuations proc))
+ (if error (funcall (nth 1 continuations) error)
+ (funcall (nth 0 continuations) result)))
+ (id
+ (jrpc-warn "No continuation for id %s" id)))
+ (jrpc--call-deferred proc)
+ (force-mode-line-update t)))
(defun jrpc--process-send (proc message)
"Send MESSAGE to PROC (ID is optional)."
- (let ((json (json-encode message)))
+ (let ((json-object-type 'plist)
+ (json (json-encode message)))
(process-send-string proc (format "Content-Length: %d\r\n\r\n%s"
(string-bytes json)
json))
@@ -550,8 +566,9 @@ Return the request ID, or nil, in case the request was
deferred."
(or error-fn
(jrpc-lambda (&key code message &allow-other-keys)
(setf (jrpc-status proc) `(,message t))
- proc (jrpc-obj :message "error ignored, status set"
- :id id :error code)))
+ (jrpc-log-event
+ proc (jrpc-obj :message "error ignored, status set"
+ :id id :error code))))
(funcall make-timeout))
(jrpc--request-continuations proc))
(jrpc--process-send proc (jrpc-obj :jsonrpc "2.0"
@@ -575,7 +592,7 @@ DEFERRED is passed to `jrpc-async-request', which see."
(jrpc-async-request
proc method params
:success-fn (lambda (result) (throw tag `(done ,result)))
- :error-fn (jrpc-lambda (&key code message &allow-other-keys)
+ :error-fn (jrpc-lambda (&key code message _data)
(throw tag `(error ,(format "%s: %s" code message))))
:timeout-fn (lambda () (throw tag '(error "Timed out")))
:deferred deferred)
@@ -585,15 +602,16 @@ DEFERRED is passed to `jrpc-async-request', which see."
(cl-defun jrpc-notify (proc method params)
"Notify PROC of something, don't expect a reply.e"
- (jrpc--process-send proc (jrpc-obj :jasonrpc "2.0"
+ (jrpc--process-send proc (jrpc-obj :jsonrpc "2.0"
:method method
:params params)))
(cl-defun jrpc-reply (proc id &key result error)
"Reply to PROC's request ID with RESULT or ERROR."
(push id (jrpc--server-request-ids proc))
+ (unless (xor result error) (jrpc-error "Can't pass both RESULT and ERROR!"))
(jrpc--process-send
- proc`(:jasonrpc "2.0" :id ,id
+ proc `(:jsonrpc "2.0" :id ,id
,@(when result `(:result ,result))
,@(when error `(:error ,error)))))
- [elpa] externals/eglot 67d8335 17/69: jrpc.el should know nothing of mode-line updates, (continued)
- [elpa] externals/eglot 67d8335 17/69: jrpc.el should know nothing of mode-line updates, João Távora, 2018/06/22
- [elpa] externals/eglot 0e1a5f0 05/69: jrpc-connect is now passed a generic dispatching function, João Távora, 2018/06/22
- [elpa] externals/eglot 078a1a6 13/69: Add full "Commentary" section to jrpc.el, João Távora, 2018/06/22
- [elpa] externals/eglot 2f1d76d 03/69: Proper server shutdown when jrpc.el is used, João Távora, 2018/06/22
- [elpa] externals/eglot a2aa1ed 19/69: Robustify timer handling for jrpc-async-request, João Távora, 2018/06/22
- [elpa] externals/eglot 7769c07 16/69: Send message, then establish continuations, João Távora, 2018/06/22
- [elpa] externals/eglot 7f2e894 11/69: Merge branch 'master' into jsonrpc-refactor (using good ol' git merge), João Távora, 2018/06/22
- [elpa] externals/eglot e5ba4f6 18/69: Automatically reply with error if dispatcher doesn't, João Távora, 2018/06/22
- [elpa] externals/eglot 33ae871 09/69: More flexible jrpc.el and improve eglot.el's doc, João Távora, 2018/06/22
- [elpa] externals/eglot 3265c1d 10/69: Simpler callback protocol for JSONRPC parameters and results, João Távora, 2018/06/22
- [elpa] externals/eglot e906d25 14/69: Overhaul JSON and JSRONRPC error handling,
João Távora <=
- [elpa] externals/eglot 7cd94b9 12/69: Improve jrpc.el's doc (and change jrpc-request's protocol a tiny bit), João Távora, 2018/06/22
- [elpa] externals/eglot 489182d 24/69: New jsonrpc-error error type, João Távora, 2018/06/22
- [elpa] externals/eglot 5cea412 20/69: Fix typos and phrasing in commentary and docstrings, João Távora, 2018/06/22
- [elpa] externals/eglot c30f0f3 23/69: Get rid of jsonrpc.el customization group and timeout, João Távora, 2018/06/22
- [elpa] externals/eglot 5e8fe4c 26/69: Add jsonrpc-tests.el, João Távora, 2018/06/22
- [elpa] externals/eglot 05ff697 29/69: Document current API breaches a bit, João Távora, 2018/06/22
- [elpa] externals/eglot 258e3b0 07/69: Merge master into jsonrpc-refactor (using imerge), João Távora, 2018/06/22
- [elpa] externals/eglot 4bbf810 25/69: Don't return implementation details in jsonrpc-async-request, João Távora, 2018/06/22
- [elpa] externals/eglot 5874af9 32/69: Fix deferred actions (forgot the crucial non-local exit), João Távora, 2018/06/22
- [elpa] externals/eglot 481f16e 33/69: * eglot.el (eglot-eldoc-function): Remove spurious log message, João Távora, 2018/06/22