mochiweb 源码阅读(十七)
2012-08-11 02:37 rhinovirus 阅读(2040) 评论(2) 编辑 收藏 举报大家好,今天周五,继续来和大家一起分享mochiweb源码。最近我也是在看百度连城翻译的《Erlang/OTP并发编程实战》,其中第11章:为缓存添加HTTP接口,有这本书的朋友可以翻看下,这里介绍的内容,有助于大家理解mochiweb源码。
上一篇,我们讲到了mochiweb_http:headers/5函数,当所有协议头解析完毕后会有一个空行,它标志着报文头部的结束,剩下的便是消息正文。
{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);
代码中第一个分支就是当读取到空行时,继续处理消息正文,第二个分支则是解析每一个协议头。
接下来我们首先看下:mochiweb_http:new_request/3函数:
new_request(Socket, Request, RevHeaders) -> ok = mochiweb_socket:setopts(Socket, [{packet, raw}]), mochiweb:new_request({Socket, Request, lists:reverse(RevHeaders)}).
这个函数首先修改socket选项{packet, raw},不设置消息打包规则,如果{packet, 1|2|4},则表示每一个包都会带上一个N(1,2或4)字节长的长度计数。
该函数接着调用mochiweb:new_request/1函数:
%% @spec new_request({Socket, Request, Headers}) -> MochiWebRequest %% @doc Return a mochiweb_request data structure. new_request({Socket, {Method, {abs_path, Uri}, Version}, Headers}) -> mochiweb_request:new(Socket, Method, Uri, Version, mochiweb_headers:make(Headers)); % this case probably doesn't "exist". new_request({Socket, {Method, {absoluteURI, _Protocol, _Host, _Port, Uri}, Version}, Headers}) -> mochiweb_request:new(Socket, Method, Uri, Version, mochiweb_headers:make(Headers)); %% Request-URI is "*" %% From http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2 new_request({Socket, {Method, '*'=Uri, Version}, Headers}) -> mochiweb_request:new(Socket, Method, Uri, Version, mochiweb_headers:make(Headers)).
这个函数一共有三个分支,但是每一个都是匹配对应的参数,紧接着调用mochiweb_request:new/5函数,但是这里注意,我在mochiweb_request模块导出的函数列表中,并没有发现对应的函数,那么这究竟是怎么回事呢?
这种用法称之为:参数化的Module(Parameterized Module),下面是关于这个用法的相关文章:
http://mryufeng.iteye.com/blog/477376
http://erlangdisplay.iteye.com/blog/315401
http://www.erlangsir.com/2011/04/01/%E5%8F%82%E6%95%B0%E5%8C%96%E7%9A%84module/
http://www.cnblogs.com/me-sa/archive/2012/02/16/Erlang0037.html
这里注意mochiweb_request模块定义:
-module(mochiweb_request, [Socket, Method, RawPath, Version, Headers]).
关于这个用法,希望大家认真看下上面几篇文章,就能够明白了。
这里我们还需要看下:mochiweb_headers:make/1函数:
%% @spec make(headers() | [{key(), value()}]) -> headers() %% @doc Construct a headers() from the given list. make(L) when is_list(L) -> from_list(L); %% assume a non-list is already mochiweb_headers. make(T) -> T.
如果L为列表,则调用函数mochiweb_headers:from_list/1函数:
%% @spec from_list([{key(), value()}]) -> headers() %% @doc Construct a headers() from the given list. from_list(List) -> lists:foldl(fun ({K, V}, T) -> insert(K, V, T) end, empty(), List).
mochiweb_headers:empty/0函数:
%% @type headers(). %% @type key() = atom() | binary() | string(). %% @type value() = atom() | binary() | string() | integer(). %% @spec empty() -> headers() %% @doc Create an empty headers structure. empty() -> gb_trees:empty().
mochiweb_headers:insert/3函数:
%% @spec insert(key(), value(), headers()) -> headers() %% @doc Insert the pair into the headers, merging with any pre-existing key. %% A merge is done with Value = V0 ++ ", " ++ V1. insert(K, V, T) -> K1 = normalize(K), V1 = any_to_list(V), try gb_trees:insert(K1, {K, V1}, T) catch error:{key_exists, _} -> {K0, V0} = gb_trees:get(K1, T), V2 = merge(K1, V1, V0), gb_trees:update(K1, {K0, V2}, T) end.
mochiweb_headers:normalize/1函数:
normalize(K) when is_list(K) -> string:to_lower(K); normalize(K) when is_atom(K) -> normalize(atom_to_list(K)); normalize(K) when is_binary(K) -> normalize(binary_to_list(K)).
mochiweb_headers:any_to_list/1函数:
any_to_list(V) when is_list(V) -> V; any_to_list(V) when is_atom(V) -> atom_to_list(V); any_to_list(V) when is_binary(V) -> binary_to_list(V); any_to_list(V) when is_integer(V) -> integer_to_list(V).
这几个函数都比较简单,我们重点看下:gb_trees这个模块的作用,erlang doc地址:http://www.erlang.org/doc/man/gb_trees.html,也可以参考坚强2002同学的这篇文章:http://www.cnblogs.com/me-sa/archive/2012/06/23/erlang-gb_trees.html
这里注意:
delete/2与delete_any/2函数的区别是,如果key不在树中,调用delete/2会导致崩溃,而delete_any/2则什么也不做,返回新树;
enter/3与insert/3函数的区别是,如果key在树中存在,调用insert/3会导致崩溃,则enter/3则跟新树中的值。
get/2调用该函数根据Key检索树中存储的值。假设,关键是目前在树中,否则崩溃。
最后得到的数据如下:
< Headers = [{'Host',"127.0.0.1:8080"}, {'User-Agent',"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1"}, {'Accept',"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"}, {'Accept-Language',"zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3"}, {'Accept-Encoding',"gzip, deflate"}, {'Connection',"keep-alive"}, {'If-Modified-Since',"Mon, 09 Jul 2012 15:17:42 GMT"}, {'Cache-Control',"max-age=0"}] < H = {8, {"host", {'Host',"127.0.0.1:8080"}, {"accept", {'Accept',"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"}, nil, {"accept-language", {'Accept-Language',"zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3"}, {"accept-encoding",{'Accept-Encoding',"gzip, deflate"},nil,nil}, {"connection", {'Connection',"keep-alive"}, {"cache-control",{'Cache-Control',"max-age=0"},nil,nil}, nil}}}, {"user-agent", {'User-Agent',"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1"}, {"if-modified-since", {'If-Modified-Since',"Mon, 09 Jul 2012 15:17:42 GMT"}, nil,nil}, nil}}}
好了,这一篇就到这里,晚安。
本文基于署名-非商业性使用 3.0许可协议发布,欢迎转载,演绎,但是必须保留本文的署名rhinovirus(包含链接http://www.cnblogs.com/rhinovirus/),且不得用于商业目的。如您有任何疑问或者授权方面的协商,请与我联系。