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 \r\n. parse_request(<< $\n, _/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(<< $\n, _/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 $\r -> error_terminate(400, State); $\s -> parse_uri(Rest, State, SoFar); _ -> parse_method(Rest, State, << SoFar/binary, C >>) end. parse_uri(<< $\r, _/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 $\r -> 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 $\r -> 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 $\r -> 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 $\r -> 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\r\n", Rest/bits >>, S, M, P, Q) -> parse_header(Rest, S, M, P, Q, 'HTTP/1.1', []); parse_version(<< "HTTP/1.0\r\n", 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(<< $\r, $\n, 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函数。
现在就先在这,未完待续~~