📄 ejabberd_http.erl
字号:
%%%----------------------------------------------------------------------%%% File : ejabberd_http.erl%%% Author : Alexey Shchepin <alexey@sevcom.net>%%% Purpose : %%% Created : 27 Feb 2004 by Alexey Shchepin <alexey@sevcom.net>%%% Id : $Id: ejabberd_http.erl,v 1.16 2004/09/30 21:54:39 aleksey Exp $%%%-----------------------------------------------------------------------module(ejabberd_http).-author('alexey@sevcom.net').-vsn('$Revision: 1.16 $ ').%% External exports-export([start/2, start_link/2, receive_headers/1]).-include("ejabberd.hrl").-include("jlib.hrl").-include("ejabberd_http.hrl").-record(state, {sockmod, socket, request_method, request_version, request_path, request_auth, request_keepalive, request_content_length, request_lang = "en", use_http_poll = false, use_web_admin = false, end_of_request = false, trail = "" }).-define(XHTML_DOCTYPE, "<?xml version='1.0'?>\n" "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" " "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n").-define(HTML_DOCTYPE, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n").start(SockData, Opts) -> supervisor:start_child(ejabberd_http_sup, [SockData, Opts]).start_link({SockMod, Socket}, Opts) -> TLSEnabled = lists:member(tls, Opts), TLSOpts = lists:filter(fun({certfile, _}) -> true; (_) -> false end, Opts), {SockMod1, Socket1} = if TLSEnabled -> inet:setopts(Socket, [{recbuf, 8192}]), {ok, TLSSocket} = tls:tcp_to_tls(Socket, TLSOpts), {tls, TLSSocket}; true -> {SockMod, Socket} end, case SockMod1 of gen_tcp -> inet:setopts(Socket1, [{packet, http}, {recbuf, 8192}]); _ -> ok end, UseHTTPPoll = lists:member(http_poll, Opts), UseWebAdmin = lists:member(web_admin, Opts), ?DEBUG("S: ~p~n", [{UseHTTPPoll, UseWebAdmin}]), ?INFO_MSG("started: ~p", [{SockMod1, Socket1}]), {ok, proc_lib:spawn_link(ejabberd_http, receive_headers, [#state{sockmod = SockMod1, socket = Socket1, use_http_poll = UseHTTPPoll, use_web_admin = UseWebAdmin}])}.send_text(State, Text) -> (State#state.sockmod):send(State#state.socket, Text).receive_headers(State) -> SockMod = State#state.sockmod, Socket = State#state.socket, Data = SockMod:recv(Socket, 0, 300000), case State#state.sockmod of gen_tcp -> NewState = process_header(State, Data), case NewState#state.end_of_request of true -> ok; _ -> receive_headers(NewState) end; _ -> case Data of {ok, Binary} -> {Request, Trail} = parse_request( State, State#state.trail ++ binary_to_list(Binary)), State1 = State#state{trail = Trail}, NewState = lists:foldl( fun(D, S) -> case S#state.end_of_request of true -> S; _ -> process_header(S, D) end end, State1, Request), case NewState#state.end_of_request of true -> ok; _ -> receive_headers(NewState) end; _ -> ok end end.process_header(State, Data) -> SockMod = State#state.sockmod, Socket = State#state.socket, case Data of {ok, {http_request, Method, Path, Version}} -> KeepAlive = case Version of {1, 1} -> true; _ -> false end, State#state{request_method = Method, request_version = Version, request_path = Path, request_keepalive = KeepAlive}; {ok, {http_header, _, 'Connection', _, Conn}} -> KeepAlive1 = case Conn of "keep-alive" -> true; "close" -> false; _ -> State#state.request_keepalive end, State#state{request_keepalive = KeepAlive1}; {ok, {http_header, _, 'Authorization', _, Auth}} -> State#state{request_auth = parse_auth(Auth)}; {ok, {http_header, _, 'Content-Length', _, SLen}} -> case catch list_to_integer(SLen) of Len when is_integer(Len) -> State#state{request_content_length = Len}; _ -> State end; {ok, {http_header, _, 'Accept-Language', _, Langs}} -> State#state{request_lang = parse_lang(Langs)}; {ok, {http_header, _, _, _, _}} -> State; {ok, http_eoh} -> ?INFO_MSG("(~w) http query: ~w ~s~n", [State#state.socket, State#state.request_method, element(2, State#state.request_path)]), Out = process_request(State), send_text(State, Out), case State#state.request_keepalive of true -> case SockMod of gen_tcp -> inet:setopts(Socket, [{packet, http}]); _ -> ok end, #state{sockmod = SockMod, socket = Socket, use_http_poll = State#state.use_http_poll, use_web_admin = State#state.use_web_admin}; _ -> #state{end_of_request = true} end; {error, _Reason} -> #state{end_of_request = true}; _ -> #state{end_of_request = true} end.process_request(#state{request_method = 'GET', request_path = {abs_path, Path}, request_auth = Auth, request_lang = Lang, use_http_poll = UseHTTPPoll, use_web_admin = UseWebAdmin}) -> User = case Auth of {U, P} -> case ejabberd_auth:check_password(U, P) of true -> U; false -> unauthorized end; _ -> undefined end, case User of unauthorized -> make_xhtml_output( 401, [{"WWW-Authenticate", "basic realm=\"ejabberd\""}], ejabberd_web:make_xhtml([{xmlelement, "h1", [], [{xmlcdata, "401 Unauthorized"}]}])); _ -> case (catch url_decode_q_split(Path)) of {'EXIT', _} -> process_request(false); {NPath, Query} -> LQuery = case (catch parse_urlencoded(Query)) of {'EXIT', _Reason} -> []; LQ -> LQ end, LPath = string:tokens(NPath, "/"), Request = #request{method = 'GET', path = LPath, q = LQuery, user = User, lang = Lang}, case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin}, Request) of El when element(1, El) == xmlelement -> make_xhtml_output(200, [], El); {Status, Headers, El} when element(1, El) == xmlelement -> make_xhtml_output(Status, Headers, El); Text when is_list(Text) -> make_text_output(200, [], Text); {Status, Headers, Text} when is_list(Text) -> make_text_output(Status, Headers, Text) end end end;process_request(#state{request_method = 'POST', request_path = {abs_path, Path}, request_auth = Auth, request_content_length = Len, request_lang = Lang, sockmod = SockMod, socket = Socket, use_http_poll = UseHTTPPoll, use_web_admin = UseWebAdmin} = State) when is_integer(Len) -> User = case Auth of {U, P} -> case ejabberd_auth:check_password(U, P) of true -> U; false -> unauthorized end; _ -> undefined end, case User of unauthorized -> make_xhtml_output( 401, [{"WWW-Authenticate", "basic realm=\"ejabberd\""}], ejabberd_web:make_xhtml([{xmlelement, "h1", [], [{xmlcdata, "401 Unauthorized"}]}])); _ -> case SockMod of gen_tcp -> inet:setopts(Socket, [{packet, 0}]); _ -> ok end, Data = recv_data(State, Len), ?DEBUG("client data: ~p~n", [Data]), case (catch url_decode_q_split(Path)) of {'EXIT', _} -> process_request(false); {NPath, Query} -> LPath = string:tokens(NPath, "/"), LQuery = case (catch parse_urlencoded(Data)) of {'EXIT', _Reason} -> []; LQ -> LQ end, Request = #request{method = 'POST', path = LPath, q = LQuery, user = User, data = Data, lang = Lang}, case ejabberd_web:process_get({UseHTTPPoll, UseWebAdmin}, Request) of El when element(1, El) == xmlelement -> make_xhtml_output(200, [], El); {Status, Headers, El} when element(1, El) == xmlelement -> make_xhtml_output(Status, Headers, El); Text when is_list(Text) -> make_text_output(200, [], Text); {Status, Headers, Text} when is_list(Text) -> make_text_output(Status, Headers, Text) end end end;process_request(_) -> make_xhtml_output( 400, [], ejabberd_web:make_xhtml([{xmlelement, "h1", [], [{xmlcdata, "400 Bad Request"}]}])).recv_data(State, Len) -> recv_data(State, Len, []).recv_data(State, 0, Acc) -> binary_to_list(list_to_binary(Acc));recv_data(State, Len, Acc) -> case State#state.trail of [] -> case (State#state.sockmod):recv(State#state.socket, Len, 300000) of {ok, Data} -> recv_data(State, Len - size(Data), [Acc | Data]); _ -> "" end; _ -> Trail = State#state.trail, recv_data(State#state{trail = ""}, Len - length(Trail), [Acc | Trail]) end.make_xhtml_output(Status, Headers, XHTML) -> Data = case lists:member(html, Headers) of true -> list_to_binary([?HTML_DOCTYPE, xml:element_to_string(XHTML)]); _ -> list_to_binary([?XHTML_DOCTYPE, xml:element_to_string(XHTML)]) end, Headers1 = case lists:keysearch("Content-Type", 1, Headers) of {value, _} -> [{"Content-Length", integer_to_list(size(Data))} | Headers]; _ -> [{"Content-Type", "text/html; charset=utf-8"}, {"Content-Length", integer_to_list(size(Data))} | Headers] end, H = lists:map(fun({Attr, Val}) -> [Attr, ": ", Val, "\r\n"]; (_) -> [] end, Headers1), SL = ["HTTP/1.1 ", integer_to_list(Status), " ", code_to_phrase(Status), "\r\n"], [SL, H, "\r\n", Data].make_text_output(Status, Headers, Text) -> Data = list_to_binary(Text), Headers1 = case lists:keysearch("Content-Type", 1, Headers) of {value, _} -> [{"Content-Length", integer_to_list(size(Data))} | Headers]; _ -> [{"Content-Type", "text/html; charset=utf-8"}, {"Content-Length", integer_to_list(size(Data))} | Headers] end, H = lists:map(fun({Attr, Val}) -> [Attr, ": ", Val, "\r\n"] end, Headers1), SL = ["HTTP/1.1 ", integer_to_list(Status), " ", code_to_phrase(Status), "\r\n"], [SL, H, "\r\n", Data]. parse_lang(Langs) -> case string:tokens(Langs, ",; ") of [First | _] -> First; [] -> "en" end.% Code below is taken (with some modifications) from the yaws webserver, which% is distributed under the folowing license:%% This software (the yaws webserver) is free software.% Parts of this software is Copyright (c) Claes Wikstrom <klacke@hyber.org>% Any use or misuse of the source code is hereby freely allowed.%% 1. Redistributions of source code must retain the above copyright% notice as well as this list of conditions.%% 2. Redistributions in binary form must reproduce the above copyright
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -