@browsersync/bslive 0.0.5 → 0.0.9

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.
Files changed (137) hide show
  1. package/Cargo.toml +32 -18
  2. package/bin.js +6 -0
  3. package/bslive/Cargo.toml +35 -0
  4. package/{build.rs → bslive/build.rs} +1 -1
  5. package/bslive/src/lib.rs +130 -0
  6. package/bsnext/Cargo.toml +21 -0
  7. package/bsnext/src/main.rs +110 -0
  8. package/crates/bsnext_client/Cargo.toml +8 -0
  9. package/crates/bsnext_client/build.rs +53 -0
  10. package/crates/bsnext_client/dist/index.js +6493 -0
  11. package/crates/bsnext_client/generated/dto.ts +116 -0
  12. package/crates/bsnext_client/generated/schema.ts +187 -0
  13. package/crates/bsnext_client/index.html +14 -0
  14. package/crates/bsnext_client/package-lock.json +2059 -0
  15. package/crates/bsnext_client/package.json +25 -0
  16. package/crates/bsnext_client/src/lib.rs +1 -0
  17. package/crates/bsnext_client/style.css +3 -0
  18. package/crates/bsnext_client/ts/console.ts +25 -0
  19. package/crates/bsnext_client/ts/index.ts +73 -0
  20. package/crates/bsnext_client/tsconfig.json +16 -0
  21. package/crates/bsnext_core/Cargo.toml +43 -0
  22. package/crates/bsnext_core/src/dir_loader.rs +230 -0
  23. package/crates/bsnext_core/src/dto.rs +281 -0
  24. package/crates/bsnext_core/src/handlers/mod.rs +1 -0
  25. package/crates/bsnext_core/src/handlers/proxy.rs +95 -0
  26. package/crates/bsnext_core/src/lib.rs +11 -0
  27. package/crates/bsnext_core/src/meta/mod.rs +5 -0
  28. package/crates/bsnext_core/src/not_found/mod.rs +2 -0
  29. package/crates/bsnext_core/src/not_found/not_found.html +20 -0
  30. package/crates/bsnext_core/src/not_found/not_found_service.rs +41 -0
  31. package/crates/bsnext_core/src/not_found/route_list.rs +49 -0
  32. package/crates/bsnext_core/src/panic_handler.rs +32 -0
  33. package/crates/bsnext_core/src/raw_loader.rs +226 -0
  34. package/crates/bsnext_core/src/server/actor.rs +92 -0
  35. package/crates/bsnext_core/src/server/error.rs +41 -0
  36. package/crates/bsnext_core/src/server/handler_change.rs +85 -0
  37. package/crates/bsnext_core/src/server/handler_listen.rs +157 -0
  38. package/crates/bsnext_core/src/server/handler_patch.rs +42 -0
  39. package/crates/bsnext_core/src/server/handler_routes_updated.rs +27 -0
  40. package/crates/bsnext_core/src/server/handler_stop.rs +38 -0
  41. package/crates/bsnext_core/src/server/mod.rs +10 -0
  42. package/crates/bsnext_core/src/server/router/mod.rs +112 -0
  43. package/crates/bsnext_core/src/server/router/tests.rs +204 -0
  44. package/crates/bsnext_core/src/server/signals.rs +11 -0
  45. package/crates/bsnext_core/src/server/state.rs +19 -0
  46. package/crates/bsnext_core/src/servers_supervisor/actor.rs +199 -0
  47. package/crates/bsnext_core/src/servers_supervisor/file_changed_handler.rs +41 -0
  48. package/crates/bsnext_core/src/servers_supervisor/get_servers_handler.rs +23 -0
  49. package/crates/bsnext_core/src/servers_supervisor/input_changed_handler.rs +21 -0
  50. package/crates/bsnext_core/src/servers_supervisor/mod.rs +6 -0
  51. package/crates/bsnext_core/src/servers_supervisor/start_handler.rs +82 -0
  52. package/crates/bsnext_core/src/servers_supervisor/stop_handler.rs +26 -0
  53. package/crates/bsnext_core/src/ws/mod.rs +164 -0
  54. package/crates/bsnext_example/Cargo.toml +10 -0
  55. package/crates/bsnext_example/src/basic.rs +51 -0
  56. package/crates/bsnext_example/src/lib.rs +26 -0
  57. package/crates/bsnext_example/src/lit.rs +37 -0
  58. package/crates/bsnext_example/src/md.rs +18 -0
  59. package/crates/bsnext_fs/Cargo.toml +22 -0
  60. package/crates/bsnext_fs/src/actor.rs +122 -0
  61. package/crates/bsnext_fs/src/buffered_debounce.rs +166 -0
  62. package/crates/bsnext_fs/src/filter.rs +30 -0
  63. package/crates/bsnext_fs/src/inner_fs_event_handler.rs +94 -0
  64. package/crates/bsnext_fs/src/lib.rs +141 -0
  65. package/crates/bsnext_fs/src/remove_path_handler.rs +46 -0
  66. package/crates/bsnext_fs/src/stop_handler.rs +15 -0
  67. package/crates/bsnext_fs/src/stream.rs +167 -0
  68. package/crates/bsnext_fs/src/test/mod.rs +213 -0
  69. package/crates/bsnext_fs/src/watch_path_handler.rs +67 -0
  70. package/crates/bsnext_fs/src/watcher.rs +348 -0
  71. package/crates/bsnext_input/Cargo.toml +22 -0
  72. package/crates/bsnext_input/src/input_test/mod.rs +151 -0
  73. package/crates/bsnext_input/src/lib.rs +153 -0
  74. package/crates/bsnext_input/src/md.rs +541 -0
  75. package/crates/bsnext_input/src/paths.rs +64 -0
  76. package/crates/bsnext_input/src/route.rs +185 -0
  77. package/crates/bsnext_input/src/route_manifest.rs +186 -0
  78. package/crates/bsnext_input/src/server_config.rs +88 -0
  79. package/crates/bsnext_input/src/target.rs +7 -0
  80. package/crates/bsnext_input/src/watch_opt_test/mod.rs +68 -0
  81. package/crates/bsnext_input/src/yml.rs +1 -0
  82. package/crates/bsnext_output/Cargo.toml +16 -0
  83. package/crates/bsnext_output/src/json.rs +11 -0
  84. package/crates/bsnext_output/src/lib.rs +25 -0
  85. package/crates/bsnext_output/src/pretty.rs +147 -0
  86. package/crates/bsnext_resp/Cargo.toml +13 -0
  87. package/crates/bsnext_resp/src/js/snippet.html +1 -0
  88. package/crates/bsnext_resp/src/js/ws.js +1 -0
  89. package/crates/bsnext_resp/src/lib.rs +79 -0
  90. package/crates/bsnext_system/Cargo.toml +29 -0
  91. package/crates/bsnext_system/src/args.rs +43 -0
  92. package/crates/bsnext_system/src/lib.rs +227 -0
  93. package/crates/bsnext_system/src/monitor.rs +241 -0
  94. package/crates/bsnext_system/src/monitor_any_watchables.rs +127 -0
  95. package/crates/bsnext_system/src/start_kind/snapshots/bsnext_system__start_kind__start_from_paths__test__test-2.snap +11 -0
  96. package/crates/bsnext_system/src/start_kind/snapshots/bsnext_system__start_kind__start_from_paths__test__test.snap +29 -0
  97. package/crates/bsnext_system/src/start_kind/start_from_example.rs +49 -0
  98. package/crates/bsnext_system/src/start_kind/start_from_inputs.rs +67 -0
  99. package/crates/bsnext_system/src/start_kind/start_from_paths.rs +51 -0
  100. package/crates/bsnext_system/src/start_kind.rs +56 -0
  101. package/crates/bsnext_system/src/startup.rs +51 -0
  102. package/crates/bsnext_tracing/Cargo.toml +10 -0
  103. package/crates/bsnext_tracing/src/lib.rs +89 -0
  104. package/examples/basic/input.yml +5 -0
  105. package/examples/basic/public/index.html +15 -0
  106. package/examples/basic/public/reset.css +52 -0
  107. package/examples/basic/public/script.js +1 -0
  108. package/examples/basic/public/styles.css +3 -0
  109. package/examples/kitchen-sink/a-file.md +1 -0
  110. package/examples/kitchen-sink/api/1.json +1 -0
  111. package/examples/kitchen-sink/api/2.json +3 -0
  112. package/examples/kitchen-sink/app.js +1 -0
  113. package/examples/kitchen-sink/input.html +185 -0
  114. package/examples/kitchen-sink/input.yml +133 -0
  115. package/examples/kitchen-sink/styles-2.css +3 -0
  116. package/examples/lit/index.html +21 -0
  117. package/examples/lit/input.yml +5 -0
  118. package/examples/lit/lit.js +82 -0
  119. package/examples/lit/package-lock.json +62 -0
  120. package/examples/lit/package.json +15 -0
  121. package/examples/md-single/frontmatter.md +35 -0
  122. package/examples/md-single/md-single.md +35 -0
  123. package/examples/openai/.nvm +1 -0
  124. package/examples/openai/build.mjs +21 -0
  125. package/examples/openai/index.html +13 -0
  126. package/examples/openai/input.yml +59 -0
  127. package/examples/openai/package-lock.json +720 -0
  128. package/examples/openai/package.json +21 -0
  129. package/examples/openai/src/index.js +21 -0
  130. package/examples/openai/sse/01.txt +8 -0
  131. package/examples/proxy-simple/input.yml +9 -0
  132. package/examples/single/input.yaml +26 -0
  133. package/index.d.ts +3 -1
  134. package/index.js +3 -2
  135. package/package.json +22 -19
  136. package/run.sh +6 -0
  137. package/src/lib.rs +0 -9
