_site/cover/resp_listener.COVER.html

1 %% Copyright (c) 2023 Peter Morgan <peter.james.morgan@gmail.com>
2 %%
3 %% Licensed under the Apache License, Version 2.0 (the "License");
4 %% you may not use this file except in compliance with the License.
5 %% You may obtain a copy of the License at
6 %%
7 %% http://www.apache.org/licenses/LICENSE-2.0
8 %%
9 %% Unless required by applicable law or agreed to in writing, software
10 %% distributed under the License is distributed on an "AS IS" BASIS,
11 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 %% See the License for the specific language governing permissions and
13 %% limitations under the License.
14
15
16 -module(resp_listener).
17
18
19 -export([callback_mode/0]).
20 -export([handle_event/4]).
21 -export([init/1]).
22 -export([start/0]).
23 -export([start/1]).
24 -export([start_link/0]).
25 -export([start_link/1]).
26 -import(resp_statem, [nei/1]).
27 -include_lib("kernel/include/inet.hrl").
28 -include_lib("kernel/include/logger.hrl").
29
30
31 start() ->
32
:-(
?FUNCTION_NAME(#{}).
33
34
35 start(Arg) ->
36
:-(
gen_statem:start(?MODULE, [Arg], envy_gen:options(?MODULE)).
37
38
39 start_link() ->
40
:-(
?FUNCTION_NAME(#{}).
41
42
43 start_link(Arg) ->
44
:-(
gen_statem:start_link(?MODULE, [Arg], envy_gen:options(?MODULE)).
45
46
47 init([Arg]) ->
48
:-(
process_flag(trap_exit, true),
49
:-(
case resp_config:enabled(listener) of
50 true ->
51
:-(
{ok,
52 unready,
53 #{arg => Arg, requests => gen_statem:reqids_new()},
54 [nei(callback_init), nei(open)]};
55
56 false ->
57
:-(
ignore
58 end.
59
60
61 callback_mode() ->
62
:-(
handle_event_function.
63
64
65 handle_event(internal,
66 callback_init,
67 _,
68 #{arg := #{callback := Module}} = Data) ->
69
:-(
case Module:init([]) of
70 {ok, CallbackData} ->
71
:-(
{keep_state, Data#{callback_data => CallbackData}};
72
73 stop ->
74
:-(
stop;
75
76 {stop, Reason} ->
77
:-(
{stop, Reason}
78 end;
79
80 handle_event(internal,
81 {connect = EventName, Socket},
82 _,
83 #{callback_data := CallbackData,
84 arg := #{callback := Module}}) ->
85
:-(
{ok, Child} = resp_connection_sup:start_child(
86 #{socket => Socket,
87 callback => #{data => CallbackData,
88 module => Module}}),
89
:-(
{keep_state_and_data,
90 [nei({setopt,
91 #{socket => Socket,
92 level => otp,
93 option => controlling_process,
94 value => Child}}),
95
96 nei({telemetry, EventName, #{count => 1}})]};
97
98 handle_event(info,
99 {'$socket', Listener, select = EventName, Handle},
100 _,
101 #{socket := Listener}) ->
102
:-(
case socket:accept(Listener, Handle) of
103 {ok, Connected} ->
104
:-(
{keep_state_and_data,
105 [nei({telemetry, EventName, #{count => 1}}),
106 nei({connect, Connected}),
107 nei(accept)]};
108
109 {select, {select_info, _, _}} ->
110
:-(
keep_state_and_data;
111
112 {error, Reason} ->
113
:-(
{stop, Reason}
114 end;
115
116
117 handle_event(internal, accept = EventName, _, #{socket := Listener}) ->
118
:-(
case socket:accept(Listener, nowait) of
119 {ok, Connected} ->
120
:-(
{keep_state_and_data,
121 [nei({telemetry, EventName, #{count => 1}}),
122 nei({connect, Connected}),
123 nei(accept)]};
124
125 {select, {select_info, _, _}} ->
126
:-(
keep_state_and_data;
127
128 {error, Reason} ->
129
:-(
{stop, Reason}
130 end;
131
132 handle_event(internal, listen = EventName, _, #{socket := Listener}) ->
133
:-(
case socket:listen(Listener, resp_config:listener(backlog)) of
134 ok ->
135
:-(
{keep_state_and_data,
136 [nei({telemetry, EventName, #{count => 1}}), nei(accept)]};
137
138 {error, Reason} ->
139
:-(
{stop, Reason}
140 end;
141
142 handle_event(internal, open = EventName, _, Data) ->
143
:-(
case socket:open(inet, stream, tcp) of
144 {ok, Listener} ->
145
:-(
{keep_state,
146 Data#{socket => Listener},
147 [nei({telemetry,
148 EventName,
149 #{count => 1}}),
150
151 nei({setopt,
152 #{socket => Listener,
153 level => socket,
154 option => reuseaddr,
155 value => true}}),
156
157 nei(bind)]};
158
159 {error, Reason} ->
160
:-(
{stop, Reason}
161 end;
162
163 handle_event(
164 internal,
165 {setopt,
166 #{socket := Socket, level := Level, option := Option, value := Value}},
167 _,
168 _) ->
169
:-(
case socket:setopt(Socket, {Level, Option}, Value) of
170 ok ->
171
:-(
keep_state_and_data;
172
173 {error, Reason} ->
174
:-(
{stop, Reason}
175 end;
176
177 handle_event(internal, bind = EventName, _, _) ->
178
:-(
{keep_state_and_data,
179 nei({EventName,
180 #{family => inet,
181 port => resp_config:listener(port),
182 addr => any}})};
183
184 handle_event(internal,
185 {bind = EventName, Addr},
186 _,
187 #{socket := Listener} = Data) ->
188
:-(
case socket:bind(Listener, Addr) of
189 ok ->
190
:-(
{next_state,
191 ready,
192 Data,
193 [nei({telemetry, EventName, #{count => 1}, Addr}),
194 nei(listen)]};
195
196 {error, Reason} ->
197
:-(
{stop, Reason}
198 end;
199
200 handle_event(internal,
201 {telemetry, EventName, Measurements},
202 _,
203 _) ->
204
:-(
{keep_state_and_data,
205 nei({telemetry, EventName, Measurements, #{}})};
206
207 handle_event(internal,
208 {telemetry, EventName, Measurements, Metadata},
209 _,
210 Data) ->
211
:-(
ok = telemetry:execute(
212 [resp, listener, EventName],
213 Measurements,
214 maps:merge(
215 maps:with([socket], Data),
216 Metadata)),
217
:-(
keep_state_and_data;
218
219 handle_event(info, Msg, _, #{requests := Existing} = Data) ->
220
:-(
case gen_statem:check_response(Msg, Existing, true) of
221 {{reply, ok}, reaper, Updated} ->
222
:-(
{keep_state, Data#{requests := Updated}};
223
224 no_request ->
225
:-(
?LOG_ERROR(#{msg => Msg, data => Data}),
226
:-(
keep_state_and_data;
227
228 no_reply ->
229
:-(
?LOG_ERROR(#{msg => Msg, data => Data}),
230
:-(
keep_state_and_data
231 end.
Line Hits Source