Sucha's Blog ~ Archive for July, 2020

20年7月25日 周四 11:22

Curl 的 Lua 绑定,以及 multi 接口

尝试了一下 curl 发送请求的 multi 接口,另外的 easy 接口是一个阻塞的设计,将 easy handle 放到 multi 中后,变成了非阻塞的接口。

大概是下面这样的逻辑,是非阻塞的,目前遇到的遗憾,是 perform 以及 wait 都没法知道具体是哪一个 easy handle 请求结束了。

easy = curl.easy()
easy:setopt(...) -- custom request header, response header, body reader
multi = curl.multi()
multi.add_handle(easy)
repeat
    multi.perform()
untile multi.wait() <= 0

另外还试了一下 curl 中的 event-driven 接口,其实也还是 multi,但是区别于 multi.wait() 基于 底层 select 的设置,这里的 event-driven,依赖比如 libuv、libevent,以及我想集合进去的 mnet 的 事件循环。

创建这种 event-driven 的 curl 程序,还需要需要底层提供一个 timeout 的回调,也许是创建 socket connect 后, 回调具体的 fd,有了这个 fd 才能将其加入到 epoll、kqueue 里面去。

但是我自己验证的结果,感觉程序上跟 github 里面的 example 已经很像了,但一些请求没能成功。

这种方式需要设置好几个回调,在这个基础上设计中间件太复杂了,不管是输入的 header、post data,还是回调拿到 header、 body data 都太复杂了。

另外这种 event-driven 的方式还有一些隐含的条件,比如 timer 是独立于 fd 的,在 fd 前面就创建了,而且 timeout 的单位是 micro seconds,这其实是一个精度很高的 timer 了。

这里我也有疑惑,比如对于 curl 来说,这里的 event-driven 只是借用了 epoll、kqueue 的内核事件机制,我不大理解为何需要由 curl 来 创建 socket,为何不是更底层来管理 socket fd,其实参数交给 curl 来设置都可以,底层只是保证非阻塞就可以了。而 curl 更多的是面向 HTTP 这一层的中间件,raw data <-> HTTP structure data 的转换。

但不管怎么说,curl 这套东西还是太香了,毕竟每个发行版都有,每个发行版安装 dev 或者 command line tool 之后,有了头文件,就可以立马基于 libcurl 这个库进行发开发了。

因为自测 event-driven 没成功,相关信息也比较少,最后是用了上面 multi 接口包裹 easy 接口的非阻塞式设计,用了 Lua-cURLv3 这个库,就请求而言,单独使用了 select。

20年7月25日 周四 11:22

HTML 的 Content-Type: multipart/form-data

研究了一下,Mac 下轮着 Safari、Chrome、FireFox 前后台调通后,这里大概记录一下吧。

前端页面 HTML

在前端 HTML 的写法是,form 里面指定 enctype 为 "multipart/form-data",如下

<form class="cell" action="" method="POST" enctype="multipart/form-data">
    <input type="file" name="myfile" />
    <input type="submit" />
</form>

后端解析

假设在浏览器里面点击 "file" 按钮,选择发送的是一个 PDF 文件,协议上收到是下面这样的,... 是省略:

POST /path HTTP/1.1
...
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary5WeF4Lu8GyQKEIE5
Accept-Language: zh-cn

------WebKitFormBoundary5WeF4Lu8GyQKEIE5
Content-Disposition: form-data; name="myfile"; filename="noname.pdf"
Content-Type: application/pdf

%PDF-1.3
...
%%EOF

------WebKitFormBoundary5WeF4Lu8GyQKEIE5--

我自己没有试过多文件,但是从网上资料看,多文件跟单文件不同的是,前面一个 boundary 结束符后,再来一个 boundary 的 Disposition 描述分割,开启下一个数据块的传输。

代码部分就不贴了,感觉有点挫,考虑了多文件分割,以及需要传递给用户文件名、类型等信息,最后是使用了阶段状态的描述,毕竟后端收到的数据是不定长的输入。

--

最后还是整了一下代码,加入了 builder,在 multipart-formdata-lib.

20年7月16日 周四 12:04

LuaRocks with LuaJIT

标准版的 LuaRocks 只有原版的 Lua 可以配套使用,torch 为了让 LuaRocks 能够支持 LuaJIT,修改了一个版本,其实只是修改了编译过程,毕竟 LuaJIT 完整支持 Lua 5.1 的语法。

torch 的版本支持 LuaJIT 的各个版本,但最新的只到 2.1 beta2。早上我将 LuaJIT 的支持版本升级到了 2.1 beta3,地址是 luajit-rocks,然后还发了一个 pull request。但是这个项目有好几年没有更新了,不晓得会不会有 maintainer 会注意到。

torch 的 luajit 其实还有修改 2 个地方,一个是 string 的 hash 方案,一个加入了交互用的 readline。交互部分明显有改善,而 hash 部分不晓得为什么要这么改,应该也还好吧,反正为了 pull request 这里我是不敢动的。hash 用的是 tommy 的方案

在 Linux 中编译了一把,顺利通过,后面再看需要安装吧。在自己的 MacBook Pro 上安装好后,install 了 busted 和 moonscript。不得不说,其实是为了试一下 moonscript,硬是找到了 LuaRocks 的 LuaJIT based 方案。

先用一阵子再说吧。

20年7月15日 周三 22:38

MacOS 下使用 BitBar 在状态栏自定义信息

如果自己有一些信息需要通过点击状态栏图标后获得,或者通过点击状态栏图标启动、关闭的,都可以使用这个工具 BitBar 来操作。

安装好后,只需要定义 plugin 目录,然后在 plugin 目录下加入功能脚本就可以了。脚本其实只关注输出的文本,操作部分其实是通过特殊命令来定义的。

比如例子中的 refresh.sh,精简后是下面这样:

if [[ "$1" = "restart" ]]; then
osascript <<EOD
	tell application "BitBar" to quit
	delay 1
	tell application "BitBar" to activate
EOD
fi

echo "↻"
echo "---"
echo "Refresh Me| terminal=false refresh=true"
echo "Restart Bitbar| bash='$0' param1=restart terminal=false";

会在状态栏显示一个圆圈刷新,点击后下拉菜单会有 "Refresh Me" 以及 "Restart Bitbar"。

可惜的是我虽然弄懂了一点点如何编写这个 Bitbar 的 plugin 命令,但是通过 Bitbar 控制的程序最后没有符合我的预期,:-(

20年7月14日 周二 11:34

GitHub 定义 Repo 语言

之前写的 LWTheme 兼顾 Swift/ObjC 语言,GitHub 改版后,在 repo 里面没有语言百分比的显示了。

搜了一下,可以在 branch 里面定义 .gitattributes 文件,里面这样写

*.swift linguist-language=Swift

自测了一把,网站只会读取 default branch 里面的 .gitattributes,切换其他 branch 没有用。

20年7月9日 周四 15:44

OpenVPN 配置记录

其实考虑过几个比如 ShadowSocks 之类的,但是很不稳定,考虑最后也许还是用 VPN 来得稳妥一些,折腾了半天,终于跑通,先这里随便记录一下吧。

大概分两个部分,一个是服务端的配置,一个是客户端的配置。

客户端我在 Mac 下用的是 TunnelBlick,速度其实一般,还没长时间使用,后续先观察一阵。