@@ -0,0 +1,19 @@
1
+ use crate::dto::ClientEvent;
2
+ use bsnext_input::route::Route;
3
+ use std::fmt::Formatter;
4
+ use std::sync::Arc;
5
+ use tokio::sync::{broadcast, RwLock};
6
+
7
+ #[derive(Clone)]
8
+ pub struct ServerState {
9
+ pub routes: Arc<RwLock<Vec<Route>>>,
10
+ pub client_sender: Arc<broadcast::Sender<ClientEvent>>,
11
+ }
12
+
13
+ impl std::fmt::Debug for ServerState {
14
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
15
+ f.debug_struct("AppState")
16
+ .field("routes", &"Arc<RwLock<Vec<Route>>>")
17
+ .finish()
18
+ }
19
+ }
@@ -0,0 +1,199 @@
1
+ use crate::dto::{ServerChange, ServerChangeSet, ServerChangeSetItem};
2
+
3
+ use crate::server::handler_stop::Stop;
4
+ use actix::{Actor, Addr, Running};
5
+
6
+ use crate::server::actor::ServerActor;
7
+ use crate::servers_supervisor::start_handler::StartMessage;
8
+
9
+ use crate::server::handler_patch::Patch;
10
+ use bsnext_input::server_config::Identity;
11
+ use bsnext_input::Input;
12
+ use std::collections::HashSet;
13
+ use std::future::Future;
14
+ use std::net::SocketAddr;
15
+
16
+ use std::pin::Pin;
17
+ use tokio::sync::oneshot::Sender;
18
+ use tracing::{span, Level};
19
+
20
+ #[derive(Debug)]
21
+ pub struct ServersSupervisor {
22
+ pub(crate) handlers: std::collections::HashMap<Identity, ChildHandler>,
23
+ tx: Option<Sender<()>>,
24
+ }
25
+
26
+ impl ServersSupervisor {
27
+ pub fn new(tx: Sender<()>) -> Self {
28
+ Self {
29
+ handlers: std::default::Default::default(),
30
+ tx: Some(tx),
31
+ }
32
+ }
33
+
34
+ pub(crate) fn input_changed(
35
+ &mut self,
36
+ self_addr: Addr<ServersSupervisor>,
37
+ input: Input,
38
+ ) -> Pin<Box<impl Future<Output = ServerChangeSet> + Sized>> {
39
+ let span = span!(Level::TRACE, "input_changed");
40
+ let _guard = span.enter();
41
+
42
+ let existing: HashSet<_> = self.handlers.values().map(|v| v.identity.clone()).collect();
43
+ let incoming: HashSet<_> = input.servers.iter().map(|s| s.identity.clone()).collect();
44
+
45
+ let startup = incoming.difference(&existing).collect::<HashSet<_>>();
46
+ let shutdown = existing.difference(&incoming).collect::<HashSet<_>>();
47
+ let patch = existing.intersection(&incoming).collect::<HashSet<_>>();
48
+
49
+ let shutdown_jobs = shutdown
50
+ .into_iter()
51
+ .filter_map(|identity| self.handlers.get(identity).map(ToOwned::to_owned))
52
+ .collect::<Vec<_>>();
53
+
54
+ let start_jobs = startup
55
+ .iter()
56
+ .filter_map(|s| {
57
+ input
58
+ .servers
59
+ .iter()
60
+ .find(|x| x.identity == **s)
61
+ .map(ToOwned::to_owned)
62
+ })
63
+ .collect::<Vec<_>>();
64
+
65
+ let patch_jobs = patch
66
+ .iter()
67
+ .filter_map(|s| {
68
+ let child = self.handlers.get(s).map(ToOwned::to_owned);
69
+ let input = input
70
+ .servers
71
+ .iter()
72
+ .find(|x| x.identity == **s)
73
+ .map(ToOwned::to_owned);
74
+ match (child, input) {
75
+ (Some(child), Some(config)) => Some((child, config)),
76
+ _ => None,
77
+ }
78
+ })
79
+ .collect::<Vec<_>>();
80
+
81
+ Box::pin(async move {
82
+ let mut changeset = ServerChangeSet { items: vec![] };
83
+ for x in shutdown_jobs {
84
+ tracing::debug!("stopping {:?}", x.identity);
85
+ changeset.items.push(ServerChangeSetItem {
86
+ identity: (&x.identity).into(),
87
+ change: ServerChange::Stopped {
88
+ bind_address: x.socket_addr.to_string(),
89
+ },
90
+ });
91
+ match x.actor_address.send(Stop).await {
92
+ Ok(_) => {
93
+ self_addr.do_send(ChildStopped {
94
+ identity: x.identity.clone(),
95
+ });
96
+ }
97
+ Err(_) => {
98
+ tracing::error!("couldn't send Stop2 {:?}", x.identity);
99
+ }
100
+ }
101
+ }
102
+
103
+ if !start_jobs.is_empty() {
104
+ tracing::debug!("starting {:?} servers", start_jobs.len());
105
+ let idens = start_jobs
106
+ .iter()
107
+ .map(|x| &x.identity)
108
+ .map(|x| x.to_owned())
109
+ .collect::<Vec<_>>();
110
+ match self_addr
111
+ .send(StartMessage {
112
+ server_configs: start_jobs,
113
+ })
114
+ .await
115
+ {
116
+ Ok(_) => {
117
+ for x in idens {
118
+ changeset.items.push(ServerChangeSetItem {
119
+ identity: (&x).into(),
120
+ change: ServerChange::Started,
121
+ })
122
+ }
123
+ }
124
+ Err(_) => tracing::error!("could not send StartMessage to self"),
125
+ };
126
+ }
127
+
128
+ for (child, config) in patch_jobs {
129
+ tracing::debug!("patching {:?}", child.identity);
130
+ child.actor_address.do_send(Patch {
131
+ server_config: config,
132
+ });
133
+ changeset.items.push(ServerChangeSetItem {
134
+ identity: (&child.identity).into(),
135
+ change: ServerChange::Patched,
136
+ })
137
+ }
138
+
139
+ changeset
140
+ })
141
+ }
142
+ }
143
+
144
+ impl Actor for ServersSupervisor {
145
+ type Context = actix::Context<Self>;
146
+ fn started(&mut self, _ctx: &mut Self::Context) {
147
+ tracing::trace!(actor.name = "Servers", actor.lifecyle = "started")
148
+ }
149
+
150
+ fn stopping(&mut self, _ctx: &mut Self::Context) -> Running {
151
+ tracing::trace!(actor.name = "Servers", actor.lifecyle = "stopping");
152
+ Running::Stop
153
+ }
154
+
155
+ fn stopped(&mut self, _ctx: &mut Self::Context) {
156
+ let span = span!(parent: None, Level::TRACE, "stopped", actor.name = "Servers");
157
+ tracing::trace!(actor.lifecyle = "stopping");
158
+ let _guard = span.enter();
159
+ if let Some(tx) = self.tx.take() {
160
+ tracing::trace!("sending final completion");
161
+ match tx.send(()) {
162
+ Ok(_) => {
163
+ tracing::trace!("✅ sent final completion message");
164
+ }
165
+ Err(_) => {
166
+ tracing::trace!("❌ could not send final completion message. This usually means the actor went away (eg: a crash) before we could send this.");
167
+ }
168
+ }
169
+ } else {
170
+ tracing::error!("could not access oneshot sender for completion message")
171
+ }
172
+ }
173
+ }
174
+
175
+ #[derive(Debug, Clone)]
176
+ pub struct ChildHandler {
177
+ pub actor_address: Addr<ServerActor>,
178
+ pub identity: Identity,
179
+ pub socket_addr: SocketAddr,
180
+ }
181
+
182
+ #[derive(actix::Message)]
183
+ #[rtype(result = "()")]
184
+ pub struct ChildStopped {
185
+ pub identity: Identity,
186
+ }
187
+
188
+ impl actix::Handler<ChildStopped> for ServersSupervisor {
189
+ type Result = ();
190
+
191
+ fn handle(&mut self, msg: ChildStopped, _ctx: &mut Self::Context) -> Self::Result {
192
+ tracing::trace!("Handler<Stopped> for Servers {:?}", msg.identity);
193
+ self.handlers.remove(&msg.identity);
194
+ tracing::trace!(
195
+ "Handler<Stopped> remaining handlers: {}",
196
+ self.handlers.len()
197
+ )
198
+ }
199
+ }
@@ -0,0 +1,41 @@
1
+ use crate::server::handler_change::Change;
2
+ use crate::servers_supervisor::actor::ServersSupervisor;
3
+ use std::path::PathBuf;
4
+
5
+ #[derive(actix::Message)]
6
+ #[rtype(result = "()")]
7
+ pub struct FileChanged {
8
+ pub path: PathBuf,
9
+ pub id: u64,
10
+ }
11
+
12
+ impl actix::Handler<FileChanged> for ServersSupervisor {
13
+ type Result = ();
14
+
15
+ fn handle(&mut self, msg: FileChanged, _ctx: &mut Self::Context) -> Self::Result {
16
+ for child in self.handlers.values() {
17
+ if child.identity.as_id() == msg.id {
18
+ child.actor_address.do_send(Change::fs(&msg.path))
19
+ }
20
+ }
21
+ }
22
+ }
23
+
24
+ #[derive(actix::Message)]
25
+ #[rtype(result = "()")]
26
+ pub struct FilesChanged {
27
+ pub paths: Vec<PathBuf>,
28
+ pub id: u64,
29
+ }
30
+
31
+ impl actix::Handler<FilesChanged> for ServersSupervisor {
32
+ type Result = ();
33
+
34
+ fn handle(&mut self, msg: FilesChanged, _ctx: &mut Self::Context) -> Self::Result {
35
+ for child in self.handlers.values() {
36
+ if child.identity.as_id() == msg.id {
37
+ child.actor_address.do_send(Change::fs_many(&msg.paths))
38
+ }
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,23 @@
1
+ use crate::dto::{GetServersMessageResponse, ServersDTO};
2
+ use crate::servers_supervisor::actor::ServersSupervisor;
3
+
4
+ #[derive(actix::Message)]
5
+ #[rtype(result = "GetServersMessageResponse")]
6
+ pub struct GetServersMessage;
7
+
8
+ impl actix::Handler<GetServersMessage> for ServersSupervisor {
9
+ type Result = GetServersMessageResponse;
10
+
11
+ fn handle(&mut self, _msg: GetServersMessage, _ctx: &mut Self::Context) -> Self::Result {
12
+ GetServersMessageResponse {
13
+ servers: self
14
+ .handlers
15
+ .iter()
16
+ .map(|(identity, child_handler)| ServersDTO {
17
+ identity: identity.into(),
18
+ socket_addr: child_handler.socket_addr.to_string(),
19
+ })
20
+ .collect(),
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,21 @@
1
+ use crate::dto::ServerChangeSet;
2
+ use crate::servers_supervisor::actor::ServersSupervisor;
3
+ use actix::AsyncContext;
4
+ use bsnext_input::Input;
5
+ use std::future::Future;
6
+
7
+ use std::pin::Pin;
8
+
9
+ #[derive(actix::Message)]
10
+ #[rtype(result = "ServerChangeSet")]
11
+ pub struct InputChanged {
12
+ pub input: Input,
13
+ }
14
+
15
+ impl actix::Handler<InputChanged> for ServersSupervisor {
16
+ type Result = Pin<Box<dyn Future<Output = ServerChangeSet>>>;
17
+
18
+ fn handle(&mut self, msg: InputChanged, ctx: &mut Self::Context) -> Self::Result {
19
+ self.input_changed(ctx.address(), msg.input)
20
+ }
21
+ }
@@ -0,0 +1,6 @@
1
+ pub mod actor;
2
+ pub mod file_changed_handler;
3
+ pub mod get_servers_handler;
4
+ pub mod input_changed_handler;
5
+ pub mod start_handler;
6
+ pub mod stop_handler;
@@ -0,0 +1,82 @@
1
+ use crate::server::actor::ServerActor;
2
+ use crate::server::handler_listen::Listen;
3
+ use crate::servers_supervisor::actor::{ChildHandler, ServersSupervisor};
4
+ use actix::{Actor, AsyncContext};
5
+ use bsnext_input::server_config::ServerConfig;
6
+ use futures_util::future::join_all;
7
+ use futures_util::FutureExt;
8
+ use std::future::Future;
9
+ use std::pin::Pin;
10
+ use tracing::{span, Instrument, Level};
11
+
12
+ #[derive(actix::Message)]
13
+ #[rtype(result = "()")]
14
+ pub(crate) struct StartMessage {
15
+ pub server_configs: Vec<ServerConfig>,
16
+ }
17
+
18
+ impl actix::Handler<StartMessage> for ServersSupervisor {
19
+ type Result = Pin<Box<dyn Future<Output = ()>>>;
20
+
21
+ fn handle(&mut self, msg: StartMessage, ctx: &mut Self::Context) -> Self::Result {
22
+ let span = span!(Level::TRACE, "actix::Handler<StartMessage> for Servers");
23
+ let self_addr = ctx.address();
24
+
25
+ let workload = async move {
26
+ tracing::debug!("creating {} actor(s)", msg.server_configs.len());
27
+ let fts = msg
28
+ .server_configs
29
+ .into_iter()
30
+ .map(|server_config| {
31
+ let server = ServerActor::new_from_config(server_config.clone());
32
+ let actor_addr = server.start();
33
+ let c = server_config.clone();
34
+ actor_addr.send(Listen).map(|r| (r, c))
35
+ })
36
+ .collect::<Vec<_>>();
37
+
38
+ let results = join_all(fts).await;
39
+ for (fut_result, server_config) in results {
40
+ match fut_result {
41
+ Ok(msg_response) => match msg_response {
42
+ Ok((addr, actor_addr)) => {
43
+ tracing::debug!("✚ got listening child: {}", addr.to_string());
44
+ self_addr.do_send(ChildCreated {
45
+ server_handler: ChildHandler {
46
+ actor_address: actor_addr,
47
+ identity: server_config.identity,
48
+ socket_addr: addr,
49
+ },
50
+ });
51
+ }
52
+ Err(e) => {
53
+ tracing::error!("{:?} <- {}", server_config.identity, e)
54
+ }
55
+ },
56
+ Err(e) => tracing::error!(" <- [m] {}", e),
57
+ }
58
+ }
59
+ }
60
+ .instrument(span);
61
+
62
+ Box::pin(workload)
63
+ }
64
+ }
65
+
66
+ #[derive(Debug, actix::Message)]
67
+ #[rtype(result = "()")]
68
+ pub struct ChildCreated {
69
+ server_handler: ChildHandler,
70
+ }
71
+
72
+ impl actix::Handler<ChildCreated> for ServersSupervisor {
73
+ type Result = ();
74
+
75
+ fn handle(&mut self, msg: ChildCreated, _ctx: &mut Self::Context) -> Self::Result {
76
+ self.handlers.insert(
77
+ msg.server_handler.identity.clone(),
78
+ msg.server_handler.clone(),
79
+ );
80
+ tracing::trace!("ChildCreated child count: {}", self.handlers.len());
81
+ }
82
+ }
@@ -0,0 +1,26 @@
1
+ use crate::server::handler_stop::Stop;
2
+ use crate::servers_supervisor::actor::ServersSupervisor;
3
+ use futures_util::future::join_all;
4
+ use std::future::Future;
5
+ use std::pin::Pin;
6
+
7
+ #[derive(actix::Message)]
8
+ #[rtype(result = "()")]
9
+ pub struct StopServers;
10
+
11
+ impl actix::Handler<StopServers> for ServersSupervisor {
12
+ type Result = Pin<Box<dyn Future<Output = ()>>>;
13
+
14
+ fn handle(&mut self, _msg: StopServers, _ctx: &mut Self::Context) -> Self::Result {
15
+ let aaddresses = self.handlers.clone();
16
+
17
+ Box::pin(async move {
18
+ tracing::debug!("stopping {} servers", aaddresses.len());
19
+ let fts = aaddresses
20
+ .values()
21
+ .map(|handler| handler.actor_address.send(Stop))
22
+ .collect::<Vec<_>>();
23
+ join_all(fts).await;
24
+ })
25
+ }
26
+ }
@@ -0,0 +1,164 @@
1
+ use axum::extract::ws::{Message, WebSocket, WebSocketUpgrade};
2
+ use axum::extract::{ConnectInfo, State};
3
+ use axum::response::IntoResponse;
4
+
5
+ use futures_util::{SinkExt, StreamExt};
6
+
7
+ use crate::dto::ClientEvent;
8
+ use crate::server::state::ServerState;
9
+ use std::net::SocketAddr;
10
+ use std::ops::ControlFlow;
11
+ use std::sync::Arc;
12
+ use tokio::sync::broadcast::error::RecvError;
13
+ use tokio::sync::broadcast::Sender;
14
+
15
+ /// The handler for the HTTP request (this gets called when the HTTP GET lands at the start
16
+ /// of websocket negotiation). After this completes, the actual switching from HTTP to
17
+ /// websocket protocol will occur.
18
+ /// This is the last point where we can extract TCP/IP metadata such as IP address of the client
19
+ /// as well as things from HTTP headers such as user-agent of the browser etc.
20
+ pub async fn ws_handler(
21
+ State(state): State<Arc<ServerState>>,
22
+ ws: WebSocketUpgrade,
23
+ ConnectInfo(addr): ConnectInfo<SocketAddr>,
24
+ ) -> impl IntoResponse {
25
+ // finalize the upgrade process by returning upgrade callback.
26
+ // we can customize the callback by sending additional info such as address.
27
+ ws.on_upgrade(move |socket| handle_socket(socket, addr, state.client_sender.clone()))
28
+ }
29
+
30
+ /// Actual websocket statemachine (one will be spawned per connection)
31
+ async fn handle_socket(
32
+ mut socket: WebSocket,
33
+ who: SocketAddr,
34
+ client_sender: Arc<Sender<ClientEvent>>,
35
+ ) {
36
+ //send a ping (unsupported by some browsers) just to kick things off and get a response
37
+ if socket.send(Message::Ping(vec![1, 2, 3])).await.is_ok() {
38
+ tracing::trace!("Pinged {who}...");
39
+ } else {
40
+ tracing::trace!("Could not send ping {who}!");
41
+ // no Error here since the only thing we can do is to close the connection.
42
+ // If we can not send messages, there is no way to salvage the statemachine anyway.
43
+ return;
44
+ }
45
+
46
+ // receive single message from a client (we can either receive or send with socket).
47
+ // this will likely be the Pong for our Ping or a hello message from client.
48
+ // waiting for message from a client will block this task, but will not block other client's
49
+ // connections.
50
+ if let Some(msg) = socket.recv().await {
51
+ if let Ok(msg) = msg {
52
+ if process_message(msg, who).is_break() {
53
+ return;
54
+ }
55
+ } else {
56
+ tracing::debug!("client {who} abruptly disconnected");
57
+ return;
58
+ }
59
+ }
60
+
61
+ // By splitting socket we can send and receive at the same time. In this example we will send
62
+ // unsolicited messages to client based on some sort of server's internal event (i.e .timer).
63
+ let (mut sender, mut receiver) = socket.split();
64
+
65
+ // Spawn a task that will push several messages to the client (does not matter what client does)
66
+ let mut send_task = tokio::spawn(async move {
67
+ let mut count = 0;
68
+ let mut client_receiver = client_sender.subscribe();
69
+ loop {
70
+ match client_receiver.recv().await {
71
+ Ok(incoming) => {
72
+ tracing::trace!(?incoming, "ws incoming!");
73
+ let output = serde_json::to_string(&incoming);
74
+ let Ok(as_str) = output else {
75
+ tracing::error!("could not serialize incoming client message");
76
+ break;
77
+ };
78
+ if sender.send(Message::Text(as_str)).await.is_err() {
79
+ return count;
80
+ } else {
81
+ count += 1;
82
+ }
83
+ }
84
+ Err(RecvError::Closed) => {
85
+ tracing::debug!("closed!");
86
+ }
87
+ Err(RecvError::Lagged(num)) => {
88
+ tracing::error!(?num, "loagged!");
89
+ }
90
+ }
91
+ }
92
+ count
93
+ });
94
+
95
+ // This second task will receive messages from client and print them on server console
96
+ let mut recv_task = tokio::spawn(async move {
97
+ let mut cnt = 0;
98
+ while let Some(Ok(msg)) = receiver.next().await {
99
+ cnt += 1;
100
+ // print message and break if instructed to do so
101
+ if process_message(msg, who).is_break() {
102
+ break;
103
+ }
104
+ }
105
+ cnt
106
+ });
107
+
108
+ // If any one of the tasks exit, abort the other.
109
+ tokio::select! {
110
+ rv_a = (&mut send_task) => {
111
+ match rv_a {
112
+ Ok(a) => tracing::trace!("{a} messages sent to {who}"),
113
+ Err(a) => tracing::trace!("Error sending messages {a:?}")
114
+ }
115
+ recv_task.abort();
116
+ },
117
+ rv_b = (&mut recv_task) => {
118
+ match rv_b {
119
+ Ok(b) => tracing::trace!("Received {b} messages"),
120
+ Err(b) => tracing::trace!("Error receiving messages {b:?}")
121
+ }
122
+ send_task.abort();
123
+ }
124
+ }
125
+
126
+ // returning from the handler closes the websocket connection
127
+ tracing::trace!("Websocket context {who} destroyed");
128
+ }
129
+
130
+ /// helper to print contents of messages to stdout. Has special treatment for Close.
131
+ fn process_message(msg: Message, who: SocketAddr) -> ControlFlow<(), ()> {
132
+ match msg {
133
+ Message::Text(t) => {
134
+ tracing::trace!(">>> {who} sent str: {t:?}");
135
+ }
136
+ Message::Binary(d) => {
137
+ tracing::trace!(">>> {} sent {} bytes: {:?}", who, d.len(), d);
138
+ }
139
+ Message::Close(c) => {
140
+ if let Some(cf) = c {
141
+ tracing::trace!(
142
+ ">>> {} sent close with code {} and reason `{}`",
143
+ who,
144
+ cf.code,
145
+ cf.reason
146
+ );
147
+ } else {
148
+ tracing::trace!(">>> {who} somehow sent close message without CloseFrame");
149
+ }
150
+ return ControlFlow::Break(());
151
+ }
152
+
153
+ Message::Pong(v) => {
154
+ tracing::trace!(">>> {who} sent pong with {v:?}");
155
+ }
156
+ // You should never need to manually handle Message::Ping, as axum's websocket library
157
+ // will do so for you automagically by replying with Pong and copying the v according to
158
+ // spec. But if you need the contents of the pings you can see them here.
159
+ Message::Ping(v) => {
160
+ tracing::trace!(">>> {who} sent ping with {v:?}");
161
+ }
162
+ }
163
+ ControlFlow::Continue(())
164
+ }
@@ -0,0 +1,10 @@
1
+ [package]
2
+ name = "bsnext_example"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+
6
+ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7
+
8
+ [dependencies]
9
+ bsnext_input = { path = "../bsnext_input" }
10
+ clap = { workspace = true }
@@ -0,0 +1,51 @@
1
+ use bsnext_input::route::{Route, RouteKind};
2
+ use bsnext_input::server_config::Identity;
3
+ use bsnext_input::{
4
+ server_config::{self},
5
+ Input,
6
+ };
7
+
8
+ #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
9
+ pub struct BasicExample;
10
+
11
+ impl BasicExample {
12
+ pub fn into_input(self, identity: Option<Identity>) -> Input {
13
+ let server = server_config::ServerConfig {
14
+ identity: identity.unwrap_or_else(Identity::named),
15
+ routes: vec![
16
+ Route {
17
+ path: "/".to_string(),
18
+ kind: RouteKind::Html {
19
+ html: include_str!("../../../examples/basic/public/index.html").to_owned(),
20
+ },
21
+ ..Default::default()
22
+ },
23
+ Route {
24
+ path: "/styles.css".to_string(),
25
+ kind: RouteKind::Raw {
26
+ raw: include_str!("../../../examples/basic/public/styles.css").to_owned(),
27
+ },
28
+ ..Default::default()
29
+ },
30
+ Route {
31
+ path: "/script.js".to_string(),
32
+ kind: RouteKind::Raw {
33
+ raw: include_str!("../../../examples/basic/public/script.js").to_owned(),
34
+ },
35
+ ..Default::default()
36
+ },
37
+ Route {
38
+ path: "/reset.css".to_string(),
39
+ kind: RouteKind::Raw {
40
+ raw: include_str!("../../../examples/basic/public/reset.css").to_owned(),
41
+ },
42
+ ..Default::default()
43
+ },
44
+ ],
45
+ ..Default::default()
46
+ };
47
+ Input {
48
+ servers: vec![server],
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,26 @@
1
+ use crate::basic::BasicExample;
2
+ use crate::lit::LitExample;
3
+ use crate::md::MdExample;
4
+ use bsnext_input::server_config::Identity;
5
+ use bsnext_input::Input;
6
+
7
+ pub mod basic;
8
+ pub mod lit;
9
+ pub mod md;
10
+
11
+ #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
12
+ pub enum Example {
13
+ Basic,
14
+ Lit,
15
+ Md,
16
+ }
17
+
18
+ impl Example {
19
+ pub fn into_input(self, identity: Identity) -> Input {
20
+ match self {
21
+ Example::Basic => BasicExample.into_input(Some(identity)),
22
+ Example::Lit => LitExample.into_input(Some(identity)),
23
+ Example::Md => MdExample.into_input(Some(identity)),
24
+ }
25
+ }
26
+ }