diff options
| author | F. Jason Park | 2025-01-20 16:32:53 -0800 |
|---|---|---|
| committer | F. Jason Park | 2025-01-22 16:07:55 -0800 |
| commit | 331bcfaee51f7fa5ff0f6046f30e940452f3a8fe (patch) | |
| tree | 465d0c3c148c26f72249c32962678fcce7260100 /lisp/erc | |
| parent | d6b210aa09d6a5464c91e71460d9852a4445d46e (diff) | |
| download | emacs-331bcfaee51f7fa5ff0f6046f30e940452f3a8fe.tar.gz emacs-331bcfaee51f7fa5ff0f6046f30e940452f3a8fe.zip | |
Reuse client cert for connectivity probing in ERC
* lisp/erc/erc-backend.el (erc--server-connect-function)
(erc--server-post-dial-function): Rename former to latter because the
existing name is better suited for the eventual generalizing of
`erc-server-connect' in a future version.
(erc-server-connect): Use new name for `erc--server-connect-function',
`erc--server-post-dial-function'.
(erc--recon-probe-reschedule, erc--recon-probe-sentinel)
(erc--recon-probe-filter, erc--recon-probe-check): New functions
factored out of `erc-server-delayed-check-reconnect'.
(erc-server-delayed-check-reconnect): Refactor, splitting off lambdas
into top-level functions for improved tracing.
* lisp/erc/erc.el (erc-message-english-recon-probe-hung-up)
(erc-message-english-recon-probe-nobody-home): New
variables. (Bug#62044)
Thanks to Libera.Chat user arjan for reporting this bug, which
is new in ERC 5.6 and Emacs 30.1.
Diffstat (limited to 'lisp/erc')
| -rw-r--r-- | lisp/erc/erc-backend.el | 193 | ||||
| -rw-r--r-- | lisp/erc/erc.el | 2 |
2 files changed, 119 insertions, 76 deletions
diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el index 84c22d840fb..713547a591a 100644 --- a/lisp/erc/erc-backend.el +++ b/lisp/erc/erc-backend.el | |||
| @@ -716,7 +716,8 @@ The current buffer is given by BUFFER." | |||
| 716 | (run-hooks 'erc--server-post-connect-hook) | 716 | (run-hooks 'erc--server-post-connect-hook) |
| 717 | (erc-login)) | 717 | (erc-login)) |
| 718 | 718 | ||
| 719 | (defvar erc--server-connect-function #'erc--server-propagate-failed-connection | 719 | (defvar erc--server-post-dial-function |
| 720 | #'erc--server-propagate-failed-connection | ||
| 720 | "Function called one second after creating a server process. | 721 | "Function called one second after creating a server process. |
| 721 | Called with the newly created process just before the opening IRC | 722 | Called with the newly created process just before the opening IRC |
| 722 | protocol exchange.") | 723 | protocol exchange.") |
| @@ -795,7 +796,7 @@ TLS (see `erc-session-client-certificate' for more details)." | |||
| 795 | (let ((erc--msg-prop-overrides `((erc--skip . (stamp)) | 796 | (let ((erc--msg-prop-overrides `((erc--skip . (stamp)) |
| 796 | ,@erc--msg-prop-overrides))) | 797 | ,@erc--msg-prop-overrides))) |
| 797 | (erc-display-message nil nil buffer "Opening connection..\n") | 798 | (erc-display-message nil nil buffer "Opening connection..\n") |
| 798 | (run-at-time 1 nil erc--server-connect-function process)) | 799 | (run-at-time 1 nil erc--server-post-dial-function process)) |
| 799 | (message "%s...done" msg) | 800 | (message "%s...done" msg) |
| 800 | (erc--register-connection)))) | 801 | (erc--register-connection)))) |
| 801 | 802 | ||
| @@ -846,89 +847,129 @@ Make sure you are in an ERC buffer when running this." | |||
| 846 | "Double EXISTING timeout, but cap it at 5 minutes." | 847 | "Double EXISTING timeout, but cap it at 5 minutes." |
| 847 | (min 300 (* existing 2))) | 848 | (min 300 (* existing 2))) |
| 848 | 849 | ||
| 849 | ;; This may appear to hang at various places. It's assumed that when | 850 | (defun erc--recon-probe-reschedule (proc) |
| 850 | ;; *Messages* contains "Waiting for socket ..." or similar, progress | 851 | "Print a message saying PROC's intended peer can't be reached. |
| 851 | ;; will be made eventually. | 852 | Then call `erc-schedule-reconnect'." |
| 852 | 853 | (let ((buffer (process-buffer proc))) | |
| 854 | (when (buffer-live-p buffer) | ||
| 855 | (with-current-buffer buffer | ||
| 856 | (let ((erc-server-reconnect-timeout erc--server-reconnect-timeout)) | ||
| 857 | ;; FIXME either remove this deletion or explain why the one | ||
| 858 | ;; performed by `erc-schedule-reconnect' is insufficient. | ||
| 859 | ;; Perhaps because `proc' may not equal `erc-server-process'? | ||
| 860 | (when proc ; conn refused w/o :nowait | ||
| 861 | (delete-process proc)) | ||
| 862 | (erc-display-message nil '(notice error) buffer | ||
| 863 | 'recon-probe-nobody-home) | ||
| 864 | (erc-schedule-reconnect buffer 0)))))) | ||
| 865 | |||
| 866 | (defun erc--recon-probe-sentinel (proc event) | ||
| 867 | "Send a \"PING\" to PROC's peer on an \"open\" EVENT. | ||
| 868 | Otherwise, try connecting from scratch again after timeout." | ||
| 869 | (pcase event | ||
| 870 | ("open\n" | ||
| 871 | (let ((cookie (time-convert nil 'integer))) | ||
| 872 | (process-put proc 'erc--reconnect-cookie cookie) | ||
| 873 | ;; FIXME account for possible `file-error' when sending. | ||
| 874 | (run-at-time nil nil #'process-send-string proc | ||
| 875 | (format "PING %d\r\n" cookie)))) | ||
| 876 | ((and "connection broken by remote peer\n" | ||
| 877 | (guard (process-get proc 'erc--reconnect-cookie)) | ||
| 878 | (let buffer (process-buffer proc)) | ||
| 879 | (guard (buffer-live-p buffer))) | ||
| 880 | ;; This can run, for example, if the client dials a TLS-terminating | ||
| 881 | ;; endpoint with a non-TLS opener, like `erc-open-tls-stream', or | ||
| 882 | ;; if the server doesn't take kindly to an opening "PING" during | ||
| 883 | ;; connection registration. | ||
| 884 | (with-current-buffer buffer | ||
| 885 | (delete-process proc) | ||
| 886 | ;; Undo latest penalizing timeout increment. | ||
| 887 | (setq erc--server-reconnect-timeout | ||
| 888 | (max 1 (/ erc--server-reconnect-timeout 2))) | ||
| 889 | (erc-display-message nil '(notice error) buffer 'recon-probe-hung-up | ||
| 890 | ?t erc--server-reconnect-timeout) | ||
| 891 | (run-at-time erc--server-reconnect-timeout | ||
| 892 | nil #'erc-server-delayed-reconnect buffer))) | ||
| 893 | ((or "connection broken by remote peer\n" (rx bot "failed")) | ||
| 894 | (run-at-time nil nil #'erc--recon-probe-reschedule proc)))) | ||
| 895 | |||
| 896 | (defun erc--recon-probe-filter (proc string) | ||
| 897 | "Reconnect, reusing PROC if STRING contains a \"PONG\"." | ||
| 898 | (when-let* ((buffer (process-buffer proc)) | ||
| 899 | (buffer-live-p buffer)) | ||
| 900 | (with-current-buffer buffer | ||
| 901 | (setq erc--server-reconnect-timeout nil)) | ||
| 902 | (if-let* ; reuse proc if string has complete message | ||
| 903 | ((cookie (process-get proc 'erc--reconnect-cookie)) | ||
| 904 | ;; Accommodate a leading ":<source> ". | ||
| 905 | ((string-suffix-p (format "PONG %d\r\n" cookie) string))) | ||
| 906 | (progn | ||
| 907 | (erc-log-irc-protocol string nil) | ||
| 908 | (set-process-sentinel proc #'ignore) | ||
| 909 | (set-process-filter proc nil) | ||
| 910 | (run-at-time nil nil #'erc-server--reconnect-opened buffer proc)) | ||
| 911 | (delete-process proc) | ||
| 912 | (run-at-time nil nil #'erc-server-delayed-reconnect buffer)))) | ||
| 913 | |||
| 914 | (defun erc--recon-probe-check (proc tmrx) | ||
| 915 | "Restart auto-reconnect probe if PROC has failed or TIMER has EXPIRE'd. | ||
| 916 | Expect TMRX to be a cons cell of (EXPIRE . TIMER)." | ||
| 917 | (let* ((status (process-status proc)) | ||
| 918 | (expiredp (time-less-p (pop tmrx) (current-time))) | ||
| 919 | (buffer (process-buffer proc))) | ||
| 920 | (when (or expiredp | ||
| 921 | (not (eq 'connect status)) ; e.g., `closed' | ||
| 922 | (not (buffer-live-p buffer))) | ||
| 923 | (cancel-timer tmrx)) | ||
| 924 | (cond ((not (buffer-live-p buffer))) | ||
| 925 | (expiredp | ||
| 926 | (erc-display-message nil 'error buffer "Timed out while dialing...") | ||
| 927 | (delete-process proc) | ||
| 928 | (erc--recon-probe-reschedule proc)) | ||
| 929 | ((eq 'failed status) | ||
| 930 | (erc--recon-probe-reschedule proc))))) | ||
| 931 | |||
| 932 | ;; This probing strategy may appear to hang at various junctures. It's | ||
| 933 | ;; assumed that when *Messages* contains "Waiting for socket ..." or | ||
| 934 | ;; similar, progress will be made eventually. | ||
| 853 | (defun erc-server-delayed-check-reconnect (buffer) | 935 | (defun erc-server-delayed-check-reconnect (buffer) |
| 854 | "Wait for internet connectivity before trying to reconnect. | 936 | "Wait for internet connectivity before trying to reconnect. |
| 855 | Use server BUFFER's cached session info to reestablish the logical | 937 | Use server BUFFER's cached session info to reestablish the logical |
| 856 | connection at the IRC protocol level. Do this by probing for a | 938 | connection at the IRC protocol level. Do this by probing for any |
| 857 | successful response to a PING before commencing with \"connection | 939 | response to a PING, including a hang up, before (possibly) dialing again |
| 858 | registration\". Do not distinguish between configuration problems and | 940 | and commencing with \"connection registration\". Make no distinction |
| 859 | the absence of service. For example, expect users of proxy-based | 941 | between configuration issues and the absence of service in printed |
| 860 | connectors, like `erc-open-socks-tls-stream', to ensure their setup | 942 | feedback. For example, expect users of proxy-based connectors, like |
| 861 | works before choosing this function as their reconnector." | 943 | `erc-open-socks-tls-stream', to ensure their setup works before choosing |
| 944 | this function as their reconnector." | ||
| 862 | (when (buffer-live-p buffer) | 945 | (when (buffer-live-p buffer) |
| 863 | (with-current-buffer buffer | 946 | (with-current-buffer buffer |
| 864 | (setq erc--server-reconnect-timeout | 947 | (setq erc--server-reconnect-timeout |
| 865 | (funcall erc--server-reconnect-timeout-scale-function | 948 | (funcall erc--server-reconnect-timeout-scale-function |
| 866 | (or erc--server-reconnect-timeout | 949 | (or erc--server-reconnect-timeout |
| 867 | erc-server-reconnect-timeout))) | 950 | erc-server-reconnect-timeout))) |
| 868 | (let* ((reschedule (lambda (proc) | 951 | (condition-case _ |
| 869 | (when (buffer-live-p buffer) | 952 | (let* ((cert erc-session-client-certificate) |
| 870 | (with-current-buffer buffer | 953 | (tmrx (list (time-add erc--server-reconnect-timeout-check |
| 871 | (let ((erc-server-reconnect-timeout | 954 | (current-time)))) |
| 872 | erc--server-reconnect-timeout)) | 955 | (server (if (string-match erc--server-connect-dumb-ipv6-regexp |
| 873 | (when proc ; conn refused w/o :nowait | 956 | erc-session-server) |
| 874 | (delete-process proc)) | 957 | (match-string 1 erc-session-server) |
| 875 | (erc-display-message nil 'error buffer | 958 | erc-session-server)) |
| 876 | "Nobody home...") | 959 | (proc (apply erc-session-connector "*erc-connectivity-check*" |
| 877 | (erc-schedule-reconnect buffer 0)))))) | 960 | nil server erc-session-port |
| 878 | (conchk-exp (time-add erc--server-reconnect-timeout-check | 961 | (and cert (list :client-certificate cert))))) |
| 879 | (current-time))) | 962 | (setcdr tmrx (run-at-time 1 1 #'erc--recon-probe-check proc tmrx)) |
| 880 | (conchk-timer nil) | 963 | (set-process-filter proc #'erc--recon-probe-filter) |
| 881 | (conchk (lambda (proc) | 964 | (set-process-sentinel proc #'erc--recon-probe-sentinel) |
| 882 | (let ((status (process-status proc)) | 965 | (set-process-buffer proc buffer) |
| 883 | (xprdp (time-less-p conchk-exp (current-time)))) | 966 | ;; Should `erc-server-process' also be set to `proc' here so |
| 884 | (when (or xprdp (not (eq 'connect status))) | 967 | ;; that `erc-schedule-reconnect' can use it? |
| 885 | (cancel-timer conchk-timer)) | 968 | (cl-assert (processp proc)) |
| 886 | (when (buffer-live-p buffer) | 969 | (when (eq (process-status proc) 'open) ; :nowait is nil |
| 887 | (cond (xprdp (erc-display-message | 970 | (erc--recon-probe-sentinel proc "open\n"))) |
| 888 | nil 'error buffer | 971 | ;; E.g., "make client process failed" "Connection refused". |
| 889 | "Timed out while dialing...") | 972 | (file-error (erc--recon-probe-reschedule nil)))))) |
| 890 | (delete-process proc) | ||
| 891 | (funcall reschedule proc)) | ||
| 892 | ((eq 'failed status) | ||
| 893 | (funcall reschedule proc))))))) | ||
| 894 | (sentinel (lambda (proc event) | ||
| 895 | (pcase event | ||
| 896 | ("open\n" | ||
| 897 | (let ((cookie (time-convert nil 'integer))) | ||
| 898 | (process-put proc 'erc--reconnect-cookie cookie) | ||
| 899 | (run-at-time nil nil #'process-send-string proc | ||
| 900 | (format "PING %d\r\n" cookie)))) | ||
| 901 | ((or "connection broken by remote peer\n" | ||
| 902 | (rx bot "failed")) | ||
| 903 | (run-at-time nil nil reschedule proc))))) | ||
| 904 | (filter (lambda (proc string) | ||
| 905 | (with-current-buffer buffer | ||
| 906 | (setq erc--server-reconnect-timeout nil)) | ||
| 907 | (if-let* ; reuse proc if string has complete message | ||
| 908 | ((cookie (process-get proc 'erc--reconnect-cookie)) | ||
| 909 | ((string-suffix-p (format "PONG %d\r\n" cookie) | ||
| 910 | string))) ; leading ":<source> " | ||
| 911 | (progn | ||
| 912 | (erc-log-irc-protocol string nil) | ||
| 913 | (set-process-sentinel proc #'ignore) | ||
| 914 | (set-process-filter proc nil) | ||
| 915 | (run-at-time nil nil | ||
| 916 | #'erc-server--reconnect-opened | ||
| 917 | buffer proc)) | ||
| 918 | (delete-process proc) | ||
| 919 | (run-at-time nil nil #'erc-server-delayed-reconnect | ||
| 920 | buffer))))) | ||
| 921 | (condition-case _ | ||
| 922 | (let ((proc (funcall erc-session-connector | ||
| 923 | "*erc-connectivity-check*" nil | ||
| 924 | erc-session-server erc-session-port))) | ||
| 925 | (setq conchk-timer (run-at-time 1 1 conchk proc)) | ||
| 926 | (set-process-filter proc filter) | ||
| 927 | (set-process-sentinel proc sentinel) | ||
| 928 | (when (eq (process-status proc) 'open) ; :nowait is nil | ||
| 929 | (funcall sentinel proc "open\n"))) | ||
| 930 | ;; E.g., "make client process failed" "Connection refused". | ||
| 931 | (file-error (funcall reschedule nil))))))) | ||
| 932 | 973 | ||
| 933 | (defun erc-server-prefer-check-reconnect (buffer) | 974 | (defun erc-server-prefer-check-reconnect (buffer) |
| 934 | "Defer to another reconnector based on BUFFER's `erc-session-connector'. | 975 | "Defer to another reconnector based on BUFFER's `erc-session-connector'. |
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index 28ccb5809a7..72335a444cb 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el | |||
| @@ -9572,6 +9572,8 @@ SOFTP, only do so when defined as a variable." | |||
| 9572 | (ignore-list . "%-8p %s") | 9572 | (ignore-list . "%-8p %s") |
| 9573 | (reconnecting . "Reconnecting in %ms: attempt %i/%n ...") | 9573 | (reconnecting . "Reconnecting in %ms: attempt %i/%n ...") |
| 9574 | (reconnect-canceled . "Canceled %u reconnect timer with %cs to go...") | 9574 | (reconnect-canceled . "Canceled %u reconnect timer with %cs to go...") |
| 9575 | (recon-probe-hung-up . "Server answered but hung up. Delaying by %ts...") | ||
| 9576 | (recon-probe-nobody-home . "Nobody home...") | ||
| 9575 | (finished . "\n\n*** ERC finished ***\n") | 9577 | (finished . "\n\n*** ERC finished ***\n") |
| 9576 | (terminated . "\n\n*** ERC terminated: %e\n") | 9578 | (terminated . "\n\n*** ERC terminated: %e\n") |
| 9577 | (login . "Logging in as `%n'...") | 9579 | (login . "Logging in as `%n'...") |