-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathechessd_mime_types.erl
146 lines (123 loc) · 4.78 KB
/
echessd_mime_types.erl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
%% @doc
%% MIME types reference keeper process.
%% @author Aleksey Morarash <[email protected]>
%% @since 21 Nov 2013
%% @copyright 2012, Aleksey Morarash
-module(echessd_mime_types).
-behaviour(gen_server).
%% API exports
-export([start_link/0, lookup/1, hup/0]).
%% gen_server callback exports
-export([init/1, handle_call/3, handle_info/2, handle_cast/2,
terminate/2, code_change/3]).
-include("echessd.hrl").
%% ----------------------------------------------------------------------
%% Internal signals and keywords
%% ----------------------------------------------------------------------
-define(CAST_HUP, hup).
-define(CALL_GET_STATE, get_state).
%% ----------------------------------------------------------------------
%% API functions
%% ----------------------------------------------------------------------
%% @doc Start the process as part of the supervision tree.
-spec start_link() -> {ok, Pid :: pid()} | ignore | {error, Reason :: any()}.
start_link() ->
gen_server:start_link(
{local, ?MODULE}, ?MODULE, _Args = undefined, _Options = []).
%% @doc Search mime type for the file extension.
-spec lookup(Extension :: string()) -> MimeType :: string().
lookup(Extension) ->
case ets:lookup(?MODULE, string:to_lower(Extension)) of
[{_, MimeType}] ->
MimeType;
_ ->
"application/octet-stream"
end.
%% @doc Schedule configuration reload.
-spec hup() -> ok.
hup() ->
gen_server:cast(?MODULE, ?CAST_HUP).
%% ----------------------------------------------------------------------
%% gen_server callbacks
%% ----------------------------------------------------------------------
-record(state, {}).
%% @hidden
-spec init(Args :: any()) -> {ok, InitialState :: #state{}}.
init(_Args) ->
?MODULE = ets:new(?MODULE, [named_table]),
ok = hup(),
{ok, _State = #state{}}.
%% @hidden
-spec handle_cast(Request :: ?CAST_HUP, State :: #state{}) ->
{noreply, NewState :: #state{}}.
handle_cast(?CAST_HUP, State) ->
{ok, List} = read_mime_types(echessd_cfg:get(?CFG_MIME_TYPES)),
true = ets:insert(?MODULE, [{E, T} || {T, L} <- List, E <- L]),
{noreply, State}.
%% @hidden
-spec handle_info(Info :: any(), State :: #state{}) ->
{noreply, State :: #state{}}.
handle_info(_Request, State) ->
{noreply, State}.
%% @hidden
-spec handle_call(Request :: any(), From :: any(), State :: #state{}) ->
{noreply, NewState :: #state{}}.
handle_call(_Request, _From, State) ->
{noreply, State}.
%% @hidden
-spec terminate(Reason :: any(), State :: #state{}) -> ok.
terminate(_Reason, _State) ->
ok.
%% @hidden
-spec code_change(OldVersion :: any(), State :: #state{}, Extra :: any()) ->
{ok, NewState :: #state{}}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% ----------------------------------------------------------------------
%% Internal functions
%% ----------------------------------------------------------------------
%% @doc Read and parse mime types file.
-spec read_mime_types(Filename :: file:filename()) ->
{ok, List :: [{MimeType :: nonempty_string(),
Extensions :: [nonempty_string()]}]} |
{error, Reason :: any()}.
read_mime_types(Filename) ->
case file:open(Filename, [read, raw, read_ahead]) of
{ok, FH} ->
Result =
try read_mime_types_loop(FH, [])
catch
Type:Reason:StackTrace ->
{error,
{Type, Reason, StackTrace}}
end,
catch file:close(FH),
Result;
Error -> Error
end.
-spec read_mime_types_loop(FH :: file:io_device(),
Acc ::
[{MimeType :: nonempty_string(),
Extensions :: [nonempty_string()]}]) ->
{ok,
List ::
[{MimeType :: nonempty_string(),
Extensions :: [nonempty_string()]}]} |
{error, Reason :: any()}.
read_mime_types_loop(FH, Acc) ->
case file:read_line(FH) of
eof ->
{ok, lists:reverse(Acc)};
{ok, Line} ->
case echessd_lib:strip(Line, " \t\r\n") of
[C | _] = Stripped when C /= $# ->
[MimeType | Extensions] =
string:tokens(Stripped, " \t"),
read_mime_types_loop(
FH, [{MimeType, Extensions} | Acc]);
_ ->
read_mime_types_loop(FH, Acc)
end;
Error ->
Error
end.