cowboy的一个小陷阱

我在用cowboy时遇到了一个麻烦,通过HTTP连接获取数据时不能用的HTTP/1.1的持久连接(persistent connection)进行数据交换,每个请求-响应过程必须断开连接后重新连才行,也就是说只有Connection:close时才能正常工作。

用wireshark看连接的数据包发现一个奇怪的现象,就是每个响应的最后都会紧跟着出现一个204 No Content的响应。无论是持久连接还是断开再连接都会有这个奇怪的响应跟在后头。

后来发现,原因是这样的:

cowboy用http_req代表了一次用户请求的数据记录(似乎叫session比较好点),该record记录了socket连接信息、用户请求信息(包括请求消息头、消息体等),以及服务器的响应(例如响应状态、响应消息头,响应消息体等),由cowboy_req模块提供了该record相应的操作接口。

比如 cowboy_req:method/得到请求的方法类型
{Method, Req2} = cowboy_req:method(Req)

cowboy_req:path_info/1得到请求url的路径信息
{PathInfo, Req1} = cowboy_req:path_info(Req)

注意以上方法都会返回一个新的Req1结构数据,虽然许多情况下Req和Req1其实是同一个请求记录。但是某些方法是会修改Req的,例如修改响应的头信息时,因此Req /= Req1,这时再使用Req就有麻烦了。

我是在使用reply方法的时候遇到这个问题,而cowboy_req:rely/2,3,4方法也是会修改Req记录的,如果直接处理
cowboy_req:reply(200, Headers, BodyData, Req)
最后用老的Req作为handle方法的返回,cowboy会无以为没有进行响应,所以它会自己加一个 204 No Content的响应。
错误用法:
handle(Req, State) ->
  cowboy_req:method(Req),
  ...
  cowboy_req:reply(200, Headers, BodyData, Req),
  ...
  {ok, Req}.


正确用法:
handle(Req, State) ->
  cowboy_req:method(Req),
  ....
  {ok, Req1} = cowboy_req:reply(200, Headers, BodyData, Req),
   .....
  {ok, Req1}.

也许对所有的cowboy_req模块方法,都应该同一采用这种方式使用比较好。时刻记住Req记录在cowboy_req可能发生变化。


因为所有的响应都是带Content-Length的,所以客户端可以正确的解析出连接中的第一个响应。但是在持久连接(persistent connection),即keep alive,的情况下,第二个响应就变成了204 No Content消息,就会出现上述问题。

你可能感兴趣的:(erlang,cowboy)