2016-08-24 20:09:53

Node.js: 不支持自定义HTTP Verb

准备搭一个日历服务器.

日历服务器的通用标准CalDav是一种标准的WebService,以极其严格的HTTP动词限制与XML格式数据著称.(老实说,习惯了RESTful后,怎么看WebService就怎么不顺眼)

HTTP Verb

CalDav要求使用的HTTP Verb有以下几种

propfind
proppatch
options
report
put
get
delete
move
mkcalendar

可能很多人从来没用过其中的一些HTTP Verb.实际上,HTTP/1.1标准提供了十几个标准的动词,除了常见的get put patch delete之外,propfind之类也是被包含在其中的.

但是,眼尖的同学可能已经注意到了,mkcalendar怎么也不可能是1.1里的标准动词.幸运的是,HTTP/1.1允许任意扩展HTTP动词,因此我们自己定义mkcalendar应该是可行的.

然而使用node.js的话,答案是node版本低于v4不可行,不然就没本文了

测试node支持的动词

为了演示,在低于v4版本的node环境中,使用如下代码创建一个最原始的HTTP服务器

var http = require("http");

http.createServer(function(request, response) {
    response.writeHead(200, {"Content-Type": "text/plain"});
    response.write("Hello World " + request.method);
    console.log("Method: "+ request.method);
    response.end();
}).listen(8888);

node运行之后,使用curl命令来测试动词. Windows下可以使用git bash, git bash自带很多linux命令, 比如常用的lstouch,其中就包括了curl

使用如下命令发出GET请求

curl -X GET 127.0.0.1:8888

这里应该不会出错

然而,如果使用其他动词,例如

curl -X MKCALENDAR 127.0.0.1:8888

结果是这样的:

不仅没有回应, 连node那边的控制台也没有任何输出

这说明,自定义的动词被Node服务器直接拒绝了,到底发生了什么?

源码

经过查询node源码中,控制HTTP动词在Http_parser中,两个链接如下

node支持的http动词

node的遗留黑历史

可以看到,node的http动词居然是白名单模式的,这也说明了为什么自定义动词连最低层的HTTP服务器都接受不到,因为直接被http_parser给拒绝掉了.

另外,上面第二个链接中可以看到,最初node解析http动词的时候,竟然是只解析第一个字符,excuse me???

早在几年前,就有人发现了这个极具吐槽性的问题,并且用GEM这种自定义动词得到了node的GET的回应,之后http_parser修正了这个问题,这也是为什么我们上面的MKCALENDAR失败了

因此,基于低版本的NODE的服务器从源头上已经不可能支持自定义HTTP动词了.这从底层直接干掉了用Node来写CalDav的服务器

v4.x.x的现状

v4更新了http_parser,添加了MKCALENDAR的支持,链接在这里:

http_parser.h

http_parser.c

但是,如你在源码中所见的,node仍然没有支持自定义动词,还是白名单过滤模式

低版本取巧的办法

低版本中,我们可以用代理的办法,让NGINX帮我们把mkcalendar转发到其他路由或者重载为其他动词,再交给Node处理

但这已经不是Node的范畴了,而且额外的做法对生产环境也极其不利.

另外一种办法更加hack, 原理是使用纯http模块创建原生服务器,并在每次连接的时候检查二进制流,将mkcalendar动词截取出来后替换为其它node支持的动词.

缺陷也很大, 如果使用的是express这些框架的话, 还需要自行修改框架的源码,在每次连接的时候进行替换.实例源码

本文链接:https://smallpath.me/post/node-http-verb

-- EOF --