Emacs 自带的 url.el 包提供了网络请求的基本 API,但是问题比较多,比如 elfeed 的作者的 Elfeed, cURL, and You[1] 这篇文章就介绍了使用 cURL 代替 url-retrieve 的好处,主要有以下几点:
1.更快
• 早期的 url.el 中 DNS 查询是同步的,直到 25 版本才改成异步
• 更方便定义头信息,减少不必要的请求2.bug 更少
• url-retrieve 的 CALLBACK 可以会调用多次或零次
• 不用再去折腾 GnuTLS
• url.el 对 Windows 平台支持差
此外,在 Emacs 设置代理也比较局限,http 的还好,https/socks 的问题就有些多,一些问题链接:
• Make Emacs access to HTTPS over Socks proxy[2]
• Emacs 怎么使用代理[3]
• [吐槽] 新手劝退元凶: url.el #我们中出了一个叛徒[4]
如果连网络请求不通畅,那么诸如 eww、elfeed 等怎么会有好的体验?elfeed 还算好,提供了 curl 的支持,但很多包是不支持,因此最彻底的解决办法就是本文标题说的,直接用 curl 来作为 use-retrieve 的后端实现。
此外,curl 的代理支持也很简单, -x socks://127.0.0.1:1080
就可以指定 socks 代理,而且没有 bug!
上面介绍了使用 curl 的动机,那么如果实现呢?可以使用 around 机制来拦截 url.el 内部的接口,繁琐的地方在于解析 curl 的结果,让它符合 url.el 内部接口。
幸运的是,社区内已经有包解决这个问题了,它就是 mb-url[5] ,其前身是 curl-url-retrieve[6] 。在使用过程中,我发现了它的一些问题[7],目前需要使用我 fork 的版本[8]才能正确请求类似图片之类的二进制数据。下面给出使用 use-package 的配置,供读者参考:
(setq socks-proxy "socks://127.0.0.1:1080")
(use-package mb-url-http
:load-path "path to your local directory"
:defer t
:commands (mb-url-http-around-advice)
:init
(setq mb-url-http-backend 'mb-url-http-curl
mb-url-http-curl-switches `("--max-time" "20" "-x" ,socks-proxy))
(advice-add 'url-http :around 'mb-url-http-around-advice))
测试代码:
(let* ((buffer (url-retrieve-synchronously "https://emacstalk.github.io/images/logo.png"))
(data (with-current-buffer buffer
(goto-char (point-min))
(search-forward "\n\n")
(buffer-substring-no-properties (point) (point-max)))))
(insert-image (create-image data nil t))
(kill-buffer buffer))
执行上面的代码,如果展示出 logo 的图片,说明 mb-url 可以正确解析图片数据。
通过使用 mb-url 问题是可以得到解决,但是不仅会疑问,Emacs 当初为什么不直接集成 libcurl[9] ,而是选择自己来实现呢?git log 可以看到 url.el 的首次提交是 2004-05-04,根据 cURL Release Table[10] ,那时候大概是版本 7.11,不知道是不是当时的版本还不成熟?
搜了下 devel 邮件列表,最新的一次讨论是 Emacs HTTP libraries[11],看下来是建议先 profile 下现在 url.el 的问题所在,另起炉灶的可能性不大,还是以改进现有代码的方式为主,此外 RMS 也提到 curl 的集成也可以通过 fork/exec 的方式,这样更简单些。
[1]
Elfeed, cURL, and You: https://nullprogram.com/blog/2016/06/16/
[2]
Make Emacs access to HTTPS over Socks proxy: https://stackoverflow.com/questions/19699294/make-emacs-access-to-https-over-socks-proxy
[3]
Emacs 怎么使用代理: https://emacs-china.org/t/topic/2808
[4]
[吐槽] 新手劝退元凶: url.el #我们中出了一个叛徒: https://emacs-china.org/t/url-el/11276
[5]
mb-url: https://github.com/dochang/mb-url
[6]
curl-url-retrieve: https://github.com/nicferrier/curl-url-retrieve
[7]
问题: https://github.com/dochang/mb-url/issues/5
[8]
fork 的版本: https://github.com/jiacai2050/mb-url
[9]
libcurl: https://curl.se/libcurl/
[10]
cURL Release Table: https://curl.se/docs/releases.html
[11]
Emacs HTTP libraries: https://lists.gnu.org/archive/html/emacs-devel/2020-12/msg01291.html