% vim: ts=4 sw=4 et % Simple Bridge % Copyright (c) 2008-2010 Rusty Klophaus % See MIT-LICENSE for licensing information. -module (inets_simple_bridge). -behaviour (simple_bridge). -include("simple_bridge.hrl"). -include_lib("inets/include/httpd.hrl"). -export ([ init/1, protocol/1, host/1, request_method/1, path/1, uri/1, peer_ip/1, peer_port/1, headers/1, cookies/1, query_params/1, post_params/1, request_body/1, socket/1, recv_from_socket/3, protocol_version/1, native_header_type/0 ]). -export([ build_response/2 ]). init(Req) -> Req. protocol(Req) -> case Req#mod.socket of S when is_tuple(S), element(1, S) =:= sslsocket -> https; _ -> http end. host(Req) -> Headers = headers(Req), Host = proplists:get_value("host", Headers), XForwardedFor = proplists:get_value("x-forwarded-for", Headers), error_logger:info_msg("~p, ~p, ~p~n",[Req#mod.absolute_uri, Host, XForwardedFor]), simple_bridge_util:infer_host(Req#mod.absolute_uri, Host, XForwardedFor). request_method(Req) -> list_to_atom(Req#mod.method). path(Req) -> {Path, _QueryString} = split_request_uri(Req#mod.request_uri, []), Path. uri(Req) -> Req#mod.request_uri. peer_ip(Req) -> Socket = Req#mod.socket, {ok, {IP, _Port}} = case Socket of S when is_tuple(S), element(1, S) =:= sslsocket -> ssl:peername(Socket); _ -> inet:peername(Socket) end, IP. peer_port(Req) -> Socket = Req#mod.socket, {ok, {_IP, Port}} = case Socket of S when is_tuple(S), element(1, S) =:= sslsocket -> ssl:peername(Socket); _ -> inet:peername(Socket) end, Port. native_header_type() -> list. headers(Req) -> Req#mod.parsed_header. cookies(Req) -> Headers = Req#mod.parsed_header, CookieData = proplists:get_value("cookie", Headers, ""), simple_bridge_util:parse_cookie_header(CookieData). query_params(Req) -> {_Path, QueryString} = split_request_uri(Req#mod.request_uri, []), Query = handle_parse_qs(QueryString, ?PARSE_QS(QueryString)), [{Key, Value} || {Key, Value} <- Query, Key /= []]. post_params(Req) -> Body = request_body(Req), Query = handle_parse_qs(Body, ?PARSE_QS(Body)), [{Key, Value} || {Key, Value} <- Query, Key /= []]. handle_parse_qs(_QS, Err={error, _, _}) -> error_logger:warning_msg("Unable to parse QueryString for Inets. ~nReason: ~p~n",[Err]), []; handle_parse_qs(_QS, Res) -> Res. request_body(Req) -> Req#mod.entity_body. socket(Req) -> Req#mod.socket. recv_from_socket(Length, Timeout, Req) -> Socket = socket(Req), case gen_tcp:recv(Socket, Length, Timeout) of {ok, Data} -> Data; _ -> exit(normal) end. protocol_version(Req) -> case Req#mod.http_version of "HTTP/0.9" -> {0, 9}; "HTTP/1.0" -> {1, 0}; "HTTP/1.1" -> {1, 1} end. %%% PRIVATE FUNCTIONS %%% split_request_uri([], Path) -> {lists:reverse(Path), ""}; split_request_uri([$?|QueryString], Path) -> {lists:reverse(Path), QueryString}; split_request_uri([H|T], Path) -> split_request_uri(T,[H|Path]). build_response(Req, Res) -> ResponseCode = Res#response.status_code, case Res#response.data of {data, Data} -> Size = integer_to_list(erlang:iolist_size(Data)), % Assemble headers... Headers = lists:flatten([ {code, ResponseCode}, {content_length, Size}, [{massage(X#header.name), X#header.value} || X <- Res#response.headers], [create_cookie_header(X) || X <- Res#response.cookies] ]), % Send the inets response... {break,[ {response, {response, Headers, Data}} ]}; {file, _Path} -> mod_get:do(Req) end. create_cookie_header(Cookie = #cookie{}) -> {K, V} = simple_bridge_util:create_cookie_header(Cookie), {binary_to_list(K), V}. % Inets wants some headers as lowercase atoms, and the rest as lists. So let's fix these up. massage(Header) when is_binary(Header) -> massage(binary_to_list(Header)); massage(Header) -> X = simple_bridge_util:atomize_header(Header), case lists:member(X, special_headers()) of true -> X; false -> Header end. special_headers() -> [accept_ranges , allow , cache_control , content_MD5 , content_encoding , content_language , content_length , content_location , content_range , content_type , date , etag , expires , last_modified , location , pragma , retry_after , server , trailer , transfer_encoding].