%%%------------------------------------------------------------------- %%% @author licheng5 %%% @copyright (C) 2020, %%% @doc %%% %%% @end %%% Created : 26. 4月 2020 3:36 下午 %%%------------------------------------------------------------------- -module(http_protocol). -author("licheng5"). %% API -export([init/2]). -spec init(Req :: cowboy_req:req(), Opts :: [module()]) -> {ok, cowboy_req:req(), [module()]}. init(Req0, Opts = [Mod|_]) -> Method = binary_to_list(cowboy_req:method(Req0)), Path = binary_to_list(cowboy_req:path(Req0)), GetParams0 = cowboy_req:parse_qs(Req0), GetParams = maps:from_list(GetParams0), {ok, PostParams, Req1} = parse_body(Req0), Headers = cowboy_req:headers(Req0), logger:debug("[http_protocol] request path: ~p, get_params: ~p, post_params: ~p, headers: ~p", [Path, GetParams, PostParams, Headers]), try Mod:handle_request(Method, Path, GetParams, PostParams) of {ok, StatusCode, Resp} -> AcceptEncoding = cowboy_req:header(<<"accept-encoding">>, Req1, <<>>), Req2 = case iolist_size(Resp) >= 1024 andalso supported_gzip(AcceptEncoding) of true -> Resp1 = zlib:gzip(Resp), cowboy_req:reply(StatusCode, #{ <<"Content-Type">> => <<"application/json;charset=utf-8">>, <<"Content-Encoding">> => <<"gzip">> }, Resp1, Req1); false -> cowboy_req:reply(StatusCode, #{ <<"Content-Type">> => <<"application/json;charset=utf-8">> }, Resp, Req1) end, {ok, Req2, Opts} catch throw:Error -> Req2 = cowboy_req:reply(404, #{ <<"Content-Type">> => <<"application/json;charset=utf-8">> }, Error, Req1), {ok, Req2, Opts}; _:Error:Stack -> logger:warning("[http_handler] get error: ~p, stack: ~p", [Error, Stack]), ErrorBin = sdlan_util:term_to_binary(Error), Req2 = cowboy_req:reply(500, #{ <<"Content-Type">> => <<"text/html;charset=utf-8">> }, <<"Internal Server Error: ", ErrorBin/binary>>, Req1), {ok, Req2, Opts} end. %% 判断是否支持gzip supported_gzip(AcceptEncoding) when is_binary(AcceptEncoding) -> binary:match(AcceptEncoding, <<"gzip">>) =/= nomatch. parse_body(Req0) -> ContentType = cowboy_req:header(<<"content-type">>, Req0), case ContentType of <<"application/json", _/binary>> -> {ok, Body, Req1} = read_body(Req0), case Body /= <<>> of true -> {ok, catch json:decode(Body), Req1}; false -> {ok, #{}, Req1} end; <<"application/x-www-form-urlencoded">> -> {ok, PostParams0, Req1} = cowboy_req:read_urlencoded_body(Req0), PostParams = maps:from_list(PostParams0), {ok, PostParams, Req1}; _ -> {ok, #{}, Req0} end. %% 读取请求体 read_body(Req) -> read_body(Req, <<>>). read_body(Req, AccData) -> case cowboy_req:read_body(Req) of {ok, Data, Req1} -> {ok, <>, Req1}; {more, Data, Req1} -> read_body(Req1, <>) end.