相信很多人都被 url.el 坑过而没有意识到,通常第一反应是:上代理、改 tls。然而未必奏效,因为敌在本能寺。
据我不缜密的观察,这种情况直到 26.3 (都快到 9102 年了) 才有所改善。
实际上 25.3 以及之前版本的代理是没法用的:
(with-emacs-25.3
(require 'tls)
(with-eval-after-load 'tls
(push "/private/etc/ssl/cert.pem" gnutls-trustfiles))
(setq tls-checktrust t)
(let ((url-proxy-services
'(("http" . #1="127.0.0.1:8123")
("https" . #1#))))
(with-current-buffer
(url-retrieve-synchronously "https://gnu.org")
(buffer-string))))
;; =>
;; "HTTP/1.1 400 Couldn't parse URL
;; Connection: close
;; Date: Sat, 23 Nov 2019 13:34:14 GMT
;; Content-Type: text/html
;; Content-Length: 453
;; Expires: 0
;; Cache-Control: no-cache
;; Pragma: no-cache
;;
;; <!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">
;; <html><head>
;; <title>Proxy error: 400 Couldn't parse URL.</title>
;; </head><body>
;; <h1>400 Couldn't parse URL</h1>
;; <p>The following error occurred while trying to access <strong>https://gnu.org</strong>:<br><br>
;; <strong>400 Couldn't parse URL</strong></p>
;; <hr>Generated Sat, 23 Nov 2019 21:34:14 CST by Polipo on <em>mbp33.lan:8123</em>.
;; </body></html>
;; "
这个 BUG 早在 2012 年就存在了,直到 26.1 发布的时候才修复:
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=11788
GNU bug report logs - #11788
url-http does not properly handle https over proxy
Previous Next
Package: emacs;
Reported by: Andreas Schwab <schwab linux-m68k.org>
Date: Tue, 26 Jun 2012 10:25:02 UTC
Owned by: Magnus Henoch <mange freemail.hu>
Severity: wishlist
Tags: fixed, patch
Merged with 10, 12636
Found in version 24.2.50
Fixed in version 26.1
Done: Lars Magne Ingebrigtsen <larsi gnus.org>
Bug is archived. No further changes may be made.
没想到 26.2 类似的问题又重现了:
(with-emacs-26.2
;; Fix 400 issue
;; (setq gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3")
(with-current-buffer
(url-retrieve-synchronously "https://www.adafruit.com/feed/quotes.xml")
(buffer-string)))
;; =>
;; "HTTP/1.1 400 Bad Request
;; Server: cloudflare
;; Date: Sat, 23 Nov 2019 09:32:25 GMT
;; Content-Type: text/html
;; Content-Length: 253
;; Connection: close
;; CF-RAY: -
;;
;; <html>
;; <head><title>400 The plain HTTP request was sent to HTTPS port</title></head>
;; <body>
;; <center><h1>400 Bad Request</h1></center>
;; <center>The plain HTTP request was sent to HTTPS port</center>
;; <hr><center>cloudflare</center>
;; </body>
;; </html>
;; "
然后 26.3 修复了。接下来 27.1 会不会再出幺儿子,拭目以待。
1 个赞
cireu
2
为什么url.el
底层不用libcurl
?libcurl一直很稳定,难道是libcurl不够清真?
cireu
3
url.el 配合TLS用的时候是有问题的。参见
解决方案是手动设置TLS加密强度
(let ((gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3"))
...)
我的init.el就有这么一行,忘了什么时候看到的了。
「HTTPS 代理问题 #11788 」和「TLS 问题 #34341」没关系吧?
HTTPS 代理问题是因为 url-proxy 代理的时候,没考虑代理 HTTPS 时需要用 HTTP Connect 方法。我前两天给 Emacs url.el 配置 PAC 的时候,发现 url-proxy 实际也不支持SOCKS 代理,url-find-proxy-for-url
有提到 SOCKS 代理,但是 url-proxy
却只支持 HTTP 代理。
至于 TLS 问题,我就一窍不通了,而且也几乎没困扰到我。
(新的)http协议挺复杂,最好是用现成的C库。自己实现太刚了。
不知道怎么想的,我看 request.el 就很好啊。
我前面例子有这一行,为了抛错特意注释掉了。
bing
8
后端用gnutls很不方便,我配置文件里用url-retrieve下载一些文件。每次用新机器初始化emacs配置,通常默认不安装gnutls,代码走到url-retrieve就报错了
私以为底层还是用标准的 C/C++库,甚至 rust,可移植性强、性能高,上层只需要封装就好。
cireu
11
第三方的可以绑定一下libcurl,内置的url.el估计没救了,url.el也是用lisp实现了大部分逻辑,基于Emacs底层的network stream机制。和libcurl这种已经封装好高级逻辑的不同。
在emacs里访问socks服务器有什么api可用吗?或者调用linux的命令行?
可以,SOCKS 是 TCP 层协议,所以能建立 TCP Socket 就行,make-network-process
支持 UDP、TCP 和 Unix domain socket,不支持 Raw socket。
而且 Emacs 自带了一个 SOCKS 5 客户端 socks.el,但我刚刚试了试没成功,没搞明白什么情况,连 HTTP 也不行。而且它的代码质量也不高。
发现我把 socks-server
设置错了,改正后可用,下面用 socks.el 访问 example.com
;; $ ssh -D localhost
;; $ curl -x socks5://localhost:8080 -v -I example.com
(setq socks-server '("Default server" "0.0.0.0" 8080 5))
;; => ("Default server" "0.0.0.0" 8080 5)
(socks-open-network-stream "hello" "*hello*" "example.com" 80)
;; => #<process socks>
(process-send-string (get-process "socks")
"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
;; => nil
;; 打开 *hello* 查看 HTML
不清楚如何测试 HTTPS。
1 个赞
这就相当于:
(let ((socks-server '("Default server" "0.0.0.0" 8080 5))
(url-gateway-method 'socks))
(display-buffer
(url-retrieve-synchronously "http://example.com")))
如果请求的是 HTTPS,应该也调用同样的函数:url-retrieve -> url-open-stream -> socks-open-network-stream
。
1 个赞
应该是的。
socks.el + url.el 的一个问题是 DNS 还是在用户电脑上完成的,而不通过 SOCKS(SOCKS 5 支持 UDP,DNS 使用 UDP),所以 DNS 有问题的话,网站照样访问不了。Mac 下,系统设置开启 SOCKS 代理之后,Chrome 和 Safari 的 DNS 也会走 SOCKS 代理,但 FireFox 貌似不会。
2 个赞
这段不必划掉,socks.el 的实现或许真的有问题,又或是在使用 socks-open-network-stream
之前有些前置的条件必须设置而文档没有说明。
之所以这样说,是因为我在一个运行了很久、跑了各种测试片段的 Emacs 中执行 (socks-open-network-stream "hello" "*hello*" "example.com" 80)
是成功的,但是开启一个全新的 Emacs 却始终失败,而且提示信息也未反映出失败的真正原因,可见这个失败是意料之外的情况。
以下代码中的断言在我电脑(macOS 10.12.6, Emacs 25.1~27.0)上 100% 成立:
;;; test-socks.el --- Test socks -*- lexical-binding: t; -*-
;; 2019-12-20 18.34.04
(toggle-debug-on-error)
(require 'cl-macs)
(require 'socks)
(let ((ver (string-to-number emacs-version)))
(cl-assert (<= 25.1 ver) nil (format "Expected 25.1 or newer, actual %s" ver)))
(setq socks-server (list "V2Ray" "127.0.0.1" 7891 5))
(cl-assert (equal '(wrong-type-argument number-or-marker-p nil)
(condition-case err
(socks-open-network-stream "example" "*example*" "example.com" 80)
(error
err))))
(cl-assert (setq proc (url-open-stream "example" "*example*" "example.com" 80 'socks)))
(message "==> process status: %s" (process-status proc))
;; Local Variables:
;; quickrun-option-cmd-alist: ((:command . "/bin/bash")
;; (:exec . ("emacs-nightly -Q -l %s --batch")))
;; End:
;;; test-socks.el ends here
socks-open-network-stream
失败的原因也许可以从更上层的 url-open-stream
中找到端倪。