mod_http_upload: Add "custom_headers" option

The new "custom_headers" option allows admins to specify header fields
that are added to HTTP responses.
This commit is contained in:
Holger Weiss 2015-08-31 22:24:52 +02:00
parent 10092e3bd5
commit 9eb68b4ef3
2 changed files with 48 additions and 22 deletions

View File

@ -146,6 +146,15 @@ The configurable mod_http_upload options are:
In any other case, a 'service-unavailable' error stanza is sent to the In any other case, a 'service-unavailable' error stanza is sent to the
client. client.
- custom_headers (default: [])
This option specifies additional header fields to be included in all HTTP
responses. For example:
custom_headers:
"Access-Control-Allow-Origin": "*"
"Access-Control-Allow-Methods": "GET, PUT"
- rm_on_unregister (default: 'true') - rm_on_unregister (default: 'true')
This option specifies whether files uploaded by a user should be removed This option specifies whether files uploaded by a user should be removed

View File

@ -185,11 +185,18 @@ mod_opt_type(service_url) ->
fun(<<"http://", _/binary>> = URL) -> URL; fun(<<"http://", _/binary>> = URL) -> URL;
(<<"https://", _/binary>> = URL) -> URL (<<"https://", _/binary>> = URL) -> URL
end; end;
mod_opt_type(custom_headers) ->
fun(Headers) ->
lists:map(fun({K, V}) ->
{iolist_to_binary(K), iolist_to_binary(V)}
end, Headers)
end;
mod_opt_type(rm_on_unregister) -> mod_opt_type(rm_on_unregister) ->
fun(B) when is_boolean(B) -> B end; fun(B) when is_boolean(B) -> B end;
mod_opt_type(_) -> mod_opt_type(_) ->
[host, name, access, max_size, secret_length, jid_in_url, file_mode, [host, name, access, max_size, secret_length, jid_in_url, file_mode,
dir_mode, docroot, put_url, get_url, service_url, rm_on_unregister]. dir_mode, docroot, put_url, get_url, service_url, custom_headers,
rm_on_unregister].
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% gen_server callbacks. %% gen_server callbacks.
@ -347,24 +354,24 @@ process(LocalPath, #request{method = 'PUT', host = Host, ip = IP,
[?ADDR_TO_STR(IP), Host, Path]), [?ADDR_TO_STR(IP), Host, Path]),
case store_file(Path, Data, FileMode, DirMode) of case store_file(Path, Data, FileMode, DirMode) of
ok -> ok ->
http_response(201); http_response(Host, 201);
{error, Error} -> {error, Error} ->
?ERROR_MSG("Cannot store file ~s from ~s for ~s: ~s", ?ERROR_MSG("Cannot store file ~s from ~s for ~s: ~s",
[Path, ?ADDR_TO_STR(IP), Host, Error]), [Path, ?ADDR_TO_STR(IP), Host, Error]),
http_response(500) http_response(Host, 500)
end; end;
{ok, Size, Path} -> {ok, Size, Path} ->
?INFO_MSG("Rejecting file ~s from ~s for ~s: Size is ~B, not ~B", ?INFO_MSG("Rejecting file ~s from ~s for ~s: Size is ~B, not ~B",
[Path, ?ADDR_TO_STR(IP), Host, byte_size(Data), Size]), [Path, ?ADDR_TO_STR(IP), Host, byte_size(Data), Size]),
http_response(413); http_response(Host, 413);
{error, Error} -> {error, Error} ->
?INFO_MSG("Rejecting file from ~s for ~s: ~p", ?INFO_MSG("Rejecting file from ~s for ~s: ~p",
[?ADDR_TO_STR(IP), Host, Error]), [?ADDR_TO_STR(IP), Host, Error]),
http_response(403); http_response(Host, 403);
Error -> Error ->
?ERROR_MSG("Cannot handle PUT request from ~s for ~s: ~p", ?ERROR_MSG("Cannot handle PUT request from ~s for ~s: ~p",
[?ADDR_TO_STR(IP), Host, Error]), [?ADDR_TO_STR(IP), Host, Error]),
http_response(500) http_response(Host, 500)
end; end;
process(LocalPath, #request{method = 'GET', host = Host, ip = IP}) -> process(LocalPath, #request{method = 'GET', host = Host, ip = IP}) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME), Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
@ -385,33 +392,33 @@ process(LocalPath, #request{method = 'GET', host = Host, ip = IP}) ->
$", FileName/binary, $">>}] $", FileName/binary, $">>}]
end, end,
Headers2 = [{<<"Content-Type">>, ContentType} | Headers1], Headers2 = [{<<"Content-Type">>, ContentType} | Headers1],
http_response(200, Headers2, Data); http_response(Host, 200, Headers2, Data);
{error, eacces} -> {error, eacces} ->
?INFO_MSG("Cannot serve ~s to ~s: Permission denied", ?INFO_MSG("Cannot serve ~s to ~s: Permission denied",
[Path, ?ADDR_TO_STR(IP)]), [Path, ?ADDR_TO_STR(IP)]),
http_response(403); http_response(Host, 403);
{error, enoent} -> {error, enoent} ->
?INFO_MSG("Cannot serve ~s to ~s: No such file or directory", ?INFO_MSG("Cannot serve ~s to ~s: No such file or directory",
[Path, ?ADDR_TO_STR(IP)]), [Path, ?ADDR_TO_STR(IP)]),
http_response(404); http_response(Host, 404);
{error, eisdir} -> {error, eisdir} ->
?INFO_MSG("Cannot serve ~s to ~s: Is a directory", ?INFO_MSG("Cannot serve ~s to ~s: Is a directory",
[Path, ?ADDR_TO_STR(IP)]), [Path, ?ADDR_TO_STR(IP)]),
http_response(404); http_response(Host, 404);
{error, Error} -> {error, Error} ->
?INFO_MSG("Cannot serve ~s to ~s: ~p", ?INFO_MSG("Cannot serve ~s to ~s: ~p",
[Path, ?ADDR_TO_STR(IP), Error]), [Path, ?ADDR_TO_STR(IP), Error]),
http_response(500) http_response(Host, 500)
end; end;
Error -> Error ->
?ERROR_MSG("Cannot handle GET request from ~s for ~s: ~p", ?ERROR_MSG("Cannot handle GET request from ~s for ~s: ~p",
[?ADDR_TO_STR(IP), Host, Error]), [?ADDR_TO_STR(IP), Host, Error]),
http_response(500) http_response(Host, 500)
end; end;
process(_LocalPath, #request{method = Method, host = Host, ip = IP}) -> process(_LocalPath, #request{method = Method, host = Host, ip = IP}) ->
?DEBUG("Rejecting ~s request from ~s for ~s", ?DEBUG("Rejecting ~s request from ~s for ~s",
[Method, ?ADDR_TO_STR(IP), Host]), [Method, ?ADDR_TO_STR(IP), Host]),
http_response(405, [{<<"Allow">>, <<"GET, PUT">>}]). http_response(Host, 405, [{<<"Allow">>, <<"GET, PUT">>}]).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Internal functions. %% Internal functions.
@ -689,30 +696,40 @@ guess_content_type(FileName) ->
?DEFAULT_CONTENT_TYPE, ?DEFAULT_CONTENT_TYPE,
?CONTENT_TYPES). ?CONTENT_TYPES).
-spec http_response(100..599) -spec http_response(binary(), 100..599)
-> {pos_integer(), [{binary(), binary()}], binary()}. -> {pos_integer(), [{binary(), binary()}], binary()}.
http_response(Code) -> http_response(Host, Code) ->
http_response(Code, []). http_response(Host, Code, []).
-spec http_response(100..599, [{binary(), binary()}]) -spec http_response(binary(), 100..599, [{binary(), binary()}])
-> {pos_integer(), [{binary(), binary()}], binary()}. -> {pos_integer(), [{binary(), binary()}], binary()}.
http_response(Code, ExtraHeaders) -> http_response(Host, Code, ExtraHeaders) ->
http_response(Code, ExtraHeaders, <<(code_to_message(Code))/binary, $\n>>). Message = <<(code_to_message(Code))/binary, $\n>>,
http_response(Host, Code, ExtraHeaders, Message).
-spec http_response(100..599, [{binary(), binary()}], binary()) -spec http_response(binary(), 100..599, [{binary(), binary()}], binary())
-> {pos_integer(), [{binary(), binary()}], binary()}. -> {pos_integer(), [{binary(), binary()}], binary()}.
http_response(Code, ExtraHeaders, Body) -> http_response(Host, Code, ExtraHeaders, Body) ->
ServerHeader = {<<"Server">>, <<"ejabberd ", (?VERSION)/binary>>}, ServerHeader = {<<"Server">>, <<"ejabberd ", (?VERSION)/binary>>},
CustomHeaders =
gen_mod:get_module_opt(Host, ?MODULE, custom_headers,
fun(Headers) ->
lists:map(fun({K, V}) ->
{iolist_to_binary(K),
iolist_to_binary(V)}
end, Headers)
end,
[]),
Headers = case proplists:is_defined(<<"Content-Type">>, ExtraHeaders) of Headers = case proplists:is_defined(<<"Content-Type">>, ExtraHeaders) of
true -> true ->
[ServerHeader | ExtraHeaders]; [ServerHeader | ExtraHeaders];
false -> false ->
[ServerHeader, {<<"Content-Type">>, <<"text/plain">>} | [ServerHeader, {<<"Content-Type">>, <<"text/plain">>} |
ExtraHeaders] ExtraHeaders]
end, end ++ CustomHeaders,
{Code, Headers, Body}. {Code, Headers, Body}.
-spec code_to_message(100..599) -> binary(). -spec code_to_message(100..599) -> binary().