• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

emqx / esockd / 356

15 Dec 2023 02:41PM UTC coverage: 72.414%. First build
356

Pull #183

github

web-flow
Merge 8bfe0c31c into 5cb22a8b1
Pull Request #183: feat(listener): support changing options on the fly

191 of 213 new or added lines in 10 files covered. (89.67%)

840 of 1160 relevant lines covered (72.41%)

62.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.74
/src/esockd_server.erl
1
%%--------------------------------------------------------------------
2
%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved.
3
%%
4
%% Licensed under the Apache License, Version 2.0 (the "License");
5
%% you may not use this file except in compliance with the License.
6
%% You may obtain a copy of the License at
7
%%
8
%%     http://www.apache.org/licenses/LICENSE-2.0
9
%%
10
%% Unless required by applicable law or agreed to in writing, software
11
%% distributed under the License is distributed on an "AS IS" BASIS,
12
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
%% See the License for the specific language governing permissions and
14
%% limitations under the License.
15
%%--------------------------------------------------------------------
16

17
-module(esockd_server).
18

19
-behaviour(gen_server).
20

21
-export([start_link/0, stop/0]).
22

23
%% stats API
24
-export([ stats_fun/2
25
        , init_stats/2
26
        , get_stats/1
27
        , inc_stats/3
28
        , dec_stats/3
29
        , del_stats/1
30
        , ensure_stats/1
31
        ]).
32

33
%% listener properties API
34
-export([ get_listener_prop/2
35
        , list_listener_props/1
36
        , set_listener_prop/3
37
        , erase_listener_props/1
38
        ]).
39

40
%% gen_server callbacks
41
-export([ init/1
42
        , handle_call/3
43
        , handle_cast/2
44
        , handle_info/2
45
        , terminate/2
46
        , code_change/3
47
        ]).
48

49
-record(state, {
50
    listener_props :: #{esockd:listener_ref() => #{_Name => _Value}}
51
}).
52

53
-define(SERVER, ?MODULE).
54
-define(STATS_TAB, esockd_stats).
55

56
%%--------------------------------------------------------------------
57
%% API
58
%%--------------------------------------------------------------------
59

60
-spec(start_link() -> {ok, pid()} | ignore | {error, term()}).
61
start_link() ->
62
    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
6✔
63

64
-spec(stop() -> ok).
65
stop() -> gen_server:stop(?SERVER).
2✔
66

67
-spec(stats_fun({atom(), esockd:listen_on()}, atom()) -> fun()).
68
stats_fun({Protocol, ListenOn}, Metric) ->
69
    init_stats({Protocol, ListenOn}, Metric),
1✔
70
    fun({inc, Num}) -> esockd_server:inc_stats({Protocol, ListenOn}, Metric, Num);
1✔
71
       ({dec, Num}) -> esockd_server:dec_stats({Protocol, ListenOn}, Metric, Num)
1✔
72
    end.
73

74
-spec(init_stats({atom(), esockd:listen_on()}, atom()) -> ok).
75
init_stats({Protocol, ListenOn}, Metric) ->
76
    gen_server:call(?SERVER, {init, {Protocol, ListenOn}, Metric}).
118✔
77

78
-spec(get_stats({atom(), esockd:listen_on()}) -> [{atom(), non_neg_integer()}]).
79
get_stats({Protocol, ListenOn}) ->
80
    [{Metric, Val} || [Metric, Val]
6✔
81
                      <- ets:match(?STATS_TAB, {{{Protocol, ListenOn}, '$1'}, '$2'})].
6✔
82

83
-spec(inc_stats({atom(), esockd:listen_on()}, atom(), pos_integer()) -> any()).
84
inc_stats({Protocol, ListenOn}, Metric, Num) when is_integer(Num) ->
85
    update_counter({{Protocol, ListenOn}, Metric}, Num).
62✔
86

87
-spec(dec_stats({atom(), esockd:listen_on()}, atom(), pos_integer()) -> any()).
88
dec_stats({Protocol, ListenOn}, Metric, Num) when is_integer(Num) ->
89
    update_counter({{Protocol, ListenOn}, Metric}, -Num).
2✔
90

91
update_counter(Key, Num) ->
92
    ets:update_counter(?STATS_TAB, Key, {2, Num}).
64✔
93

94
-spec(del_stats({atom(), esockd:listen_on()}) -> ok).
95
del_stats({Protocol, ListenOn}) ->
96
    gen_server:cast(?SERVER, {del, {Protocol, ListenOn}}).
59✔
97

98
-spec ensure_stats({atom(), esockd:listen_on()}) -> ok.
99
ensure_stats(StatsKey) ->
100
    ok = ?MODULE:init_stats(StatsKey, accepted),
58✔
101
    ok = ?MODULE:init_stats(StatsKey, closed_overloaded).
58✔
102

