zoukankan      html  css  js  c++  java
  • cowboy源码分析(二)

    接 cowboy源码分析(一)

    下面我们重点看看cowboy_protocol.erl代码

    -module(cowboy_protocol).
    
    %% API.
    -export([start_link/4]).
    
    %% Internal.
    -export([init/4]).
    -export([parse_request/3]).
    -export([resume/6]).
    
    -type opts() :: [{compress, boolean()}
        | {env, cowboy_middleware:env()}
        | {max_empty_lines, non_neg_integer()}
        | {max_header_name_length, non_neg_integer()}
        | {max_header_value_length, non_neg_integer()}
        | {max_headers, non_neg_integer()}
        | {max_keepalive, non_neg_integer()}
        | {max_request_line_length, non_neg_integer()}
        | {middlewares, [module()]}
        | {onrequest, cowboy:onrequest_fun()}
        | {onresponse, cowboy:onresponse_fun()}
        | {timeout, timeout()}].
    -export_type([opts/0]).
    
    -record(state, {
        socket :: inet:socket(),
        transport :: module(),
        middlewares :: [module()],
        compress :: boolean(),
        env :: cowboy_middleware:env(),
        onrequest :: undefined | cowboy:onrequest_fun(),
        onresponse = undefined :: undefined | cowboy:onresponse_fun(),
        max_empty_lines :: non_neg_integer(),
        req_keepalive = 1 :: non_neg_integer(),
        max_keepalive :: non_neg_integer(),
        max_request_line_length :: non_neg_integer(),
        max_header_name_length :: non_neg_integer(),
        max_header_value_length :: non_neg_integer(),
        max_headers :: non_neg_integer(),
        timeout :: timeout(),
        until :: non_neg_integer() | infinity
    }).
    
    -include_lib("cowlib/include/cow_inline.hrl").
    
    %% API.
    
    -spec start_link(ranch:ref(), inet:socket(), module(), opts()) -> {ok, pid()}.
    start_link(Ref, Socket, Transport, Opts) ->
        Pid = spawn_link(?MODULE, init, [Ref, Socket, Transport, Opts]),
        {ok, Pid}.
    
    %% Internal.
    
    %% Faster alternative to proplists:get_value/3.
    get_value(Key, Opts, Default) ->
        case lists:keyfind(Key, 1, Opts) of
            {_, Value} -> Value;
            _ -> Default
        end.
    
    -spec init(ranch:ref(), inet:socket(), module(), opts()) -> ok.
    init(Ref, Socket, Transport, Opts) ->
        Compress = get_value(compress, Opts, false),
        MaxEmptyLines = get_value(max_empty_lines, Opts, 5),
        MaxHeaderNameLength = get_value(max_header_name_length, Opts, 64),
        MaxHeaderValueLength = get_value(max_header_value_length, Opts, 4096),
        MaxHeaders = get_value(max_headers, Opts, 100),
        MaxKeepalive = get_value(max_keepalive, Opts, 100),
        MaxRequestLineLength = get_value(max_request_line_length, Opts, 4096),
        Middlewares = get_value(middlewares, Opts, [cowboy_router, cowboy_handler]),
        Env = [{listener, Ref}|get_value(env, Opts, [])],
        OnRequest = get_value(onrequest, Opts, undefined),
        OnResponse = get_value(onresponse, Opts, undefined),
        Timeout = get_value(timeout, Opts, 5000),
        ok = ranch:accept_ack(Ref),
        wait_request(<<>>, #state{socket=Socket, transport=Transport,
            middlewares=Middlewares, compress=Compress, env=Env,
            max_empty_lines=MaxEmptyLines, max_keepalive=MaxKeepalive,
            max_request_line_length=MaxRequestLineLength,
            max_header_name_length=MaxHeaderNameLength,
            max_header_value_length=MaxHeaderValueLength, max_headers=MaxHeaders,
            onrequest=OnRequest, onresponse=OnResponse,
            timeout=Timeout, until=until(Timeout)}, 0).
    
    -spec until(timeout()) -> non_neg_integer() | infinity.
    until(infinity) ->
        infinity;
    until(Timeout) ->
        {Me, S, Mi} = os:timestamp(),
        Me * 1000000000 + S * 1000 + Mi div 1000 + Timeout.
    
    %% Request parsing.
    %%
    %% The next set of functions is the request parsing code. All of it
    %% runs using a single binary match context. This optimization ends
    %% right after the header parsing is finished and the code becomes
    %% more interesting past that point.
    
    -spec recv(inet:socket(), module(), non_neg_integer() | infinity)
        -> {ok, binary()} | {error, closed | timeout | atom()}.
    recv(Socket, Transport, infinity) ->
        Transport:recv(Socket, 0, infinity);
    recv(Socket, Transport, Until) ->
        {Me, S, Mi} = os:timestamp(),
        Now = Me * 1000000000 + S * 1000 + Mi div 1000,
        Timeout = Until - Now,
        if    Timeout < 0 ->
                {error, timeout};
            true ->
                Transport:recv(Socket, 0, Timeout)
        end.
    
    -spec wait_request(binary(), #state{}, non_neg_integer()) -> ok.
    wait_request(Buffer, State=#state{socket=Socket, transport=Transport,
            until=Until}, ReqEmpty) ->
        case recv(Socket, Transport, Until) of
            {ok, Data} ->
                parse_request(<< Buffer/binary, Data/binary >>, State, ReqEmpty);
            {error, _} ->
                terminate(State)
        end.
    
    -spec parse_request(binary(), #state{}, non_neg_integer()) -> ok.
    %% Empty lines must be using 
    .
    parse_request(<< $
    , _/binary >>, State, _) ->
        error_terminate(400, State);
    %% We limit the length of the Request-line to MaxLength to avoid endlessly
    %% reading from the socket and eventually crashing.
    parse_request(Buffer, State=#state{max_request_line_length=MaxLength,
            max_empty_lines=MaxEmpty}, ReqEmpty) ->
        case match_eol(Buffer, 0) of
            nomatch when byte_size(Buffer) > MaxLength ->
                error_terminate(414, State);
            nomatch ->
                wait_request(Buffer, State, ReqEmpty);
            1 when ReqEmpty =:= MaxEmpty ->
                error_terminate(400, State);
            1 ->
                << _:16, Rest/binary >> = Buffer,
                parse_request(Rest, State, ReqEmpty + 1);
            _ ->
                parse_method(Buffer, State, <<>>)
        end.
    
    match_eol(<< $
    , _/bits >>, N) ->
        N;
    match_eol(<< _, Rest/bits >>, N) ->
        match_eol(Rest, N + 1);
    match_eol(_, _) ->
        nomatch.
    
    parse_method(<< C, Rest/bits >>, State, SoFar) ->
        case C of
            $
     -> error_terminate(400, State);
            $s -> parse_uri(Rest, State, SoFar);
            _ -> parse_method(Rest, State, << SoFar/binary, C >>)
        end.
    
    parse_uri(<< $
    , _/bits >>, State, _) ->
        error_terminate(400, State);
    parse_uri(<< "* ", Rest/bits >>, State, Method) ->
        parse_version(Rest, State, Method, <<"*">>, <<>>);
    parse_uri(<< "http://", Rest/bits >>, State, Method) ->
        parse_uri_skip_host(Rest, State, Method);
    parse_uri(<< "https://", Rest/bits >>, State, Method) ->
        parse_uri_skip_host(Rest, State, Method);
    parse_uri(<< "HTTP://", Rest/bits >>, State, Method) ->
        parse_uri_skip_host(Rest, State, Method);
    parse_uri(<< "HTTPS://", Rest/bits >>, State, Method) ->
        parse_uri_skip_host(Rest, State, Method);
    parse_uri(Buffer, State, Method) ->
        parse_uri_path(Buffer, State, Method, <<>>).
    
    parse_uri_skip_host(<< C, Rest/bits >>, State, Method) ->
        case C of
            $
     -> error_terminate(400, State);
            $/ -> parse_uri_path(Rest, State, Method, <<"/">>);
            $s -> parse_version(Rest, State, Method, <<"/">>, <<>>);
            $? -> parse_uri_query(Rest, State, Method, <<"/">>, <<>>);
            $# -> skip_uri_fragment(Rest, State, Method, <<"/">>, <<>>);
            _ -> parse_uri_skip_host(Rest, State, Method)
        end.
    
    parse_uri_path(<< C, Rest/bits >>, State, Method, SoFar) ->
        case C of
            $
     -> error_terminate(400, State);
            $s -> parse_version(Rest, State, Method, SoFar, <<>>);
            $? -> parse_uri_query(Rest, State, Method, SoFar, <<>>);
            $# -> skip_uri_fragment(Rest, State, Method, SoFar, <<>>);
            _ -> parse_uri_path(Rest, State, Method, << SoFar/binary, C >>)
        end.
    
    parse_uri_query(<< C, Rest/bits >>, S, M, P, SoFar) ->
        case C of
            $
     -> error_terminate(400, S);
            $s -> parse_version(Rest, S, M, P, SoFar);
            $# -> skip_uri_fragment(Rest, S, M, P, SoFar);
            _ -> parse_uri_query(Rest, S, M, P, << SoFar/binary, C >>)
        end.
    
    skip_uri_fragment(<< C, Rest/bits >>, S, M, P, Q) ->
        case C of
            $
     -> error_terminate(400, S);
            $s -> parse_version(Rest, S, M, P, Q);
            _ -> skip_uri_fragment(Rest, S, M, P, Q)
        end.
    
    parse_version(<< "HTTP/1.1
    ", Rest/bits >>, S, M, P, Q) ->
        parse_header(Rest, S, M, P, Q, 'HTTP/1.1', []);
    parse_version(<< "HTTP/1.0
    ", Rest/bits >>, S, M, P, Q) ->
        parse_header(Rest, S, M, P, Q, 'HTTP/1.0', []);
    parse_version(_, State, _, _, _) ->
        error_terminate(505, State).
    
    %% Stop receiving data if we have more than allowed number of headers.
    wait_header(_, State=#state{max_headers=MaxHeaders}, _, _, _, _, Headers)
            when length(Headers) >= MaxHeaders ->
        error_terminate(400, State);
    wait_header(Buffer, State=#state{socket=Socket, transport=Transport,
            until=Until}, M, P, Q, V, H) ->
        case recv(Socket, Transport, Until) of
            {ok, Data} ->
                parse_header(<< Buffer/binary, Data/binary >>,
                    State, M, P, Q, V, H);
            {error, timeout} ->
                error_terminate(408, State);
            {error, _} ->
                terminate(State)
        end.
    
    parse_header(<< $
    , $
    , Rest/bits >>, S, M, P, Q, V, Headers) ->
        request(Rest, S, M, P, Q, V, lists:reverse(Headers));         %这里就是url处理完后的入口
    parse_header(Buffer, State=#state{max_header_name_length=MaxLength},
            M, P, Q, V, H) ->
        case match_colon(Buffer, 0) of
            nomatch when byte_size(Buffer) > MaxLength ->
                error_terminate(400, State);
            nomatch ->
                wait_header(Buffer, State, M, P, Q, V, H);
            _ ->
                parse_hd_name(Buffer, State, M, P, Q, V, H, <<>>)
        end.

    %%省略若干行

    在这里我们省略的一些代码,看看cowboy_protocol的主要流程

    注意标红色的部分

    首先是start_link/4启动init -》

    -》init 初始化参数,找不到的设置为默认 -》

    -》设置超时时间,进入cowboy_protocol:wait_request -》

    -》接收数据,recv -》

    -》解析接收数据,parse_method 首先是http的方法(get/put等) -》

    -》 parse_uri 解析请求的url (/或者/test.html),其中parse_uri_query 解析请求url的?后面的参数 -》

    -》 parse_version 解析http的版本(http 1.1/1.0等) -》

    -》 parse_header 解析剩下的头部

    解析完成的头部信息如下(例子)

      M:<<"GET">>,
      P:<<"/">>,
      Q:<<>>,
      V:'HTTP/1.1',
      Headers:[{<<"if-modified-since">>,
      <<"Tue, 22 Mar 2016 09:51:57 GMT">>},
      {<<"if-none-match">>,
      <<""3936977058"">>},
      {<<"accept-language">>,
      <<"zh-CN,zh;q=0.8">>},
      {<<"accept-encoding">>,
      <<"gzip, deflate, sdch">>},
      {<<"user-agent">>,
      <<"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36">>},
      {<<"upgrade-insecure-requests">>,
      <<"1">>},
      {<<"accept">>,
      <<"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8">>},
      {<<"cache-control">>,
      <<"max-age=0">>},
      {<<"connection">>,
      <<"keep-alive">>},
      {<<"host">>,
      <<"192.168.80.146:8080">>}]

    对照上面的流程就能很清晰了

    头部完成后进入request/7函数。

     现在就先在这,未完待续~~

  • 相关阅读:
    macOS 遇到 svnadmin无法使用的情况
    语音识别进化简史:从造技术到建系统
    你是什么垃圾?人工智能面对干垃圾和湿垃圾“有点蒙”
    垃圾分类的事,让机器人做去吧!
    怎样才能叫一只奶牛自愿挤奶?
    第一次,脑机接口可以实时读取人类语言了
    机器人工作原理的超详细解析,生动、形象!
    1900页数学基础:面向CS的线性代数、拓扑、微积分和最优化
    微软Azure AI负责人:OpenAI只在微软云上训练模型
    Velodyne收购高清地图公司 将研发更安全的ADAS系统
  • 原文地址:https://www.cnblogs.com/tudou008/p/5685282.html
Copyright © 2011-2022 走看看