Implement reload/3 for mod_http_upload

This commit is contained in:
Evgeny Khramtsov 2019-08-06 12:31:33 +03:00
parent 11d519677e
commit 934392fd7e
2 changed files with 116 additions and 73 deletions

View File

@ -56,7 +56,7 @@
-callback start(binary(), opts()) -> ok | {ok, pid()} | {error, term()}.
-callback stop(binary()) -> any().
-callback reload(binary(), opts(), opts()) -> ok | {ok, pid()}.
-callback reload(binary(), opts(), opts()) -> ok | {ok, pid()} | {error, term()}.
-callback mod_opt_type(atom()) -> econf:validator().
-callback mod_options(binary()) -> [{atom(), term()} | atom()].
-callback depends(binary(), opts()) -> [{module(), hard | soft}].

View File

@ -61,6 +61,7 @@
%% gen_mod/supervisor callbacks.
-export([start/2,
stop/1,
reload/3,
depends/2,
mod_opt_type/1,
mod_options/1]).
@ -90,23 +91,23 @@
-include("translate.hrl").
-record(state,
{server_host :: binary(),
hosts :: [binary()],
name :: binary(),
access :: atom(),
max_size :: pos_integer() | infinity,
secret_length :: pos_integer(),
jid_in_url :: sha1 | node,
{server_host = <<>> :: binary(),
hosts = [] :: [binary()],
name = <<>> :: binary(),
access = none :: atom(),
max_size = infinity :: pos_integer() | infinity,
secret_length = 40 :: pos_integer(),
jid_in_url = sha1 :: sha1 | node,
file_mode :: integer() | undefined,
dir_mode :: integer() | undefined,
docroot :: binary(),
put_url :: binary(),
get_url :: binary(),
docroot = <<>> :: binary(),
put_url = <<>> :: binary(),
get_url = <<>> :: binary(),
service_url :: binary() | undefined,
thumbnail :: boolean(),
custom_headers :: [{binary(), binary()}],
thumbnail = false :: boolean(),
custom_headers = [] :: [{binary(), binary()}],
slots = #{} :: slots(),
external_secret :: binary()}).
external_secret = <<>> :: binary()}).
-record(media_info,
{path :: binary(),
@ -122,37 +123,37 @@
%%--------------------------------------------------------------------
%% gen_mod/supervisor callbacks.
%%--------------------------------------------------------------------
-spec start(binary(), gen_mod:opts()) -> {ok, pid()} | {error, already_started}.
-spec start(binary(), gen_mod:opts()) -> {ok, pid()} | {error, term()}.
start(ServerHost, Opts) ->
case mod_http_upload_opt:rm_on_unregister(Opts) of
true ->
ejabberd_hooks:add(remove_user, ServerHost, ?MODULE,
remove_user, 50);
false ->
ok
end,
Proc = get_proc_name(ServerHost, ?MODULE),
case whereis(Proc) of
undefined ->
gen_mod:start_child(?MODULE, ServerHost, Opts, Proc);
_Pid ->
case gen_mod:start_child(?MODULE, ServerHost, Opts, Proc) of
{ok, _} = Ret -> Ret;
{error, {already_started, _}} = Err ->
?ERROR_MSG("Multiple virtual hosts can't use a single 'put_url' "
"without the @HOST@ keyword", []),
{error, already_started}
Err;
Err ->
Err
end.
-spec stop(binary()) -> ok | {error, any()}.
stop(ServerHost) ->
case mod_http_upload_opt:rm_on_unregister(ServerHost) of
true ->
ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE,
remove_user, 50);
false ->
ok
end,
Proc = get_proc_name(ServerHost, ?MODULE),
gen_mod:stop_child(Proc).
-spec reload(binary(), gen_mod:opts(), gen_mod:opts()) -> ok | {ok, pid()} | {error, term()}.
reload(ServerHost, NewOpts, OldOpts) ->
NewURL = mod_http_upload_opt:put_url(NewOpts),
OldURL = mod_http_upload_opt:put_url(OldOpts),
OldProc = get_proc_name(ServerHost, ?MODULE, OldURL),
NewProc = get_proc_name(ServerHost, ?MODULE, NewURL),
if OldProc /= NewProc ->
gen_mod:stop_child(OldProc),
start(ServerHost, NewOpts);
true ->
gen_server:cast(NewProc, {reload, NewOpts, OldOpts})
end.
-spec mod_opt_type(atom()) -> econf:validator().
mod_opt_type(name) ->
econf:binary();
@ -234,46 +235,15 @@ init([ServerHost|_]) ->
process_flag(trap_exit, true),
Opts = gen_mod:get_module_opts(ServerHost, ?MODULE),
Hosts = gen_mod:get_opt_hosts(Opts),
Name = mod_http_upload_opt:name(Opts),
Access = mod_http_upload_opt:access(Opts),
MaxSize = mod_http_upload_opt:max_size(Opts),
SecretLength = mod_http_upload_opt:secret_length(Opts),
JIDinURL = mod_http_upload_opt:jid_in_url(Opts),
DocRoot = mod_http_upload_opt:docroot(Opts),
FileMode = mod_http_upload_opt:file_mode(Opts),
DirMode = mod_http_upload_opt:dir_mode(Opts),
PutURL = mod_http_upload_opt:put_url(Opts),
GetURL = case mod_http_upload_opt:get_url(Opts) of
undefined -> PutURL;
URL -> URL
end,
ServiceURL = mod_http_upload_opt:service_url(Opts),
Thumbnail = mod_http_upload_opt:thumbnail(Opts),
ExternalSecret = mod_http_upload_opt:external_secret(Opts),
CustomHeaders = mod_http_upload_opt:custom_headers(Opts),
DocRoot1 = expand_home(str:strip(DocRoot, right, $/)),
DocRoot2 = expand_host(DocRoot1, ServerHost),
case DirMode of
undefined ->
ok;
Mode ->
file:change_mode(DocRoot2, Mode)
case mod_http_upload_opt:rm_on_unregister(Opts) of
true ->
ejabberd_hooks:add(remove_user, ServerHost, ?MODULE,
remove_user, 50);
false ->
ok
end,
lists:foreach(
fun(Host) ->
ejabberd_router:register_route(Host, ServerHost)
end, Hosts),
{ok, #state{server_host = ServerHost, hosts = Hosts, name = Name,
access = Access, max_size = MaxSize,
secret_length = SecretLength, jid_in_url = JIDinURL,
file_mode = FileMode, dir_mode = DirMode,
thumbnail = Thumbnail,
docroot = DocRoot2,
put_url = expand_host(str:strip(PutURL, right, $/), ServerHost),
get_url = expand_host(str:strip(GetURL, right, $/), ServerHost),
service_url = ServiceURL,
external_secret = ExternalSecret,
custom_headers = CustomHeaders}}.
State = init_state(ServerHost, Hosts, Opts),
{ok, State}.
-spec handle_call(_, {pid(), _}, state())
-> {reply, {ok, pos_integer(), binary(),
@ -309,6 +279,24 @@ handle_call(Request, From, State) ->
{noreply, State}.
-spec handle_cast(_, state()) -> {noreply, state()}.
handle_cast({reload, NewOpts, OldOpts},
#state{server_host = ServerHost} = State) ->
case {mod_http_upload_opt:rm_on_unregister(NewOpts),
mod_http_upload_opt:rm_on_unregister(OldOpts)} of
{true, false} ->
ejabberd_hooks:add(remove_user, ServerHost, ?MODULE,
remove_user, 50);
{false, true} ->
ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE,
remove_user, 50);
_ ->
ok
end,
NewHosts = gen_mod:get_opt_hosts(NewOpts),
OldHosts = gen_mod:get_opt_hosts(OldOpts),
lists:foreach(fun ejabberd_router:unregister_route/1, OldHosts -- NewHosts),
NewState = init_state(State#state{hosts = NewHosts -- OldHosts}, NewOpts),
{noreply, NewState};
handle_cast(Request, State) ->
?WARNING_MSG("Unexpected cast: ~p", [Request]),
{noreply, State}.
@ -347,6 +335,7 @@ handle_info(Info, State) ->
-spec terminate(normal | shutdown | {shutdown, _} | _, state()) -> ok.
terminate(Reason, #state{server_host = ServerHost, hosts = Hosts}) ->
?DEBUG("Stopping HTTP upload process for ~s: ~p", [ServerHost, Reason]),
ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE, remove_user, 50),
lists:foreach(fun ejabberd_router:unregister_route/1, Hosts).
-spec code_change({down, _} | _, state(), _) -> {ok, state()}.
@ -484,12 +473,66 @@ process(_LocalPath, #request{method = Method, host = Host, ip = IP}) ->
[Method, encode_addr(IP), Host]),
http_response(405, [{<<"Allow">>, <<"OPTIONS, HEAD, GET, PUT">>}]).
%%--------------------------------------------------------------------
%% State initialization
%%--------------------------------------------------------------------
-spec init_state(binary(), [binary()], gen_mod:opts()) -> state().
init_state(ServerHost, Hosts, Opts) ->
init_state(#state{server_host = ServerHost, hosts = Hosts}, Opts).
-spec init_state(state(), gen_mod:opts()) -> state().
init_state(#state{server_host = ServerHost, hosts = Hosts} = State, Opts) ->
Name = mod_http_upload_opt:name(Opts),
Access = mod_http_upload_opt:access(Opts),
MaxSize = mod_http_upload_opt:max_size(Opts),
SecretLength = mod_http_upload_opt:secret_length(Opts),
JIDinURL = mod_http_upload_opt:jid_in_url(Opts),
DocRoot = mod_http_upload_opt:docroot(Opts),
FileMode = mod_http_upload_opt:file_mode(Opts),
DirMode = mod_http_upload_opt:dir_mode(Opts),
PutURL = mod_http_upload_opt:put_url(Opts),
GetURL = case mod_http_upload_opt:get_url(Opts) of
undefined -> PutURL;
URL -> URL
end,
ServiceURL = mod_http_upload_opt:service_url(Opts),
Thumbnail = mod_http_upload_opt:thumbnail(Opts),
ExternalSecret = mod_http_upload_opt:external_secret(Opts),
CustomHeaders = mod_http_upload_opt:custom_headers(Opts),
DocRoot1 = expand_home(str:strip(DocRoot, right, $/)),
DocRoot2 = expand_host(DocRoot1, ServerHost),
case DirMode of
undefined ->
ok;
Mode ->
file:change_mode(DocRoot2, Mode)
end,
lists:foreach(
fun(Host) ->
ejabberd_router:register_route(Host, ServerHost)
end, Hosts),
State#state{server_host = ServerHost, hosts = Hosts, name = Name,
access = Access, max_size = MaxSize,
secret_length = SecretLength, jid_in_url = JIDinURL,
file_mode = FileMode, dir_mode = DirMode,
thumbnail = Thumbnail,
docroot = DocRoot2,
put_url = expand_host(str:strip(PutURL, right, $/), ServerHost),
get_url = expand_host(str:strip(GetURL, right, $/), ServerHost),
service_url = ServiceURL,
external_secret = ExternalSecret,
custom_headers = CustomHeaders}.
%%--------------------------------------------------------------------
%% Exported utility functions.
%%--------------------------------------------------------------------
-spec get_proc_name(binary(), atom()) -> atom().
get_proc_name(ServerHost, ModuleName) ->
PutURL = mod_http_upload_opt:put_url(ServerHost),
get_proc_name(ServerHost, ModuleName, PutURL).
-spec get_proc_name(binary(), atom(), binary()) -> atom().
get_proc_name(ServerHost, ModuleName, PutURL) ->
%% Once we depend on OTP >= 20.0, we can use binaries with http_uri.
{ok, {_Scheme, _UserInfo, Host0, _Port, Path0, _Query}} =
http_uri:parse(binary_to_list(expand_host(PutURL, ServerHost))),