103
-spec get_listener_prop(esockd:listener_ref(), _Name) -> _Value | undefined.
104
get_listener_prop(ListenerRef = {_Proto, _ListenOn}, Name) ->
105
    gen_server:call(?SERVER, {get_listener_prop, ListenerRef, Name}, infinity).
601✔
106

107
-spec list_listener_props(esockd:listener_ref()) -> [{_Name, _Value}].
108
list_listener_props(ListenerRef = {_Proto, _ListenOn}) ->
NEW
109
    gen_server:call(?SERVER, {list_listener_props, ListenerRef}, infinity).
×
110

111
-spec set_listener_prop(esockd:listener_ref(), _Name, _Value) -> _ValueWas.
112
set_listener_prop(ListenerRef = {_Proto, _ListenOn}, Name, Value) ->
113
    gen_server:call(?SERVER, {set_listener_prop, ListenerRef, Name, Value}, infinity).    
304✔
114

115
-spec erase_listener_props(esockd:listener_ref()) -> [{_Name, _ValueWas}].
116
erase_listener_props(ListenerRef = {_Proto, _ListenOn}) ->
117
    gen_server:call(?SERVER, {erase_listener_props, ListenerRef}, infinity).
61✔
118

119
%%--------------------------------------------------------------------
120
%% gen_server callbacks
121
%%--------------------------------------------------------------------
122

123
init([]) ->
124
    _ = ets:new(?STATS_TAB, [public, set, named_table,
6✔
125
                             {write_concurrency, true}]),
126
    {ok, #state{listener_props = #{}}}.
6✔
127

128
handle_call({init, {Protocol, ListenOn}, Metric}, _From, State) ->
129
    true = ets:insert(?STATS_TAB, {{{Protocol, ListenOn}, Metric}, 0}),
118✔
130
    {reply, ok, State, hibernate};
118✔
131

132
handle_call({get_listener_prop, ListenerRef, Name}, _From,
133
            State = #state{listener_props = LProps}) ->
134
    {reply, lprops_get(ListenerRef, Name, LProps), State};
601✔
135

136
handle_call({set_listener_prop, ListenerRef, Name, NValue}, _From,
137
            State = #state{listener_props = LProps}) ->
138
    Value = lprops_get(ListenerRef, Name, LProps),
304✔
139
    NLProps = lprops_set(ListenerRef, Name, NValue, LProps),
304✔
140
    {reply, Value, State#state{listener_props = NLProps}};
304✔
141

142
handle_call({list_listener_props, ListenerRef}, _From,
143
            State = #state{listener_props = LProps}) ->
NEW
144
    {reply, lprops_list(ListenerRef, LProps), State};
×
145

146
handle_call({erase_listener_props, ListenerRef}, _From,
147
            State = #state{listener_props = LProps}) ->
148
    Props = lprops_list(ListenerRef, LProps),
61✔
149
    {reply, Props, State#state{listener_props = lprops_erase(ListenerRef, LProps)}};
61✔
150

151
handle_call(Req, _From, State) ->
152
    error_logger:error_msg("[~s] Unexpected call: ~p", [?MODULE, Req]),
1✔
153
    {reply, ignore, State}.
1✔
154

155
handle_cast({del, {Protocol, ListenOn}}, State) ->
156
    ets:match_delete(?STATS_TAB, {{{Protocol, ListenOn}, '_'}, '_'}),
59✔
157
    {noreply, State, hibernate};
59✔
158

159
handle_cast(Msg, State) ->
160
    error_logger:error_msg("[~s] Unexpected cast: ~p", [?MODULE, Msg]),
1✔
161
    {noreply, State}.
1✔
162

163
handle_info(Info, State) ->
164
    error_logger:error_msg("[~s] Unexpected info: ~p", [?MODULE, Info]),
1✔
165
    {noreply, State}.
1✔
166

167
terminate(_Reason, _State) ->
168
    ok.
2✔
169

170
code_change(_OldVsn, State, _Extra) ->
171
    {ok, State}.
1✔
172

173
%%
174

175
lprops_get(ListenerRef, Name, LProps) ->
176
    case LProps of
905✔
177
        #{ListenerRef := Props} ->
178
            maps:get(Name, Props, undefined);
850✔
179
        #{} ->
180
            undefined
55✔
181
    end.
182

183
lprops_set(ListenerRef, Name, Value, LProps) ->
184
    Props = maps:get(ListenerRef, LProps, #{}),
304✔
185
    LProps#{ListenerRef => Props#{Name => Value}}.
304✔
186

187
lprops_list(ListenerRef, LProps) ->
188
    Props = maps:get(ListenerRef, LProps, #{}),
61✔
189
    maps:to_list(Props).
61✔
190

191
lprops_erase(ListenerRef, LProps) ->
192
    maps:remove(ListenerRef, LProps).
61✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc