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

emqx / esockd / 354

14 Dec 2023 12:54PM UTC coverage: 71.491%. First build
354

Pull #183

github

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

170 of 192 new or added lines in 10 files covered. (88.54%)

820 of 1147 relevant lines covered (71.49%)

60.53 hits per line

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

90.48
/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

51
-define(SERVER, ?MODULE).
52
-define(STATS_TAB, esockd_stats).
53

54
%%--------------------------------------------------------------------
55
%% API
56
%%--------------------------------------------------------------------
57

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

62
-spec(stop() -> ok).
63
stop() -> gen_server:stop(?SERVER).
2✔
64

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

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

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

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

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

89
update_counter(Key, Num) ->
90
    ets:update_counter(?STATS_TAB, Key, {2, Num}).
61✔
91

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

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

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

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

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

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

117
%%--------------------------------------------------------------------
118
%% gen_server callbacks
119
%%--------------------------------------------------------------------
120

121
-define(LPROP(LREF, NAME), {LREF, prop, NAME}).
122

123
init([]) ->
124
    _ = ets:new(?STATS_TAB, [public, set, named_table,
6✔
125
                             {write_concurrency, true}]),
126
    {ok, #state{}}.
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, State) ->
133
    Value = erlang:get(?LPROP(ListenerRef, Name)),
601✔
134
    {reply, Value, State};
601✔
135

136
handle_call({set_listener_prop, ListenerRef, Name, Value}, _From, State) ->
137
    ValueWas = erlang:put(?LPROP(ListenerRef, Name), Value),
304✔
138
    {reply, ValueWas, State};
304✔
139

140
handle_call({list_listener_props, ListenerRef}, _From, State) ->
NEW
141
    Props = [{Name, Value} || {?LPROP(Ref, Name), Value} <- erlang:get()
×
NEW
142
                            , Ref == ListenerRef
×
143
                            ],
NEW
144
    {reply, Props, State};
×
145

146
handle_call({erase_listener_props, ListenerRef}, _From, State) ->
147
    Props = [{Name, erlang:erase(K)} || {K = ?LPROP(Ref, Name), _} <- erlang:get()
61✔
148
                                      , Ref == ListenerRef
382✔
149
                                      ],
150
    {reply, Props, State};
61✔
151

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

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

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

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

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

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

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