feat(acl): make mqtt over websocket work with the new config
This commit is contained in:
parent
707851c36f
commit
630b54f6ee
|
@ -2218,8 +2218,8 @@ example_common_websocket_options {
|
||||||
##
|
##
|
||||||
## @doc listeners.<name>.websocket.compress
|
## @doc listeners.<name>.websocket.compress
|
||||||
## ValueType: Boolean
|
## ValueType: Boolean
|
||||||
## Default: true
|
## Default: false
|
||||||
websocket.compress: true
|
websocket.compress: false
|
||||||
|
|
||||||
## The idle timeout for external WebSocket connections.
|
## The idle timeout for external WebSocket connections.
|
||||||
##
|
##
|
||||||
|
@ -2244,6 +2244,13 @@ example_common_websocket_options {
|
||||||
## Default: true
|
## Default: true
|
||||||
websocket.fail_if_no_subprotocol: true
|
websocket.fail_if_no_subprotocol: true
|
||||||
|
|
||||||
|
## Supported subprotocols
|
||||||
|
##
|
||||||
|
## @doc listeners.<name>.websocket.supported_subprotocols
|
||||||
|
## ValueType: String
|
||||||
|
## Default: mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5
|
||||||
|
websocket.supported_subprotocols: "mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5"
|
||||||
|
|
||||||
## Enable origin check in header for websocket connection
|
## Enable origin check in header for websocket connection
|
||||||
##
|
##
|
||||||
## @doc listeners.<name>.websocket.check_origin_enable
|
## @doc listeners.<name>.websocket.check_origin_enable
|
||||||
|
|
|
@ -243,7 +243,7 @@ init(Parent, Transport, RawSocket, Options) ->
|
||||||
exit_on_sock_error(Reason)
|
exit_on_sock_error(Reason)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
init_state(Transport, Socket, Options) ->
|
init_state(Transport, Socket, #{zone := Zone, listener := Listener} = Opts) ->
|
||||||
{ok, Peername} = Transport:ensure_ok_or_exit(peername, [Socket]),
|
{ok, Peername} = Transport:ensure_ok_or_exit(peername, [Socket]),
|
||||||
{ok, Sockname} = Transport:ensure_ok_or_exit(sockname, [Socket]),
|
{ok, Sockname} = Transport:ensure_ok_or_exit(sockname, [Socket]),
|
||||||
Peercert = Transport:ensure_ok_or_exit(peercert, [Socket]),
|
Peercert = Transport:ensure_ok_or_exit(peercert, [Socket]),
|
||||||
|
@ -253,8 +253,6 @@ init_state(Transport, Socket, Options) ->
|
||||||
peercert => Peercert,
|
peercert => Peercert,
|
||||||
conn_mod => ?MODULE
|
conn_mod => ?MODULE
|
||||||
},
|
},
|
||||||
Zone = maps:get(zone, Options),
|
|
||||||
Listener = maps:get(listener, Options),
|
|
||||||
Limiter = emqx_limiter:init(Zone, undefined, undefined, []),
|
Limiter = emqx_limiter:init(Zone, undefined, undefined, []),
|
||||||
FrameOpts = #{
|
FrameOpts = #{
|
||||||
strict_mode => emqx_config:get_listener_conf(Zone, Listener, [mqtt, strict_mode]),
|
strict_mode => emqx_config:get_listener_conf(Zone, Listener, [mqtt, strict_mode]),
|
||||||
|
@ -262,7 +260,7 @@ init_state(Transport, Socket, Options) ->
|
||||||
},
|
},
|
||||||
ParseState = emqx_frame:initial_parse_state(FrameOpts),
|
ParseState = emqx_frame:initial_parse_state(FrameOpts),
|
||||||
Serialize = emqx_frame:serialize_opts(),
|
Serialize = emqx_frame:serialize_opts(),
|
||||||
Channel = emqx_channel:init(ConnInfo, Options),
|
Channel = emqx_channel:init(ConnInfo, Opts),
|
||||||
GcState = case emqx_config:get_listener_conf(Zone, Listener, [force_gc]) of
|
GcState = case emqx_config:get_listener_conf(Zone, Listener, [force_gc]) of
|
||||||
#{enable := false} -> undefined;
|
#{enable := false} -> undefined;
|
||||||
GcPolicy -> emqx_gc:init(GcPolicy)
|
GcPolicy -> emqx_gc:init(GcPolicy)
|
||||||
|
@ -295,11 +293,9 @@ run_loop(Parent, State = #state{transport = Transport,
|
||||||
peername = Peername,
|
peername = Peername,
|
||||||
channel = Channel}) ->
|
channel = Channel}) ->
|
||||||
emqx_logger:set_metadata_peername(esockd:format(Peername)),
|
emqx_logger:set_metadata_peername(esockd:format(Peername)),
|
||||||
case emqx_config:get_listener_conf(emqx_channel:info(zone, Channel),
|
ShutdownPolicy = emqx_config:get_listener_conf(emqx_channel:info(zone, Channel),
|
||||||
emqx_channel:info(listener, Channel), [force_shutdown]) of
|
emqx_channel:info(listener, Channel), [force_shutdown]),
|
||||||
#{enable := false} -> ok;
|
emqx_misc:tune_heap_size(ShutdownPolicy),
|
||||||
ShutdownPolicy -> emqx_misc:tune_heap_size(ShutdownPolicy)
|
|
||||||
end,
|
|
||||||
case activate_socket(State) of
|
case activate_socket(State) of
|
||||||
{ok, NState} -> hibernate(Parent, NState);
|
{ok, NState} -> hibernate(Parent, NState);
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
|
@ -793,15 +789,11 @@ check_oom(State = #state{channel = Channel}) ->
|
||||||
ShutdownPolicy = emqx_config:get_listener_conf(emqx_channel:info(zone, Channel),
|
ShutdownPolicy = emqx_config:get_listener_conf(emqx_channel:info(zone, Channel),
|
||||||
emqx_channel:info(listener, Channel), [force_shutdown]),
|
emqx_channel:info(listener, Channel), [force_shutdown]),
|
||||||
?tp(debug, check_oom, #{policy => ShutdownPolicy}),
|
?tp(debug, check_oom, #{policy => ShutdownPolicy}),
|
||||||
case ShutdownPolicy of
|
case emqx_misc:check_oom(ShutdownPolicy) of
|
||||||
#{enable := false} -> ok;
|
{shutdown, Reason} ->
|
||||||
ShutdownPolicy ->
|
%% triggers terminate/2 callback immediately
|
||||||
case emqx_misc:check_oom(ShutdownPolicy) of
|
erlang:exit({shutdown, Reason});
|
||||||
{shutdown, Reason} ->
|
_ -> ok
|
||||||
%% triggers terminate/2 callback immediately
|
|
||||||
erlang:exit({shutdown, Reason});
|
|
||||||
_ -> ok
|
|
||||||
end
|
|
||||||
end,
|
end,
|
||||||
State.
|
State.
|
||||||
|
|
||||||
|
|
|
@ -74,13 +74,13 @@ do_start_listener(ZoneName, ListenerName, #{type := tcp, bind := ListenOn} = Opt
|
||||||
%% Start MQTT/WS listener
|
%% Start MQTT/WS listener
|
||||||
do_start_listener(ZoneName, ListenerName, #{type := ws, bind := ListenOn} = Opts) ->
|
do_start_listener(ZoneName, ListenerName, #{type := ws, bind := ListenOn} = Opts) ->
|
||||||
Id = listener_id(ZoneName, ListenerName),
|
Id = listener_id(ZoneName, ListenerName),
|
||||||
RanchOpts = ranch_opts(Opts),
|
RanchOpts = ranch_opts(ListenOn, Opts),
|
||||||
WsOpts = ws_opts(ZoneName, ListenerName, Opts),
|
WsOpts = ws_opts(ZoneName, ListenerName, Opts),
|
||||||
case is_ssl(Opts) of
|
case is_ssl(Opts) of
|
||||||
false ->
|
false ->
|
||||||
cowboy:start_clear(Id, with_port(ListenOn, RanchOpts), WsOpts);
|
cowboy:start_clear(Id, RanchOpts, WsOpts);
|
||||||
true ->
|
true ->
|
||||||
cowboy:start_tls(Id, with_port(ListenOn, RanchOpts), WsOpts)
|
cowboy:start_tls(Id, RanchOpts, WsOpts)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
esockd_opts(Opts0) ->
|
esockd_opts(Opts0) ->
|
||||||
|
@ -104,21 +104,22 @@ ws_opts(ZoneName, ListenerName, Opts) ->
|
||||||
ProxyProto = maps:get(proxy_protocol, Opts, false),
|
ProxyProto = maps:get(proxy_protocol, Opts, false),
|
||||||
#{env => #{dispatch => Dispatch}, proxy_header => ProxyProto}.
|
#{env => #{dispatch => Dispatch}, proxy_header => ProxyProto}.
|
||||||
|
|
||||||
ranch_opts(Opts) ->
|
ranch_opts(ListenOn, Opts) ->
|
||||||
NumAcceptors = maps:get(acceptors, Opts, 4),
|
NumAcceptors = maps:get(acceptors, Opts, 4),
|
||||||
MaxConnections = maps:get(max_connections, Opts, 1024),
|
MaxConnections = maps:get(max_connections, Opts, 1024),
|
||||||
|
SocketOpts = case is_ssl(Opts) of
|
||||||
|
true -> tcp_opts(Opts) ++ proplists:delete(handshake_timeout, ssl_opts(Opts));
|
||||||
|
false -> tcp_opts(Opts)
|
||||||
|
end,
|
||||||
#{num_acceptors => NumAcceptors,
|
#{num_acceptors => NumAcceptors,
|
||||||
max_connections => MaxConnections,
|
max_connections => MaxConnections,
|
||||||
handshake_timeout => maps:get(handshake_timeout, Opts, 15000),
|
handshake_timeout => maps:get(handshake_timeout, Opts, 15000),
|
||||||
socket_opts => case is_ssl(Opts) of
|
socket_opts => ip_port(ListenOn) ++ SocketOpts}.
|
||||||
true -> tcp_opts(Opts) ++ proplists:delete(handshake_timeout, ssl_opts(Opts));
|
|
||||||
false -> tcp_opts(Opts)
|
|
||||||
end}.
|
|
||||||
|
|
||||||
with_port(Port, Opts = #{socket_opts := SocketOption}) when is_integer(Port) ->
|
ip_port(Port) when is_integer(Port) ->
|
||||||
Opts#{socket_opts => [{port, Port}| SocketOption]};
|
[{port, Port}];
|
||||||
with_port({Addr, Port}, Opts = #{socket_opts := SocketOption}) ->
|
ip_port({Addr, Port}) ->
|
||||||
Opts#{socket_opts => [{ip, Addr}, {port, Port}| SocketOption]}.
|
[{ip, Addr}, {port, Port}].
|
||||||
|
|
||||||
esockd_access_rules(StrRules) ->
|
esockd_access_rules(StrRules) ->
|
||||||
Access = fun(S) ->
|
Access = fun(S) ->
|
||||||
|
|
|
@ -43,16 +43,17 @@ deep_get(ConfKeyPath, Map, Default) ->
|
||||||
{ok, Data} -> Data
|
{ok, Data} -> Data
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec deep_find(config_key_path(), map()) -> {ok, term()} | {not_found, config_key(), term()}.
|
-spec deep_find(config_key_path(), map()) ->
|
||||||
|
{ok, term()} | {not_found, config_key_path(), term()}.
|
||||||
deep_find([], Map) ->
|
deep_find([], Map) ->
|
||||||
{ok, Map};
|
{ok, Map};
|
||||||
deep_find([Key | KeyPath], Map) when is_map(Map) ->
|
deep_find([Key | KeyPath] = Path, Map) when is_map(Map) ->
|
||||||
case maps:find(Key, Map) of
|
case maps:find(Key, Map) of
|
||||||
{ok, SubMap} -> deep_find(KeyPath, SubMap);
|
{ok, SubMap} -> deep_find(KeyPath, SubMap);
|
||||||
error -> {not_found, Key, Map}
|
error -> {not_found, Path, Map}
|
||||||
end;
|
end;
|
||||||
deep_find([Key | _KeyPath], Data) ->
|
deep_find(_KeyPath, Data) ->
|
||||||
{not_found, Key, Data}.
|
{not_found, _KeyPath, Data}.
|
||||||
|
|
||||||
-spec deep_put(config_key_path(), map(), term()) -> map().
|
-spec deep_put(config_key_path(), map(), term()) -> map().
|
||||||
deep_put([], Map, Config) when is_map(Map) ->
|
deep_put([], Map, Config) when is_map(Map) ->
|
||||||
|
|
|
@ -364,11 +364,11 @@ fields("mqtt_ws_listener") ->
|
||||||
fields("ws_opts") ->
|
fields("ws_opts") ->
|
||||||
[ {"mqtt_path", t(string(), undefined, "/mqtt")}
|
[ {"mqtt_path", t(string(), undefined, "/mqtt")}
|
||||||
, {"mqtt_piggyback", t(union(single, multiple), undefined, multiple)}
|
, {"mqtt_piggyback", t(union(single, multiple), undefined, multiple)}
|
||||||
, {"compress", t(boolean())}
|
, {"compress", t(boolean(), undefined, false)}
|
||||||
, {"idle_timeout", t(duration(), undefined, "15s")}
|
, {"idle_timeout", t(duration(), undefined, "15s")}
|
||||||
, {"max_frame_size", maybe_infinity(integer())}
|
, {"max_frame_size", maybe_infinity(integer())}
|
||||||
, {"fail_if_no_subprotocol", t(boolean(), undefined, true)}
|
, {"fail_if_no_subprotocol", t(boolean(), undefined, true)}
|
||||||
, {"supported_subprotocols", t(string(), undefined,
|
, {"supported_subprotocols", t(comma_separated_list(), undefined,
|
||||||
"mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5")}
|
"mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5")}
|
||||||
, {"check_origin_enable", t(boolean(), undefined, false)}
|
, {"check_origin_enable", t(boolean(), undefined, false)}
|
||||||
, {"allow_origin_absence", t(boolean(), undefined, true)}
|
, {"allow_origin_absence", t(boolean(), undefined, true)}
|
||||||
|
@ -401,12 +401,12 @@ fields("ssl_opts") ->
|
||||||
|
|
||||||
fields("deflate_opts") ->
|
fields("deflate_opts") ->
|
||||||
[ {"level", t(union([none, default, best_compression, best_speed]))}
|
[ {"level", t(union([none, default, best_compression, best_speed]))}
|
||||||
, {"mem_level", t(range(1, 9))}
|
, {"mem_level", t(range(1, 9), undefined, 8)}
|
||||||
, {"strategy", t(union([default, filtered, huffman_only, rle]))}
|
, {"strategy", t(union([default, filtered, huffman_only, rle]))}
|
||||||
, {"server_context_takeover", t(union(takeover, no_takeover))}
|
, {"server_context_takeover", t(union(takeover, no_takeover))}
|
||||||
, {"client_context_takeover", t(union(takeover, no_takeover))}
|
, {"client_context_takeover", t(union(takeover, no_takeover))}
|
||||||
, {"server_max_window_bits", t(integer())}
|
, {"server_max_window_bits", t(range(8, 15), undefined, 15)}
|
||||||
, {"client_max_window_bits", t(integer())}
|
, {"client_max_window_bits", t(range(8, 15), undefined, 15)}
|
||||||
];
|
];
|
||||||
|
|
||||||
fields("module") ->
|
fields("module") ->
|
||||||
|
|
|
@ -174,21 +174,13 @@ call(WsPid, Req, Timeout) when is_pid(WsPid) ->
|
||||||
%% WebSocket callbacks
|
%% WebSocket callbacks
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
init(Req, Opts) ->
|
init(Req, #{zone := Zone, listener := Listener} = Opts) ->
|
||||||
%% WS Transport Idle Timeout
|
%% WS Transport Idle Timeout
|
||||||
IdleTimeout = proplists:get_value(idle_timeout, Opts, 7200000),
|
WsOpts = #{compress => get_ws_opts(Zone, Listener, compress),
|
||||||
DeflateOptions = maps:from_list(proplists:get_value(deflate_options, Opts, [])),
|
deflate_opts => get_ws_opts(Zone, Listener, deflate_opts),
|
||||||
MaxFrameSize = case proplists:get_value(max_frame_size, Opts, 0) of
|
max_frame_size => get_ws_opts(Zone, Listener, max_frame_size),
|
||||||
0 -> infinity;
|
idle_timeout => get_ws_opts(Zone, Listener, idle_timeout)
|
||||||
I -> I
|
|
||||||
end,
|
|
||||||
Compress = proplists:get_bool(compress, Opts),
|
|
||||||
WsOpts = #{compress => Compress,
|
|
||||||
deflate_opts => DeflateOptions,
|
|
||||||
max_frame_size => MaxFrameSize,
|
|
||||||
idle_timeout => IdleTimeout
|
|
||||||
},
|
},
|
||||||
|
|
||||||
case check_origin_header(Req, Opts) of
|
case check_origin_header(Req, Opts) of
|
||||||
{error, Message} ->
|
{error, Message} ->
|
||||||
?LOG(error, "Invalid Origin Header ~p~n", [Message]),
|
?LOG(error, "Invalid Origin Header ~p~n", [Message]),
|
||||||
|
@ -196,18 +188,17 @@ init(Req, Opts) ->
|
||||||
ok -> parse_sec_websocket_protocol(Req, Opts, WsOpts)
|
ok -> parse_sec_websocket_protocol(Req, Opts, WsOpts)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
parse_sec_websocket_protocol(Req, Opts, WsOpts) ->
|
parse_sec_websocket_protocol(Req, #{zone := Zone, listener := Listener} = Opts, WsOpts) ->
|
||||||
FailIfNoSubprotocol = proplists:get_value(fail_if_no_subprotocol, Opts),
|
|
||||||
case cowboy_req:parse_header(<<"sec-websocket-protocol">>, Req) of
|
case cowboy_req:parse_header(<<"sec-websocket-protocol">>, Req) of
|
||||||
undefined ->
|
undefined ->
|
||||||
case FailIfNoSubprotocol of
|
case get_ws_opts(Zone, Listener, fail_if_no_subprotocol) of
|
||||||
true ->
|
true ->
|
||||||
{ok, cowboy_req:reply(400, Req), WsOpts};
|
{ok, cowboy_req:reply(400, Req), WsOpts};
|
||||||
false ->
|
false ->
|
||||||
{cowboy_websocket, Req, [Req, Opts], WsOpts}
|
{cowboy_websocket, Req, [Req, Opts], WsOpts}
|
||||||
end;
|
end;
|
||||||
Subprotocols ->
|
Subprotocols ->
|
||||||
SupportedSubprotocols = proplists:get_value(supported_subprotocols, Opts),
|
SupportedSubprotocols = get_ws_opts(Zone, Listener, supported_subprotocols),
|
||||||
NSupportedSubprotocols = [list_to_binary(Subprotocol)
|
NSupportedSubprotocols = [list_to_binary(Subprotocol)
|
||||||
|| Subprotocol <- SupportedSubprotocols],
|
|| Subprotocol <- SupportedSubprotocols],
|
||||||
case pick_subprotocol(Subprotocols, NSupportedSubprotocols) of
|
case pick_subprotocol(Subprotocols, NSupportedSubprotocols) of
|
||||||
|
@ -231,31 +222,30 @@ pick_subprotocol([Subprotocol | Rest], SupportedSubprotocols) ->
|
||||||
pick_subprotocol(Rest, SupportedSubprotocols)
|
pick_subprotocol(Rest, SupportedSubprotocols)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
parse_header_fun_origin(Req, Opts) ->
|
parse_header_fun_origin(Req, #{zone := Zone, listener := Listener}) ->
|
||||||
case cowboy_req:header(<<"origin">>, Req) of
|
case cowboy_req:header(<<"origin">>, Req) of
|
||||||
undefined ->
|
undefined ->
|
||||||
case proplists:get_bool(allow_origin_absence, Opts) of
|
case get_ws_opts(Zone, Listener, allow_origin_absence) of
|
||||||
true -> ok;
|
true -> ok;
|
||||||
false -> {error, origin_header_cannot_be_absent}
|
false -> {error, origin_header_cannot_be_absent}
|
||||||
end;
|
end;
|
||||||
Value ->
|
Value ->
|
||||||
Origins = proplists:get_value(check_origins, Opts, []),
|
case lists:member(Value, get_ws_opts(Zone, Listener, check_origins)) of
|
||||||
case lists:member(Value, Origins) of
|
|
||||||
true -> ok;
|
true -> ok;
|
||||||
false -> {origin_not_allowed, Value}
|
false -> {origin_not_allowed, Value}
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
check_origin_header(Req, Opts) ->
|
check_origin_header(Req, #{zone := Zone, listener := Listener} = Opts) ->
|
||||||
case proplists:get_bool(check_origin_enable, Opts) of
|
case get_ws_opts(Zone, Listener, check_origin_enable) of
|
||||||
true -> parse_header_fun_origin(Req, Opts);
|
true -> parse_header_fun_origin(Req, Opts);
|
||||||
false -> ok
|
false -> ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
websocket_init([Req, Opts]) ->
|
websocket_init([Req, #{zone := Zone, listener := Listener} = Opts]) ->
|
||||||
{Peername, Peercert} =
|
{Peername, Peercert} =
|
||||||
case proplists:get_bool(proxy_protocol, Opts)
|
case emqx_config:get_listener_conf(Zone, Listener, [proxy_protocol]) andalso
|
||||||
andalso maps:get(proxy_header, Req) of
|
maps:get(proxy_header, Req) of
|
||||||
#{src_address := SrcAddr, src_port := SrcPort, ssl := SSL} ->
|
#{src_address := SrcAddr, src_port := SrcPort, ssl := SSL} ->
|
||||||
SourceName = {SrcAddr, SrcPort},
|
SourceName = {SrcAddr, SrcPort},
|
||||||
%% Notice: Only CN is available in Proxy Protocol V2 additional info
|
%% Notice: Only CN is available in Proxy Protocol V2 additional info
|
||||||
|
@ -266,7 +256,7 @@ websocket_init([Req, Opts]) ->
|
||||||
{SourceName, SourceSSL};
|
{SourceName, SourceSSL};
|
||||||
#{src_address := SrcAddr, src_port := SrcPort} ->
|
#{src_address := SrcAddr, src_port := SrcPort} ->
|
||||||
SourceName = {SrcAddr, SrcPort},
|
SourceName = {SrcAddr, SrcPort},
|
||||||
{SourceName , nossl};
|
{SourceName, nossl};
|
||||||
_ ->
|
_ ->
|
||||||
{get_peer(Req, Opts), cowboy_req:cert(Req)}
|
{get_peer(Req, Opts), cowboy_req:cert(Req)}
|
||||||
end,
|
end,
|
||||||
|
@ -288,22 +278,31 @@ websocket_init([Req, Opts]) ->
|
||||||
ws_cookie => WsCookie,
|
ws_cookie => WsCookie,
|
||||||
conn_mod => ?MODULE
|
conn_mod => ?MODULE
|
||||||
},
|
},
|
||||||
Zone = proplists:get_value(zone, Opts),
|
Limiter = emqx_limiter:init(Zone, undefined, undefined, []),
|
||||||
PubLimit = emqx_zone:publish_limit(Zone),
|
MQTTPiggyback = get_ws_opts(Zone, Listener, mqtt_piggyback),
|
||||||
BytesIn = proplists:get_value(rate_limit, Opts),
|
FrameOpts = #{
|
||||||
RateLimit = emqx_zone:ratelimit(Zone),
|
strict_mode => emqx_config:get_listener_conf(Zone, Listener, [mqtt, strict_mode]),
|
||||||
Limiter = emqx_limiter:init(Zone, PubLimit, BytesIn, RateLimit),
|
max_size => emqx_config:get_listener_conf(Zone, Listener, [mqtt, max_packet_size])
|
||||||
MQTTPiggyback = proplists:get_value(mqtt_piggyback, Opts, multiple),
|
},
|
||||||
FrameOpts = emqx_zone:mqtt_frame_options(Zone),
|
|
||||||
ParseState = emqx_frame:initial_parse_state(FrameOpts),
|
ParseState = emqx_frame:initial_parse_state(FrameOpts),
|
||||||
Serialize = emqx_frame:serialize_opts(),
|
Serialize = emqx_frame:serialize_opts(),
|
||||||
Channel = emqx_channel:init(ConnInfo, Opts),
|
Channel = emqx_channel:init(ConnInfo, Opts),
|
||||||
GcState = emqx_zone:init_gc_state(Zone),
|
GcState = case emqx_config:get_listener_conf(Zone, Listener, [force_gc]) of
|
||||||
StatsTimer = emqx_zone:stats_timer(Zone),
|
#{enable := false} -> undefined;
|
||||||
|
GcPolicy -> emqx_gc:init(GcPolicy)
|
||||||
|
end,
|
||||||
|
StatsTimer = case emqx_config:get_listener_conf(Zone, Listener, [stats, enable]) of
|
||||||
|
true -> undefined;
|
||||||
|
false -> disabled
|
||||||
|
end,
|
||||||
%% MQTT Idle Timeout
|
%% MQTT Idle Timeout
|
||||||
IdleTimeout = emqx_zone:idle_timeout(Zone),
|
IdleTimeout = emqx_channel:get_mqtt_conf(Zone, Listener, idle_timeout),
|
||||||
IdleTimer = start_timer(IdleTimeout, idle_timeout),
|
IdleTimer = start_timer(IdleTimeout, idle_timeout),
|
||||||
emqx_misc:tune_heap_size(emqx_zone:oom_policy(Zone)),
|
case emqx_config:get_listener_conf(emqx_channel:info(zone, Channel),
|
||||||
|
emqx_channel:info(listener, Channel), [force_shutdown]) of
|
||||||
|
#{enable := false} -> ok;
|
||||||
|
ShutdownPolicy -> emqx_misc:tune_heap_size(ShutdownPolicy)
|
||||||
|
end,
|
||||||
emqx_logger:set_metadata_peername(esockd:format(Peername)),
|
emqx_logger:set_metadata_peername(esockd:format(Peername)),
|
||||||
{ok, #state{peername = Peername,
|
{ok, #state{peername = Peername,
|
||||||
sockname = Sockname,
|
sockname = Sockname,
|
||||||
|
@ -317,7 +316,9 @@ websocket_init([Req, Opts]) ->
|
||||||
postponed = [],
|
postponed = [],
|
||||||
stats_timer = StatsTimer,
|
stats_timer = StatsTimer,
|
||||||
idle_timeout = IdleTimeout,
|
idle_timeout = IdleTimeout,
|
||||||
idle_timer = IdleTimer
|
idle_timer = IdleTimer,
|
||||||
|
zone = Zone,
|
||||||
|
listener = Listener
|
||||||
}, hibernate}.
|
}, hibernate}.
|
||||||
|
|
||||||
websocket_handle({binary, Data}, State) when is_list(Data) ->
|
websocket_handle({binary, Data}, State) when is_list(Data) ->
|
||||||
|
@ -517,11 +518,16 @@ run_gc(Stats, State = #state{gc_state = GcSt}) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
check_oom(State = #state{channel = Channel}) ->
|
check_oom(State = #state{channel = Channel}) ->
|
||||||
OomPolicy = emqx_zone:oom_policy(emqx_channel:info(zone, Channel)),
|
ShutdownPolicy = emqx_config:get_listener_conf(emqx_channel:info(zone, Channel),
|
||||||
case ?ENABLED(OomPolicy) andalso emqx_misc:check_oom(OomPolicy) of
|
emqx_channel:info(listener, Channel), [force_shutdown]),
|
||||||
Shutdown = {shutdown, _Reason} ->
|
case ShutdownPolicy of
|
||||||
postpone(Shutdown, State);
|
#{enable := false} -> ok;
|
||||||
_Other -> State
|
#{enable := true} ->
|
||||||
|
case emqx_misc:check_oom(ShutdownPolicy) of
|
||||||
|
Shutdown = {shutdown, _Reason} ->
|
||||||
|
postpone(Shutdown, State);
|
||||||
|
_Other -> State
|
||||||
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
@ -741,9 +747,10 @@ classify([Event|More], Packets, Cmds, Events) ->
|
||||||
|
|
||||||
trigger(Event) -> erlang:send(self(), Event).
|
trigger(Event) -> erlang:send(self(), Event).
|
||||||
|
|
||||||
get_peer(Req, Opts) ->
|
get_peer(Req, #{zone := Zone, listener := Listener}) ->
|
||||||
{PeerAddr, PeerPort} = cowboy_req:peer(Req),
|
{PeerAddr, PeerPort} = cowboy_req:peer(Req),
|
||||||
AddrHeader = cowboy_req:header(proplists:get_value(proxy_address_header, Opts), Req, <<>>),
|
AddrHeader = cowboy_req:header(
|
||||||
|
get_ws_opts(Zone, Listener, proxy_address_header), Req, <<>>),
|
||||||
ClientAddr = case string:tokens(binary_to_list(AddrHeader), ", ") of
|
ClientAddr = case string:tokens(binary_to_list(AddrHeader), ", ") of
|
||||||
[] ->
|
[] ->
|
||||||
undefined;
|
undefined;
|
||||||
|
@ -756,7 +763,8 @@ get_peer(Req, Opts) ->
|
||||||
_ ->
|
_ ->
|
||||||
PeerAddr
|
PeerAddr
|
||||||
end,
|
end,
|
||||||
PortHeader = cowboy_req:header(proplists:get_value(proxy_port_header, Opts), Req, <<>>),
|
PortHeader = cowboy_req:header(
|
||||||
|
get_ws_opts(Zone, Listener, proxy_port_header), Req, <<>>),
|
||||||
ClientPort = case string:tokens(binary_to_list(PortHeader), ", ") of
|
ClientPort = case string:tokens(binary_to_list(PortHeader), ", ") of
|
||||||
[] ->
|
[] ->
|
||||||
undefined;
|
undefined;
|
||||||
|
@ -777,3 +785,5 @@ set_field(Name, Value, State) ->
|
||||||
Pos = emqx_misc:index_of(Name, record_info(fields, state)),
|
Pos = emqx_misc:index_of(Name, record_info(fields, state)),
|
||||||
setelement(Pos+1, State, Value).
|
setelement(Pos+1, State, Value).
|
||||||
|
|
||||||
|
get_ws_opts(Zone, Listener, Key) ->
|
||||||
|
emqx_config:get_listener_conf(Zone, Listener, [websocket, Key]).
|
||||||
|
|
Loading…
Reference in New Issue