Advertisement
Guest User

Untitled

a guest
Mar 13th, 2020
581
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lisp 8.65 KB | None | 0 0
  1. ;;; naria2-jsonrpc.el --- Control aria2 in Emacs via WebSocket -*- lexical-binding: t -*-
  2.  
  3. ;; Copyright (C) 2019-2020 Zhu Zihao
  4.  
  5. ;; Author: Zhu Zihao <all_but_last@163.com>
  6. ;; URL: https://github.com/cireu/emacs-naria2
  7. ;; Version: 0.0.1
  8. ;; Package-Requires: ((emacs "25.2") (jsonrpc "1.0.7") (websocket "1.11.1"))
  9. ;; Keywords: conn, lisp
  10.  
  11. ;; This file is NOT part of GNU Emacs.
  12.  
  13. ;; This file is free software; you can redistribute it and/or modify
  14. ;; it under the terms of the GNU General Public License as published by
  15. ;; the Free Software Foundation; either version 3, or (at your option)
  16. ;; any later version.
  17.  
  18. ;; This program is distributed in the hope that it will be useful,
  19. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21. ;; GNU General Public License for more details.
  22.  
  23. ;; For a full copy of the GNU General Public License
  24. ;; see <https://www.gnu.org/licenses/>.
  25.  
  26. ;;; Commentary:
  27.  
  28. ;; * Introduction
  29.  
  30. ;; This package allow you to control your [[https://github.com/aria2/aria2][aria2]] instance via WebSocket and JSONRPC
  31. ;; in Emacs. Some low-level API was wrapped for usage.
  32.  
  33. ;; * Installation
  34.  
  35. ;; The suggested way to install is =package.el=.
  36.  
  37. ;; * Examples
  38.  
  39. ;; #+begin_src emacs-lisp
  40. ;; (require 'naria2-jsonrpc)
  41.  
  42. ;; ;; Sync API
  43. ;; ;; Notice that closing a connection in explicit is *always* unnecessary,
  44. ;; ;; because `naria2-jsonrpc-connection' is a RAII object, so the release of
  45. ;; ;; resource can be delegated to GC.
  46.  
  47. ;; (letrec ((inst (naria2-jsonrpc-connect "localhost" 6800 nil "tokenfoobar"))
  48. ;;          (gid (naria2-jsonrpc-sync-call inst "addUri"
  49. ;;                                         [["http://example.org/file"]]))
  50. ;;          (cancelback (naria2-jsonrpc-on inst "onDownloadComplete"
  51. ;;                                         (lambda (_data)
  52. ;;                                           (message "Task completed")
  53. ;;                                           (funcall cancelback)))))
  54. ;;   (message "The GID of download task is %s" gid))
  55.  
  56. ;; ;; Async API
  57. ;; (let ((inst (naria2-jsonrpc-connect "localhost" 6800 nil nil)))
  58. ;;   (naria2-jsonrpc-async-call
  59. ;;    inst "addUri"
  60. ;;    :success-fn (lambda (data)
  61. ;;                  (message "The GID of download task is %s" gid))))
  62. ;; #+end_src
  63.  
  64. ;; * Reference
  65.  
  66. ;; - [[https://aria2.github.io/manual/en/html/aria2c.html][API manual of Aria2]]
  67.  
  68. ;;; Code:
  69.  
  70. (require 'jsonrpc)
  71. (require 'websocket)
  72. (require 'eieio)
  73.  
  74. (eval-when-compile
  75.   (require 'cl-lib)
  76.   (require 'subr-x)
  77.   (require 'pcase))
  78.  
  79. ;;; Utilities
  80.  
  81. (defun naria2-jsonrpc-parse-string (str)
  82.   "Parse STR into a JSON object."
  83.   (with-temp-buffer
  84.     (insert str)
  85.     (goto-char (point-min))
  86.     (jsonrpc--json-read)))
  87.  
  88. ;;; JSONRPC
  89.  
  90. (defclass naria2-jsonrpc-connection (jsonrpc-connection)
  91.   ((-secret
  92.     :initform nil
  93.     :documentation
  94.     "The authorization token for RPC call.")
  95.    (-socket
  96.     :documentation
  97.     "The websocket connection.")
  98.    (-notification-handlers-table
  99.     :initform (make-hash-table :test #'equal)
  100.     :documentation
  101.     "The hash table to store handlers of different notifications from aria2.")
  102.    (-finalizer
  103.     :documentation
  104.     "The object returned by `make-finalizer'."))
  105.   "The websocket JSONRPC connection to aria2 download server.")
  106.  
  107. ;; JSONRPC Implementation
  108.  
  109. (cl-defmethod jsonrpc-connection-send ((conn naria2-jsonrpc-connection)
  110.                                        &rest args &key method &allow-other-keys)
  111.   (when method
  112.     (let* ((msg `(:jsonrpc "2.0" ,@args))
  113.            (json-str (jsonrpc--json-encode msg))
  114.            (socket (oref conn -socket)))
  115.       (websocket-send-text socket json-str)
  116.       (jsonrpc--log-event conn msg 'client))))
  117.  
  118. (cl-defmethod jsonrpc-shutdown ((conn naria2-jsonrpc-connection))
  119.   (websocket-close (oref conn -socket)))
  120.  
  121. (cl-defmethod jsonrpc-running-p ((conn naria2-jsonrpc-connection))
  122.   (websocket-openp (oref conn -socket)))
  123.  
  124. (cl-defmethod jsonrpc-connection-ready-p ((conn naria2-jsonrpc-connection)
  125.                                           _what)
  126.   (eq (websocket-ready-state (oref conn -socket)) 'open))
  127.  
  128. ;;; API
  129.  
  130. (defun naria2-jsonrpc-connect (host port &optional secure? secret)
  131.   "Connect to aria2 websocket server on PORT at HOST.
  132.  
  133. If SECURE? is non-nil, use secure connection instead of plain one.
  134.  
  135. SECRET must be a string if provided, it should match the `--rpc-secret' option
  136. of remote aria2c instance."
  137.   (cl-labels ((make-message-handler (conn-ref)
  138.                 (lambda (_ws frame)
  139.                   (let* ((text (websocket-frame-text frame))
  140.                          (msg (naria2-jsonrpc-parse-string text))
  141.                          (conn (gethash t conn-ref)))
  142.                     (jsonrpc-connection-receive conn msg)))))
  143.     (let* ((proto (if secure? "wss" "ws"))
  144.            (base-url (format "%s://%s:%d" proto host port))
  145.            (name (concat "ARIA2 " base-url))
  146.            (conn (make-instance 'naria2-jsonrpc-connection
  147.                                 :name name
  148.                                 :notification-dispatcher
  149.                                 #'naria2-jsonrpc--do-notification-dispatch))
  150.            (weak-ref
  151.              (let ((ht (make-hash-table :weakness 'value :test #'eq)))
  152.                (puthash t conn ht)
  153.                ht))
  154.            (finalizer (lambda ()
  155.                         (jsonrpc-shutdown conn))))
  156.       (let* ((handshake-url (concat base-url "/jsonrpc"))
  157.              (socket (websocket-open
  158.                       handshake-url
  159.                       :on-message (make-message-handler weak-ref))))
  160.         (setf (oref conn -socket) socket)
  161.         (setf (oref conn -secret) secret)
  162.         (setf (oref conn -finalizer) (make-finalizer finalizer)))
  163.       conn)))
  164.  
  165. (defun naria2-jsonrpc--do-notification-dispatch (conn event params)
  166.   "Run all handlers of EVENT from CONN with PARAMS."
  167.   (let* ((fullname (symbol-name event))
  168.          (name (string-remove-prefix "aria2." fullname))
  169.          (method-table (oref conn -notification-handlers-table)))
  170.     (dolist (fn (gethash name method-table))
  171.       (funcall fn params))))
  172.  
  173. (defun naria2-jsonrpc-on (conn event handler)
  174.   "Listen an EVENT from CONN with HANDLER.
  175.  
  176. Return a function run with no arguments to cancel the HANDLER."
  177.   (with-slots ((method-table -notification-handlers-table)) conn
  178.     (cl-callf nconc (gethash event method-table) (list handler))
  179.     (lambda ()
  180.       (cl-callf2 delq handler method-table))))
  181.  
  182. (defun naria2-jsonrpc--normalize-callbody (conn method params)
  183.   "Normalize the body of a call via CONN to remote METHOD with PARAMS.
  184.  
  185. Return (FINAL-METHOD . FINAL-PARAMS).
  186.  
  187. FINAL-METHOD is a string in format `aria2.%s'. where `%s' will be
  188. replaced by the stringified METHOD.
  189.  
  190. FINAL-PARAMS is PARAMS itself by default, but if a secret from CONN
  191. is provided, a secret token will be inserted at the head position of PARAMS."
  192.   (cl-flet ((stringify (elem)
  193.               (cl-etypecase elem
  194.                 (keyword (substring (symbol-name elem) 1))
  195.                 (symbol (symbol-name elem))
  196.                 (string elem))))
  197.     (let* ((final-method (concat "aria2." (stringify method)))
  198.            (secret (oref conn -secret))
  199.            ;; Tho weired, but see `aria2c(1)'.
  200.            (final-params `[,@(if secret (list (format "token:%s" secret)))
  201.                            ,@params]))
  202.       (cons final-method final-params))))
  203.  
  204. (defun naria2-jsonrpc-async-call (conn method params &rest args)
  205.   "Call remote aria2 METHOD with PARAMS via CONN asynchronously.
  206.  
  207. See `jsonrpc-async-request' for the documentation of ARGS.
  208.  
  209. \(fn CONN METHOD PARAMS &key SUCCESS-FN ERROR-FN TIMEOUT-FN TIMEOUT DEFERRED)"
  210.   (pcase-let ((`(,method . ,params)
  211.                 (naria2-jsonrpc--normalize-callbody conn method params)))
  212.     (apply #'jsonrpc-async-request conn method params args)))
  213.  
  214. (defun naria2-jsonrpc-sync-call (conn method params &rest args)
  215.   "Call remote aria2 METHOD with PARAMS via CONN synchronously.
  216.  
  217. See `jsonrpc-request' for the documentation of ARGS.
  218.  
  219. \(fn CONN METHOD PARAMS &key DEFERRED TIMEOUT CANCEL-ON-INPUT \
  220. CANCEL-ON-INPUT-RETVAL)"
  221.   (pcase-let ((`(,method . ,params)
  222.                 (naria2-jsonrpc--normalize-callbody conn method params)))
  223.     (apply #'jsonrpc-request conn method params args)))
  224.  
  225. (defun naria2-jsonrpc-notify (conn method params)
  226.   "Call remote aria2 METHOD with PARAMS via CONN, don't expect a return value."
  227.   (pcase-let ((`(,method . ,params)
  228.                 (naria2-jsonrpc--normalize-callbody conn method params)))
  229.     (jsonrpc-notify conn method params)))
  230.  
  231. (provide 'naria2-jsonrpc)
  232.  
  233. ;; Local Variables:
  234. ;; coding: utf-8
  235. ;; End:
  236.  
  237. ;;; naria2-jsonrpc.el ends here
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement