代码改变世界

Cowboy 源码分析(十四)

2012-06-05 00:58  rhinovirus  阅读(1488)  评论(0编辑  收藏  举报

  大家好,今天接着做几个测试,把上一篇理解并不透彻的几个方法再加强下:

  第一个,测试 cowboy_http:whitespace/2 方法的作用,测试代码以及结果如下,左边是测试代码,右边是测试结果:

  

  这样就能很明白的看出这个这个函数的作用了,注意:测试结果第二行,\s 变成空格了。

  第二个,测试 cowboy_http:list/3 以及 cowboy_http:token_ci/2 方法,增加打印log,测试代码如下:

-module(main).
-export([start/0, stop/0]).

start() ->
    io:format("test 1 ~n"),
    list(<<>>, fun token_ci/2, []),
    
    io:format("test 2 ~n"),
    list(<<",abc">>, fun token_ci/2, []),
    ok.

%% @doc Skip whitespace.
-spec whitespace(binary(), fun()) -> any().
whitespace(<< C, Rest/binary >>, Fun)
        when C =:= $\s; C =:= $\t ->
    ip:format("call whitespace 1~n"),
    whitespace(Rest, Fun);
whitespace(Data, Fun) ->
    io:format("call whitespace 2~n"),
    Fun(Data).

-spec list(binary(), fun(), [binary()]) -> [any()] | {error, badarg}.
%% From the RFC:
%% <blockquote>Wherever this construct is used, null elements are allowed,
%% but do not contribute to the count of elements present.
%% That is, "(element), , (element) " is permitted, but counts
%% as only two elements. Therefore, where at least one element is required,
%% at least one non-null element MUST be present.</blockquote>
list(Data, Fun, Acc) ->
    whitespace(Data,
           fun (<<>>) -> 
               io:format("1~n"),
               Acc;
           (<< $,, Rest/binary >>) -> 
               io:format("2~n"),
               list(Rest, Fun, Acc);
           (Rest) -> 
               io:format("3~n"),
               Fun(Rest,
               fun (D, I) ->
                   io:format("4~n"),
                   whitespace(D,
                          fun (<<>>) -> 
                              io:format("5~n"),
                              [I|Acc];
                          (<< $,, R/binary >>) ->
                              io:format("6~n"),
                              list(R, Fun, [I|Acc]);
                          (_Any) -> 
                              io:format("7~n"),
                              {error, badarg}
                        end)
                end)
        end).

%% @doc Parse a case-insensitive token.
%%
%% Changes all characters to lowercase.
-spec token_ci(binary(), fun()) -> any().
token_ci(Data, Fun) ->
    io:format("call token_ci. ~n"),
    token(Data, Fun, ci, <<>>).

-spec token(binary(), fun(), ci | cs, binary()) -> any().
token(<<>>, Fun, _Case, Acc) ->
    io:format("call token 1. ~n"),
    Fun(<<>>, Acc);
token(Data = << C, _Rest/binary >>, Fun, _Case, Acc)
        when C =:= $(; C =:= $); C =:= $<; C =:= $>; C =:= $@;
             C =:= $,; C =:= $;; C =:= $:; C =:= $\\; C =:= $";
             C =:= $/; C =:= $[; C =:= $]; C =:= $?; C =:= $=;
             C =:= ${; C =:= $}; C =:= $\s; C =:= $\t;
             C < 32; C =:= 127 ->
    io:format("call token 2. ~n"),
    Fun(Data, Acc);
token(<< C, Rest/binary >>, Fun, Case = ci, Acc) ->
    io:format("call token 3. ~n"),
    %%C2 = cowboy_bstr:char_to_lower(C),
    C2 = char_to_lower(C),
    token(Rest, Fun, Case, << Acc/binary, C2 >>);
token(<< C, Rest/binary >>, Fun, Case, Acc) ->
    io:format("call token 4. ~n"),
    token(Rest, Fun, Case, << Acc/binary, C >>).

%% @doc Convert [A-Z] characters to lowercase.
%% @end
%% We gain noticeable speed by matching each value directly.
-spec char_to_lower(char()) -> char().
char_to_lower($A) -> $a;
char_to_lower($B) -> $b;
char_to_lower($C) -> $c;
char_to_lower($D) -> $d;
char_to_lower($E) -> $e;
char_to_lower($F) -> $f;
char_to_lower($G) -> $g;
char_to_lower($H) -> $h;
char_to_lower($I) -> $i;
char_to_lower($J) -> $j;
char_to_lower($K) -> $k;
char_to_lower($L) -> $l;
char_to_lower($M) -> $m;
char_to_lower($N) -> $n;
char_to_lower($O) -> $o;
char_to_lower($P) -> $p;
char_to_lower($Q) -> $q;
char_to_lower($R) -> $r;
char_to_lower($S) -> $s;
char_to_lower($T) -> $t;
char_to_lower($U) -> $u;
char_to_lower($V) -> $v;
char_to_lower($W) -> $w;
char_to_lower($X) -> $x;
char_to_lower($Y) -> $y;
char_to_lower($Z) -> $z;
char_to_lower(Ch) -> Ch.

stop() ->
    ok.

  测试结果如下:

  

  现在大家对于上一篇,提到的 cowboy_http:nonempty_list(Value, fun cowboy_http:token_ci/2) 是不是有进一步的理解了,这里之所以会比较复杂,其实主要是作者把匿名函数当作参数传递,怎么说呢,使用这种方式,有它的灵活性在,但是其实这降低了代码的可读性,所以我个人推荐大家不建议用这种复杂的嵌套多层的匿名函数。

  还有个知识点差点忘说了,erlang对于二进制数据的处理还是比较强大了,而新手(包括我)理解二进制解析的时候,会吃力一些,这时候大家动手,写几个测试函数,能够加深理解。

  cowboy_http:token/4 方法有用到一个方法,大家在上面我贴出来的代码中,也能看到,这个方法很简单,字符的大写转换成小写。

  我们理解完上面这些代码的时候,就可以继续往下看了。还记得上一篇提到的cowboy_http_req:parse_header/3 这个函数吗:

parse_header(Name, Req, Default) when Name =:= 'Connection' ->
    parse_header(Name, Req, Default,
        fun (Value) ->
            cowboy_http:nonempty_list(Value, fun cowboy_http:token_ci/2)
        end);

  这里定义了一个匿名函数,作为第四个参数传递到 cowboy_http_req:parse_header/4 这个函数,我们将在下一篇中,继续看下这个函数。

  今天就到这里,洗澡睡觉了,谢谢大家支持。