上一篇,我们使用debugger和HttpFox很方便了,查看了方法中的变量,不得不说,debugger 断点调试还是比较好用的。这一篇,我们仍将使用这些工具来帮助我们了解代码,好了,接着上一篇继续来看 cowboy_http_protocol:request/2 方法:
上面两张图,是我们上一篇文章,我们看到的函数,以及变量的值,这一篇,我们继续来看下这个函数:
URLDecode = fun(Bin) -> URLDecFun(Bin, URLDecArg) end,定义了一个匿名函数,Bin是匿名函数定义的变量,我们看下 URLDecFun 这个变量,
urldecode={URLDecFun, URLDecArg}=URLDec
还记得 #state{} 在 cowboy_http_protocol:init/4 方法中,urldecode 的定义吗?我截了个图,方便大家回忆,从下面图,我们可以很清楚看到 urldecode 的值:
那么 urldecode={URLDecFun, URLDecArg}=URLDec 自然就是 urldecode={fun cowboy_http:urldecode/2, crash}=URLDec。
弄明白了这几个参数,我们继续往下看:
{Path, RawPath, Qs} = cowboy_dispatcher:split_path(AbsPath, URLDecode), 看下 cowboy_dispatcher:split_path/2 这个函数:
%% @doc Split a path into a list of path segments.
%%
%% Following RFC2396, this function may return path segments containing any
%% character, including / if, and only if, a / was escaped
%% and part of a path segment.
-spec split_path(binary(), fun((binary()) -> binary())) ->{tokens(), binary(), binary()}.
split_path(Path, URLDec) ->case binary:split(Path, <<"?">>) of[Path] -> {do_split_path(Path, <<"/">>, URLDec), Path, <<>>};[<<>>, Qs] -> {[], <<>>, Qs};[Path2, Qs] -> {do_split_path(Path2, <<"/">>, URLDec), Path2, Qs}end.-spec do_split_path(binary(), <<_:8>>, fun((binary()) -> binary())) -> tokens().
do_split_path(RawPath, Separator, URLDec) ->EncodedPath &#61; case binary:split(RawPath, Separator, [global, trim]) of[<<>>|Path] -> Path;Path -> Pathend,[URLDec(Token) || Token <- EncodedPath].
函数的注释意思为&#xff1a;分割路径中的参数成为一个列表。我们看下 case binary:split(Path, <<"?">>) of 这行&#xff0c;这个函数第一次遇到&#xff0c;同样查下erlang doc&#xff1a;http://www.erlang.org/doc/man/binary.html#split-2 比较简单&#xff0c;就是按照第二个参数&#xff0c;分割第一个参数&#xff0c;返回分割后的列表。在这里 Path &#61; <<"/">> &#xff0c;那么分割后为 [<<"/">>]。
往下看 cowboy_dispatcher:do_split_path/3 这个函数&#xff0c;同样用到了 binary:split/3 函数&#xff0c;只不过这次是三个参数&#xff0c;如果你认真看了上面我给出的 erlang doc 链接&#xff0c;你应该能知道 binary:split/2 其实调用了 binary:split/3&#xff0c;只不过最后一个参数是 []。那么我们看下这次出现的第三个参数是什么意思呢&#xff1f;下面是erlang doc 给出的说明&#xff1a;
- trim
-
Removes trailing empty parts of the result (as does trim in re:split/3)
移除结果中尾部为空的部分。
global -
Repeats the split until the Subject is exhausted. Conceptually the global option makes split work on the positions returned by matches/3, while it normally works on the position returned by match/3.
重复分隔直到不能按Pattern项分割Subject。
好了&#xff0c;弄清楚这2个参数的意思&#xff0c;我们就知道结果&#xff1a;
EncodedPath &#61; case binary:split(RawPath, Separator, [global, trim]) of
EncodedPath &#61; case binary:split(<<"/">>, <<"/">>, [global, trim]) of
EncodedPath &#61; []
接下来看下&#xff1a;[URLDec(Token) || Token <- EncodedPath]. 这里是一个列表解析&#xff0c;URLDec &#61; URLDecode &#61; fun(Bin) -> URLDecFun(Bin, URLDecArg) end.
这里依次对 EncodedPath列表中的元素&#xff0c;依次调用 URLDec(Token)&#xff0c;然后返回每个调用后的结果组成的列表。
这边我把 cowboy_http:urldecode/2 方法贴下下面&#xff0c;我并不打算去解释这个方法&#xff0c;大家详细看下&#xff0c;其实这个方法很简单&#xff0c;就是对 URL 中的参数解码&#xff1a;
%% &#64;doc Decode a URL encoded binary.
%% &#64;equiv urldecode(Bin, crash)
-spec urldecode(binary()) -> binary().
urldecode(Bin) when is_binary(Bin) ->urldecode(Bin, <<>>, crash).%% &#64;doc Decode a URL encoded binary.
%% The second argument specifies how to handle percent characters that are not
%% followed by two valid hex characters. Use &#96;skip&#39; to ignore such errors,
%% if &#96;crash&#39; is used the function will fail with the reason &#96;badarg&#39;.
-spec urldecode(binary(), crash | skip) -> binary().
urldecode(Bin, OnError) when is_binary(Bin) ->urldecode(Bin, <<>>, OnError).-spec urldecode(binary(), binary(), crash | skip) -> binary().
urldecode(<<$%, H, L, Rest/binary>>, Acc, OnError) ->G &#61; unhex(H),M &#61; unhex(L),if G &#61;:&#61; error; M &#61;:&#61; error ->case OnError of skip -> ok; crash -> erlang:error(badarg) end,urldecode(<
urldecode(<<$%, Rest/binary>>, Acc, OnError) ->case OnError of skip -> ok; crash -> erlang:error(badarg) end,urldecode(Rest, <
urldecode(<<$&#43;, Rest/binary>>, Acc, OnError) ->urldecode(Rest, <
urldecode(<
urldecode(<<>>, Acc, _OnError) ->Acc.
又跑了老远&#xff0c;回到 cowboy_dispatcher:split_path/2&#xff0c;最后返回 {[], <<"/">>, <<>>};
接着&#xff0c;我们回到 cowboy_http_protocol:request/2 方法&#xff0c;看下这一行&#xff1a;
{Path, RawPath, Qs} &#61; cowboy_dispatcher:split_path(AbsPath, URLDecode), 可以得到下面几个参数的值为&#xff1a;
Qs &#61; <<>>
Path &#61; []
RawPath &#61; <<"/">>
继续往下&#xff1a;
ConnAtom &#61;
if Keepalive
version_to_connection(Version);
true -> close
end,
下面是 cowboy_http_protocol:version_to_connection/1 函数&#xff1a;
-spec version_to_connection(cowboy_http:version()) -> keepalive | close.
version_to_connection({1, 1}) -> keepalive;
version_to_connection(_Any) -> close.
这段代码主要是判断 同时和服务器保持连接。而我们看下 HTTP 关于Keep-Alive的介绍&#xff1a;使客户端到服务器端的连接持续有效&#xff0c;当出现对服务器的后继请求时&#xff0c;Keep-Alive功能避免了建立或者重新建立连接。如果暂时不理解&#xff0c;没关系&#xff0c;先跳过&#xff0c;以后我还会回过头来整体介绍。
再往下&#xff1a;
parse_header(#http_req{socket&#61;Socket, transport&#61;Transport, connection&#61;ConnAtom, pid&#61;self(), method&#61;Method, version&#61;Version, path&#61;&#39;*&#39;, raw_path&#61; <<"*">>, raw_qs&#61; <<>>, onresponse&#61;OnResponse, urldecode&#61;URLDec}, State);
这行是解析 HTTP 头部&#xff0c;我们将在下一篇继续和大家分享接下来的代码&#xff0c;谢谢大家支持。