大家好,几天没跟新这个系列了,忙着考驾照,今天继续来跟大家分享mochiweb源码,上一篇我们讲到了mochiweb_http:handle_invalid_request/1函数,这一篇,我们来看下,正确的逻辑处理,再讲这之前,还有个地方跟大家提下,就是如果mochiweb_http:request/2函数,在使用receive读取消息时,如果发生超时,则是简单的关闭套接字以及退出进程,代码如下:
after ?REQUEST_RECV_TIMEOUT -> mochiweb_socket:close(Socket), exit(normal) end.
宏定义如下:
-define(REQUEST_RECV_TIMEOUT, 300000). %% timeout waiting for request line
好了,现在我们可以回到正确逻辑了:
{Protocol, _, {http_request, Method, Path, Version}} when Protocol == http orelse Protocol == ssl -> ok = mochiweb_socket:setopts(Socket, [{packet, httph}]), headers(Socket, {Method, Path, Version}, [], Body, 0);
这个分支也只有2行代码,很简单,首先调用mochiweb_socket:setopts/2设置Socket选项,这里是:{packet, httph},当设置这个选项时,返回的数据包会是什么样的呢?通过查阅erlang doc,地址:,如下图:
大致翻译如下:
这两类往往是没有必要的,socket内部的第一行被读取后,将自动从http/http_bin切换到httph/httph_bin。无论如何,有些场合可能他们是有用的,例如从区块解析数据报。
关于这个选项,还有如下一段描述:
大致翻译如下:
超文本传输协议。数据包返回的格式根据上述HttpPacket。一个包可以是一个请求,响应,一个头或端头标记。无效行返回HttpError。
确认请求的方法和报头域返回原子。其他返回作为字符串。
当预计一个HttpRequest或者一个HttpResponse,http协议类型应该只用于第一行。
下面的调用应该使用httph得到HttpHeaderhttp_eoh返回结束的标志头并开始任何以下消息体。
该变种http_bin和httph_bin将返回字符串(httpstring)为二进制文件代替列表。
好了,现在我们已经大概知道了http | httph | http_bin | httph_bin这四个选项在什么情况下使用,回到mochiweb_http:request/2函数,设置完{packet, httph}后,紧接着调用mochiweb_http:headers/5函数:
headers(Socket, Request, Headers, _Body, ?MAX_HEADERS) -> %% Too many headers sent, bad request. ok = mochiweb_socket:setopts(Socket, [{packet, raw}]), handle_invalid_request(Socket, Request, Headers);headers(Socket, Request, Headers, Body, HeaderCount) -> ok = mochiweb_socket:setopts(Socket, [{active, once}]), receive {Protocol, _, http_eoh} when Protocol == http orelse Protocol == ssl -> Req = new_request(Socket, Request, Headers), call_body(Body, Req), ?MODULE:after_response(Body, Req); {Protocol, _, {http_header, _, Name, _, Value}} when Protocol == http orelse Protocol == ssl -> headers(Socket, Request, [{Name, Value} | Headers], Body, 1 + HeaderCount); {tcp_closed, _} -> mochiweb_socket:close(Socket), exit(normal); _Other -> handle_invalid_request(Socket, Request, Headers) after ?HEADERS_RECV_TIMEOUT -> mochiweb_socket:close(Socket), exit(normal) end.
这个函数有两个分支,我们先看第一个,当HeaderCount为?MAX_HEADERS时,调用第一个分支,注释:Too many headers sent, bad request,发送太多的头部,坏的请求。
宏定义如下:
-define(MAX_HEADERS, 1000).
其实这也很好理解,递归调用mochiweb_http:headers/5函数来解析http_header,直到解析到http_eoh为止,如果HeaderCount等于?MAX_HEADERS,则会走第一个分支,从这一行我们知道这个参数会在整个调用过程递增:
{Protocol, _, {http_header, _, Name, _, Value}} when Protocol == http orelse Protocol == ssl -> headers(Socket, Request, [{Name, Value} | Headers], Body, 1 + HeaderCount);
第一个分支的逻辑,修改Socket选项{packet, raw},然后调用函数:mochiweb_http:handle_invalid_request/3处理为无效请求。
第二个分支和前一篇介绍的mochiweb_http:request/2函数,有很多相同的地方,依然是修改Socket选项为{active, once},接着使用receive读取一条消息:
消息格式说明:
{http, Socket, HttpPacket}
HttpPacket = HttpHeader | http_eoh
HttpHeader = {http_header, integer(), HttpField, Reserved=term(), Value=HttpString}
只要不是返回{http, Socket, http_eoh}就递归调用mochiweb_http:headers/5函数继续解析头部,并且保存{Name, Value}到Headers列表头部:
headers(Socket, Request, [{Name, Value} | Headers], Body, 1 + HeaderCount);
而当解析得到{http, Socket, http_eoh}时,处理如下:
{Protocol, _, http_eoh} when Protocol == http orelse Protocol == ssl -> Req = new_request(Socket, Request, Headers), call_body(Body, Req), ?MODULE:after_response(Body, Req);
这里就三行代码,首先调用mochiweb_http:new_request/3获取Req对象,接着调用mochiweb_http:call_body/2函数,最后调用该模块下的?MODULE:after_response/2函数。
好了,这一篇就到这里,下一篇我们将继续从这三个函数来和大家分享。
最后,谢谢大家的支持,晚安。