network-infra-utility 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +21 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +70 -0
- data/Rakefile +1 -1
- data/bin/dns-query +834 -0
- data/bin/geo-doc +135 -0
- data/bin/geo-get +1 -1
- data/document/ASNum/346/250/241/345/235/227/345/212/237/350/203/275/350/257/264/346/230/216.md +242 -0
- data/document/DNSQuery/345/267/245/345/205/267/344/275/277/347/224/250/346/226/271/346/263/225.md +248 -0
- data/document/Geo/345/221/275/344/273/244/345/267/245/345/205/267/344/275/277/347/224/250/346/226/271/346/263/225.md +441 -0
- data/document/IP/346/250/241/345/235/227/345/212/237/350/203/275/350/257/264/346/230/216.md +297 -0
- data/document/MAC/346/250/241/345/235/227/345/212/237/350/203/275/350/257/264/346/230/216.md +296 -0
- data/document/SSH/350/277/236/346/216/245/345/256/242/346/210/267/347/253/257/344/275/277/347/224/250/346/226/271/346/263/225.md +764 -0
- data/network-infra-utility.gemspec +4 -2
- data/network.rb +3 -1
- data/service/geodb/geodb.rb +278 -1
- data/service/ssh/README.md +942 -0
- data/service/ssh/bin/ssh-client +198 -0
- data/service/ssh/design/SSH/350/277/236/346/216/245/345/256/242/346/210/267/347/253/257/345/212/237/350/203/275/351/234/200/346/261/202/346/226/207/346/241/243.md +292 -0
- data/service/ssh/design/SSH/350/277/236/346/216/245/345/256/242/346/210/267/347/253/257/350/257/246/347/273/206/350/256/276/350/256/241/346/226/207/346/241/243.md +1521 -0
- data/service/ssh/design/SSH/350/277/236/346/216/245/345/256/242/346/210/267/347/253/257/350/275/257/344/273/266/350/256/276/350/256/241/346/226/207/346/241/243.md +2493 -0
- data/service/ssh/ext/ssh_core/bin/ssh_core.cmd +28 -0
- data/service/ssh/ext/ssh_core/config/sys.config +0 -0
- data/service/ssh/ext/ssh_core/config/vm.args +0 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/CHECKSUM +1 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/LICENSE +21 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/README.md +696 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/VERSION +1 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/contents.tar.gz +0 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/metadata.config +15 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/rebar.config +17 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/rebar.lock +1 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx.app.src +10 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx.erl +506 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_config.erl +393 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_config.hrl +18 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_consult.erl +81 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_decoder.erl +1909 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_encoder.erl +116 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_parser.erl +1214 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_to_json.erl +408 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_to_term.erl +389 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_verify.erl +121 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx.erl +506 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_config.erl +393 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_config.hrl +18 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_consult.erl +81 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_decoder.erl +1909 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_encoder.erl +116 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_parser.erl +1214 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_to_json.erl +408 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_to_term.erl +389 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_verify.erl +121 -0
- data/service/ssh/ext/ssh_core/rebar.config +24 -0
- data/service/ssh/ext/ssh_core/rebar.lock +1 -0
- data/service/ssh/ext/ssh_core/src/ssh_auth_engine.erl +156 -0
- data/service/ssh/ext/ssh_core/src/ssh_channel_stm.erl +232 -0
- data/service/ssh/ext/ssh_core/src/ssh_codec.erl +83 -0
- data/service/ssh/ext/ssh_core/src/ssh_conn_sup.erl +48 -0
- data/service/ssh/ext/ssh_core/src/ssh_conn_worker.erl +535 -0
- data/service/ssh/ext/ssh_core/src/ssh_core.app.src +36 -0
- data/service/ssh/ext/ssh_core/src/ssh_core_app.erl +11 -0
- data/service/ssh/ext/ssh_core/src/ssh_core_sup.erl +46 -0
- data/service/ssh/ext/ssh_core/src/ssh_infra_sup.erl +104 -0
- data/service/ssh/ext/ssh_core/src/ssh_ipc.hrl +80 -0
- data/service/ssh/ext/ssh_core/src/ssh_ipc_coalesce.erl +94 -0
- data/service/ssh/ext/ssh_core/src/ssh_ipc_gateway.erl +467 -0
- data/service/ssh/ext/ssh_core/src/ssh_ipc_proto.erl +95 -0
- data/service/ssh/ext/ssh_core/src/ssh_jump_chain.erl +101 -0
- data/service/ssh/ext/ssh_core/src/ssh_keepalive_mgr.erl +222 -0
- data/service/ssh/ext/ssh_core/src/ssh_known_hosts_proxy.erl +67 -0
- data/service/ssh/ext/ssh_core/src/ssh_port_fwd.erl +225 -0
- data/service/ssh/ext/ssh_core/src/ssh_sftp_session.erl +250 -0
- data/service/ssh/ext/ssh_core/src/ssh_sftp_sup.erl +62 -0
- data/service/ssh/ext/ssh_core_rs/Cargo.lock +2345 -0
- data/service/ssh/ext/ssh_core_rs/Cargo.toml +30 -0
- data/service/ssh/ext/ssh_core_rs/bin/ssh_core_rs +34 -0
- data/service/ssh/ext/ssh_core_rs/bin/ssh_core_rs.cmd +40 -0
- data/service/ssh/ext/ssh_core_rs/src/channel.rs +296 -0
- data/service/ssh/ext/ssh_core_rs/src/coalesce.rs +143 -0
- data/service/ssh/ext/ssh_core_rs/src/codec.rs +71 -0
- data/service/ssh/ext/ssh_core_rs/src/conn.rs +628 -0
- data/service/ssh/ext/ssh_core_rs/src/gateway.rs +389 -0
- data/service/ssh/ext/ssh_core_rs/src/handler.rs +293 -0
- data/service/ssh/ext/ssh_core_rs/src/keepalive.rs +194 -0
- data/service/ssh/ext/ssh_core_rs/src/main.rs +351 -0
- data/service/ssh/ext/ssh_core_rs/src/portfwd.rs +378 -0
- data/service/ssh/ext/ssh_core_rs/src/proto.rs +198 -0
- data/service/ssh/ext/ssh_core_rs/src/sftp.rs +294 -0
- data/service/ssh/lib/network_infra_utility/ssh/automation/macro_engine.rb +213 -0
- data/service/ssh/lib/network_infra_utility/ssh/client.rb +257 -0
- data/service/ssh/lib/network_infra_utility/ssh/config/schema.rb +90 -0
- data/service/ssh/lib/network_infra_utility/ssh/config/settings.rb +103 -0
- data/service/ssh/lib/network_infra_utility/ssh/config/store.rb +90 -0
- data/service/ssh/lib/network_infra_utility/ssh/ipc/coalesce.rb +83 -0
- data/service/ssh/lib/network_infra_utility/ssh/ipc/errors.rb +36 -0
- data/service/ssh/lib/network_infra_utility/ssh/ipc/router.rb +212 -0
- data/service/ssh/lib/network_infra_utility/ssh/ipc/transport.rb +81 -0
- data/service/ssh/lib/network_infra_utility/ssh/security/host_key.rb +211 -0
- data/service/ssh/lib/network_infra_utility/ssh/security/vault.rb +211 -0
- data/service/ssh/lib/network_infra_utility/ssh/session/history.rb +56 -0
- data/service/ssh/lib/network_infra_utility/ssh/session/manager.rb +92 -0
- data/service/ssh/lib/network_infra_utility/ssh/session/session.rb +109 -0
- data/service/ssh/lib/network_infra_utility/ssh/session/tree.rb +95 -0
- data/service/ssh/lib/network_infra_utility/ssh/terminal/ansi_parser.rb +435 -0
- data/service/ssh/lib/network_infra_utility/ssh/terminal/buffer.rb +78 -0
- data/service/ssh/lib/network_infra_utility/ssh/terminal/emulator.rb +159 -0
- data/service/ssh/lib/network_infra_utility/ssh/terminal/logger.rb +195 -0
- data/service/ssh/lib/network_infra_utility/ssh/terminal/screen.rb +212 -0
- data/service/ssh/lib/network_infra_utility/ssh/terminal/theme.rb +127 -0
- data/service/ssh/lib/network_infra_utility/ssh/version.rb +7 -0
- data/service/ssh/lib/network_infra_utility/ssh.rb +44 -0
- data/support/basic/as_num.rb +221 -0
- data/support/basic/mac_address.rb +281 -0
- data/version.rb +1 -1
- metadata +138 -1
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
-module(ssh_infra_sup).
|
|
2
|
+
-behaviour(supervisor).
|
|
3
|
+
|
|
4
|
+
-export([start_link/0, generate_endpoint_file/0]).
|
|
5
|
+
-export([init/1]).
|
|
6
|
+
|
|
7
|
+
-include("ssh_ipc.hrl").
|
|
8
|
+
|
|
9
|
+
%% @doc Start the infrastructure supervisor.
|
|
10
|
+
%% Supervises: ssh_known_hosts_proxy, ssh_keepalive_mgr, ssh_ipc_coalesce, ssh_ipc_gateway
|
|
11
|
+
start_link() ->
|
|
12
|
+
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
|
13
|
+
|
|
14
|
+
%% @doc Return the path to the IPC endpoint file for this VM instance.
|
|
15
|
+
%% The file is written by generate_endpoint_and_token/0 during init.
|
|
16
|
+
generate_endpoint_file() ->
|
|
17
|
+
Tmp = case os:getenv("TMPDIR") of
|
|
18
|
+
false ->
|
|
19
|
+
case os:type() of
|
|
20
|
+
{win32, _} -> os:getenv("TEMP");
|
|
21
|
+
_ -> "/tmp"
|
|
22
|
+
end;
|
|
23
|
+
Dir -> Dir
|
|
24
|
+
end,
|
|
25
|
+
UID = case os:type() of
|
|
26
|
+
{win32, _} -> os:getenv("USERNAME");
|
|
27
|
+
_ -> integer_to_list(os:getuid())
|
|
28
|
+
end,
|
|
29
|
+
filename:join(Tmp, "ssh_core_" ++ UID ++ ".endpoint").
|
|
30
|
+
|
|
31
|
+
%% @doc one_for_one, intensity 5/60s.
|
|
32
|
+
%% Keeping infra processes separate from conn_sup so a single connection
|
|
33
|
+
%% crash never cascades to gateway/keepalive/known_hosts.
|
|
34
|
+
init([]) ->
|
|
35
|
+
%% Create ETS indices before any workers start
|
|
36
|
+
ets:new(ch_index, [named_table, public, set]),
|
|
37
|
+
ets:new(sftp_index, [named_table, public, set]),
|
|
38
|
+
ets:new(portfwd_index, [named_table, public, set]),
|
|
39
|
+
|
|
40
|
+
{ok, EndpointToken} = generate_endpoint_and_token(),
|
|
41
|
+
Children = [
|
|
42
|
+
#{
|
|
43
|
+
id => ssh_known_hosts_proxy,
|
|
44
|
+
start => {ssh_known_hosts_proxy, start_link, []},
|
|
45
|
+
restart => permanent,
|
|
46
|
+
type => worker
|
|
47
|
+
},
|
|
48
|
+
#{
|
|
49
|
+
id => ssh_ipc_coalesce,
|
|
50
|
+
start => {ssh_ipc_coalesce, start_link, []},
|
|
51
|
+
restart => permanent,
|
|
52
|
+
type => worker
|
|
53
|
+
},
|
|
54
|
+
#{
|
|
55
|
+
id => ssh_keepalive_mgr,
|
|
56
|
+
start => {ssh_keepalive_mgr, start_link, [?KEEPALIVE_DEFAULT_INTERVAL_MS]},
|
|
57
|
+
restart => permanent,
|
|
58
|
+
type => worker
|
|
59
|
+
},
|
|
60
|
+
#{
|
|
61
|
+
id => ssh_ipc_gateway,
|
|
62
|
+
start => {ssh_ipc_gateway, start_link, [EndpointToken]},
|
|
63
|
+
restart => permanent,
|
|
64
|
+
type => worker
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
SupFlags = #{
|
|
68
|
+
strategy => one_for_one,
|
|
69
|
+
intensity => 5,
|
|
70
|
+
period => 60
|
|
71
|
+
},
|
|
72
|
+
{ok, {SupFlags, Children}}.
|
|
73
|
+
|
|
74
|
+
%% @doc Generate IPC auth token, write endpoint file placeholder.
|
|
75
|
+
%% Actual port is assigned later by ssh_ipc_gateway:start_listener().
|
|
76
|
+
generate_endpoint_and_token() ->
|
|
77
|
+
Token = ssh_codec:encode_b64(crypto:strong_rand_bytes(32)),
|
|
78
|
+
TmpFile = generate_endpoint_file(),
|
|
79
|
+
%% Write placeholder: endpoint line will be rewritten by gateway
|
|
80
|
+
ok = file:write_file(TmpFile, <<"\n", Token/binary>>),
|
|
81
|
+
set_file_perms_600(TmpFile),
|
|
82
|
+
{ok, #{token => Token, file => TmpFile}}.
|
|
83
|
+
|
|
84
|
+
build_endpoint() ->
|
|
85
|
+
case os:type() of
|
|
86
|
+
{win32, _} ->
|
|
87
|
+
%% On Windows, write a placeholder; actual port is assigned
|
|
88
|
+
%% by ssh_ipc_gateway:start_listener() after gen_tcp:listen(0).
|
|
89
|
+
{ok, <<"tcp://127.0.0.1:0">>};
|
|
90
|
+
_ ->
|
|
91
|
+
%% Unix/macOS: Unix socket
|
|
92
|
+
Path = <<"/tmp/ssh_core_", (integer_to_binary(os:getpid()))/binary, ".sock">>,
|
|
93
|
+
{ok, <<"unix:", Path/binary>>}
|
|
94
|
+
end.
|
|
95
|
+
|
|
96
|
+
select_free_port() ->
|
|
97
|
+
{ok, Sock} = gen_tcp:listen(0, [{ip, {127, 0, 0, 1}}]),
|
|
98
|
+
{ok, Port} = inet:port(Sock),
|
|
99
|
+
gen_tcp:close(Sock),
|
|
100
|
+
{ok, Port}.
|
|
101
|
+
|
|
102
|
+
set_file_perms_600(File) ->
|
|
103
|
+
_ = file:change_mode(File, 8#600),
|
|
104
|
+
ok.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
%% ssh_ipc.hrl — Shared records, macros, and types for IPC and core modules.
|
|
2
|
+
|
|
3
|
+
%% ---------- Protocol ----------
|
|
4
|
+
-define(PROTOCOL_VER, <<"1.0">>).
|
|
5
|
+
-define(MSG_DELIMITER, <<"\n">>).
|
|
6
|
+
-define(MAX_MSG_SIZE, 2 * 1024 * 1024). % 2 MB
|
|
7
|
+
|
|
8
|
+
%% ---------- Timeouts (ms) ----------
|
|
9
|
+
-define(CONNECT_TIMEOUT_MS, 60_000).
|
|
10
|
+
-define(RPC_DEFAULT_TIMEOUT_MS, 30_000).
|
|
11
|
+
-define(HELLO_ID, 1).
|
|
12
|
+
-define(HOSTKEY_RESOLVE_TIMEOUT_MS, 30_000).
|
|
13
|
+
|
|
14
|
+
%% ---------- Coalesce ----------
|
|
15
|
+
-define(COALESCE_TICK_MS, 8).
|
|
16
|
+
-define(COALESCE_WATERMARK_BYTES, 16 * 1024).
|
|
17
|
+
-define(FLOW_PAUSE_THRESHOLD, 512 * 1024).
|
|
18
|
+
-define(FLOW_RESUME_THRESHOLD, 128 * 1024).
|
|
19
|
+
|
|
20
|
+
%% ---------- Keepalive ----------
|
|
21
|
+
-define(KEEPALIVE_DEFAULT_INTERVAL_MS, 30_000).
|
|
22
|
+
-define(KEEPALIVE_DEFAULT_MAX_FAIL, 3).
|
|
23
|
+
|
|
24
|
+
%% ---------- Reconnect ----------
|
|
25
|
+
-define(RECONNECT_BASE_MS, 5_000).
|
|
26
|
+
-define(RECONNECT_MAX_MS, 60_000).
|
|
27
|
+
-define(RECONNECT_MAX_ATTEMPTS, 5).
|
|
28
|
+
|
|
29
|
+
%% ---------- IPC client record ----------
|
|
30
|
+
-record(client, {
|
|
31
|
+
socket :: gen_tcp:socket() | port(),
|
|
32
|
+
authed = false :: boolean(),
|
|
33
|
+
pending = #{} :: #{integer() => {erlang:timestamp(), term()}},
|
|
34
|
+
send_q = queue:new() :: queue:queue(binary()),
|
|
35
|
+
flow_blocked = false :: boolean()
|
|
36
|
+
}).
|
|
37
|
+
|
|
38
|
+
%% ---------- Connection record (conn_worker) ----------
|
|
39
|
+
-record(conn, {
|
|
40
|
+
id :: binary(),
|
|
41
|
+
spec :: map(),
|
|
42
|
+
ssh_ref :: reference() | undefined,
|
|
43
|
+
channels = #{} :: #{binary() => pid()},
|
|
44
|
+
next_ch_seq = 1 :: pos_integer(),
|
|
45
|
+
reconnect_count = 0 :: non_neg_integer(),
|
|
46
|
+
options :: [proplists:property()],
|
|
47
|
+
fail_reason :: term() | undefined,
|
|
48
|
+
proxy_sock_fun :: function() | undefined
|
|
49
|
+
}).
|
|
50
|
+
|
|
51
|
+
%% ---------- Channel record (channel_stm) ----------
|
|
52
|
+
-record(ch, {
|
|
53
|
+
id :: binary(),
|
|
54
|
+
conn_id :: binary(),
|
|
55
|
+
ssh_ref :: reference(),
|
|
56
|
+
ssh_chan_id :: integer(),
|
|
57
|
+
type :: shell | exec | subsystem,
|
|
58
|
+
term_type :: binary() | undefined,
|
|
59
|
+
cols :: pos_integer(),
|
|
60
|
+
rows :: pos_integer(),
|
|
61
|
+
command :: binary() | undefined,
|
|
62
|
+
coalesce_ref :: pid() | undefined
|
|
63
|
+
}).
|
|
64
|
+
|
|
65
|
+
%% ---------- SFTP session record ----------
|
|
66
|
+
-record(sftp, {
|
|
67
|
+
id :: binary(),
|
|
68
|
+
conn_id :: binary(),
|
|
69
|
+
ssh_ref :: reference(),
|
|
70
|
+
sftp_pid :: pid() | undefined
|
|
71
|
+
}).
|
|
72
|
+
|
|
73
|
+
%% ---------- Gateway state ----------
|
|
74
|
+
-record(gw_state, {
|
|
75
|
+
listen_sock,
|
|
76
|
+
clients = #{} :: #{reference() => #client{}},
|
|
77
|
+
auth_token :: binary(),
|
|
78
|
+
routes :: #{binary() => {module(), atom()}},
|
|
79
|
+
coalesce_ref :: pid() | undefined
|
|
80
|
+
}).
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
-module(ssh_ipc_coalesce).
|
|
2
|
+
-behaviour(gen_server).
|
|
3
|
+
|
|
4
|
+
%% High-frequency push coalescer with backpressure watermark.
|
|
5
|
+
%% Receives per-channel data via enqueue/2 and flushes batched
|
|
6
|
+
%% channel.data.batch pushes every COALESCE_TICK_MS or when
|
|
7
|
+
%% the buffer reaches COALESCE_WATERMARK_BYTES.
|
|
8
|
+
|
|
9
|
+
-export([start_link/0, enqueue/3, pause/1, resume/1]).
|
|
10
|
+
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
|
|
11
|
+
|
|
12
|
+
-include("ssh_ipc.hrl").
|
|
13
|
+
|
|
14
|
+
-record(st, {
|
|
15
|
+
buffer = #{} :: #{binary() => [binary()]}, % channel_id => [data]
|
|
16
|
+
buffer_size = 0 :: non_neg_integer(),
|
|
17
|
+
tick_ref :: reference() | undefined
|
|
18
|
+
}).
|
|
19
|
+
|
|
20
|
+
%% ---------- Public API ----------
|
|
21
|
+
|
|
22
|
+
%% @doc Start the coalesce gen_server.
|
|
23
|
+
start_link() ->
|
|
24
|
+
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
|
25
|
+
|
|
26
|
+
%% @doc Enqueue data for a channel. Called by ssh_channel_stm.
|
|
27
|
+
enqueue(CoalesceRef, ChannelId, Data) ->
|
|
28
|
+
gen_server:cast(CoalesceRef, {enqueue, ChannelId, Data}).
|
|
29
|
+
|
|
30
|
+
%% @doc Signal flow pause for a channel (backpressure).
|
|
31
|
+
pause(ChannelId) ->
|
|
32
|
+
gen_server:cast(?MODULE, {pause, ChannelId}).
|
|
33
|
+
|
|
34
|
+
%% @doc Signal flow resume for a channel.
|
|
35
|
+
resume(ChannelId) ->
|
|
36
|
+
gen_server:cast(?MODULE, {resume, ChannelId}).
|
|
37
|
+
|
|
38
|
+
%% ---------- Callbacks ----------
|
|
39
|
+
|
|
40
|
+
init([]) ->
|
|
41
|
+
TickRef = schedule_tick(),
|
|
42
|
+
{ok, #st{tick_ref = TickRef}}.
|
|
43
|
+
|
|
44
|
+
handle_call(_Req, _From, State) ->
|
|
45
|
+
{reply, {error, not_implemented}, State}.
|
|
46
|
+
|
|
47
|
+
handle_cast({enqueue, ChannelId, Data}, #st{buffer = Buf, buffer_size = Sz} = S) ->
|
|
48
|
+
NewBuf = maps:update_with(ChannelId, fun(Items) -> Items ++ [Data] end, [Data], Buf),
|
|
49
|
+
NewSz = Sz + byte_size(Data),
|
|
50
|
+
S1 = S#st{buffer = NewBuf, buffer_size = NewSz},
|
|
51
|
+
%% Flush immediately if watermark reached
|
|
52
|
+
S2 = case NewSz >= ?COALESCE_WATERMARK_BYTES of
|
|
53
|
+
true -> do_flush(S1);
|
|
54
|
+
false -> S1
|
|
55
|
+
end,
|
|
56
|
+
{noreply, S2};
|
|
57
|
+
|
|
58
|
+
handle_cast({pause, ChannelId}, S) ->
|
|
59
|
+
ssh_ipc_gateway:push_event(<<"channel.flow.pause">>, #{<<"channel_id">> => ChannelId}),
|
|
60
|
+
{noreply, S};
|
|
61
|
+
|
|
62
|
+
handle_cast({resume, ChannelId}, S) ->
|
|
63
|
+
ssh_ipc_gateway:push_event(<<"channel.flow.resume">>, #{<<"channel_id">> => ChannelId}),
|
|
64
|
+
{noreply, S};
|
|
65
|
+
|
|
66
|
+
handle_cast(_Msg, State) ->
|
|
67
|
+
{noreply, State}.
|
|
68
|
+
|
|
69
|
+
handle_info(tick, S) ->
|
|
70
|
+
S1 = do_flush(S),
|
|
71
|
+
S2 = S1#st{tick_ref = schedule_tick()},
|
|
72
|
+
{noreply, S2};
|
|
73
|
+
|
|
74
|
+
handle_info(_Info, State) ->
|
|
75
|
+
{noreply, State}.
|
|
76
|
+
|
|
77
|
+
terminate(_Reason, _State) ->
|
|
78
|
+
ok.
|
|
79
|
+
|
|
80
|
+
%% ---------- Internal ----------
|
|
81
|
+
|
|
82
|
+
schedule_tick() ->
|
|
83
|
+
erlang:send_after(?COALESCE_TICK_MS, self(), tick).
|
|
84
|
+
|
|
85
|
+
%% @doc Flush the coalesce buffer as a channel.data.batch push.
|
|
86
|
+
do_flush(#st{buffer = Buf} = S) when map_size(Buf) =:= 0 ->
|
|
87
|
+
S;
|
|
88
|
+
do_flush(#st{buffer = Buf} = S) ->
|
|
89
|
+
Items = maps:fold(fun(ChId, Datas, Acc) ->
|
|
90
|
+
B64 = ssh_codec:encode_b64(iolist_to_binary(Datas)),
|
|
91
|
+
[#{<<"id">> => ChId, <<"data">> => B64} | Acc]
|
|
92
|
+
end, [], Buf),
|
|
93
|
+
ssh_ipc_gateway:push_batch(#{<<"items">> => Items}),
|
|
94
|
+
S#st{buffer = #{}, buffer_size = 0}.
|