@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,85 @@
1
+ use crate::dto::ClientEvent;
2
+ use crate::server::actor::ServerActor;
3
+
4
+ use std::path::{Path, PathBuf};
5
+
6
+ #[derive(actix::Message, Clone, Debug)]
7
+ #[rtype(result = "()")]
8
+ pub enum Change {
9
+ Fs {
10
+ path: PathBuf,
11
+ change_kind: ChangeKind,
12
+ },
13
+ FsMany(Vec<Change>),
14
+ }
15
+
16
+ #[typeshare::typeshare]
17
+ #[derive(Clone, Debug, serde::Serialize)]
18
+ pub enum ChangeKind {
19
+ Changed,
20
+ Added,
21
+ Removed,
22
+ }
23
+
24
+ impl Change {
25
+ pub fn fs<A: AsRef<Path>>(a: A) -> Self {
26
+ Self::Fs {
27
+ path: a.as_ref().to_path_buf(),
28
+ change_kind: ChangeKind::Changed,
29
+ }
30
+ }
31
+ pub fn fs_many<A: AsRef<Path>>(a: &[A]) -> Self {
32
+ Self::FsMany(
33
+ a.iter()
34
+ .map(|p| p.as_ref().to_owned())
35
+ .map(|p| Self::Fs {
36
+ path: p,
37
+ change_kind: ChangeKind::Changed,
38
+ })
39
+ .collect(),
40
+ )
41
+ }
42
+ pub fn fs_added<A: AsRef<Path>>(a: A) -> Self {
43
+ Self::Fs {
44
+ path: a.as_ref().to_path_buf(),
45
+ change_kind: ChangeKind::Added,
46
+ }
47
+ }
48
+ pub fn fs_removed<A: AsRef<Path>>(a: A) -> Self {
49
+ Self::Fs {
50
+ path: a.as_ref().to_path_buf(),
51
+ change_kind: ChangeKind::Removed,
52
+ }
53
+ }
54
+ }
55
+
56
+ impl actix::Handler<Change> for ServerActor {
57
+ type Result = ();
58
+
59
+ fn handle(&mut self, msg: Change, _ctx: &mut Self::Context) -> Self::Result {
60
+ if let Some(sender) = self.signals.as_ref().and_then(|s| s.client_sender.as_ref()) {
61
+ // todo: what messages are the clients expecting?
62
+ tracing::info!("forwarding `Change` event to connected web socket clients");
63
+ match sender.send(ClientEvent::Change((&msg).into())) {
64
+ Ok(_) => {
65
+ tracing::trace!("change event sent to clients");
66
+ }
67
+ Err(_) => tracing::error!("not sent to client_sender"),
68
+ };
69
+ } else {
70
+ tracing::debug!("signals not ready, should they be?");
71
+ }
72
+ }
73
+ }
74
+ #[cfg(test)]
75
+ mod test {
76
+ use super::*;
77
+ use crate::dto::ChangeDTO;
78
+ #[test]
79
+ fn test_serialize() -> anyhow::Result<()> {
80
+ let fs: ChangeDTO = (&Change::fs("./a.js")).into();
81
+ let json = serde_json::to_string(&fs).unwrap();
82
+ print!("{json}");
83
+ Ok(())
84
+ }
85
+ }
@@ -0,0 +1,157 @@
1
+ use crate::server::actor::ServerActor;
2
+ use crate::server::error::ServerError;
3
+ use crate::server::router::make_router;
4
+ use crate::server::state::ServerState;
5
+ use actix::AsyncContext;
6
+ use actix_rt::Arbiter;
7
+ use bsnext_input::server_config::Identity;
8
+ use std::future::Future;
9
+ use std::io::ErrorKind;
10
+ use std::net::{SocketAddr, TcpListener};
11
+ use std::pin::Pin;
12
+ use std::sync::Arc;
13
+ use tokio::sync::{oneshot, RwLock};
14
+
15
+ #[derive(actix::Message)]
16
+ #[rtype(result = "Result<(SocketAddr, actix::Addr<ServerActor>), ServerError>")]
17
+ pub struct Listen;
18
+
19
+ impl actix::Handler<Listen> for ServerActor {
20
+ type Result =
21
+ Pin<Box<dyn Future<Output = Result<(SocketAddr, actix::Addr<ServerActor>), ServerError>>>>;
22
+
23
+ fn handle(&mut self, _msg: Listen, ctx: &mut Self::Context) -> Self::Result {
24
+ let identity = self.config.identity.clone();
25
+ tracing::trace!("actor started for {:?}", identity);
26
+ let (send_complete, handle, client_sender) = self.install_signals();
27
+ let (oneshot_send, oneshot_rec) = oneshot::channel();
28
+ let h1 = handle.clone();
29
+ let h2 = handle.clone();
30
+
31
+ let app_state = Arc::new(ServerState {
32
+ routes: Arc::new(RwLock::new(self.config.routes.clone())),
33
+ client_sender: Arc::new(client_sender),
34
+ });
35
+
36
+ self.app_state = Some(app_state.clone());
37
+ let self_addr = ctx.address();
38
+
39
+ let server = async move {
40
+ let router = make_router(&app_state);
41
+
42
+ let maybe_socket_addr: Result<SocketAddr, _> = match identity {
43
+ Identity::Both {
44
+ ref bind_address, ..
45
+ } => bind_address.parse(),
46
+ Identity::Address { ref bind_address } => bind_address.parse(),
47
+ Identity::Named { .. } => {
48
+ format!("127.0.0.1:{}", get_available_port().expect("port?")).parse()
49
+ }
50
+ };
51
+
52
+ let Ok(socket_addr) = maybe_socket_addr else {
53
+ tracing::error!(
54
+ "{:?} [❌ NOT started] could not parse bind_address",
55
+ identity
56
+ );
57
+
58
+ match oneshot_send.send(Err(ServerError::InvalidAddress {
59
+ addr_parse_error: maybe_socket_addr.unwrap_err().to_string(),
60
+ })) {
61
+ Ok(_) => {}
62
+ Err(_) => tracing::error!("oneshot send failed"),
63
+ }
64
+ return;
65
+ };
66
+
67
+ tracing::trace!("trying to listen on {:?}", socket_addr);
68
+
69
+ let server = axum_server::bind(socket_addr)
70
+ .handle(h1)
71
+ .serve(router.into_make_service_with_connect_info::<SocketAddr>());
72
+
73
+ let result: Result<_, ServerError> = match server.await {
74
+ Ok(_) => {
75
+ tracing::debug!("{:?} [started] Server all done", identity);
76
+ if send_complete.send(()).is_err() {
77
+ tracing::error!("{:?} [started] could not send complete message", identity);
78
+ }
79
+ Ok(())
80
+ }
81
+ Err(e) => match e.kind() {
82
+ ErrorKind::AddrInUse => {
83
+ tracing::error!("{:?} [not-started] [AddrInUse] {}", identity, e);
84
+ Err(ServerError::AddrInUse { socket_addr })
85
+ }
86
+ _ => {
87
+ tracing::error!("{:?} [not-started] UNKNOWN {}", identity, e);
88
+ Err(ServerError::Unknown)
89
+ }
90
+ },
91
+ };
92
+ if !oneshot_send.is_closed() {
93
+ let _r = oneshot_send.send(result);
94
+ }
95
+ };
96
+
97
+ Arbiter::current().spawn(server);
98
+
99
+ let self_addr = self_addr.clone();
100
+
101
+ Box::pin(async move {
102
+ tokio::select! {
103
+ listening = h2.listening() => {
104
+ match listening {
105
+ Some(socket_addr) => {
106
+ tracing::debug!("box::pin was listening...");
107
+ Ok((socket_addr, self_addr.clone()))
108
+ }
109
+ None => {
110
+ Err(ServerError::Unknown)
111
+ }
112
+ }
113
+ }
114
+ msg = oneshot_rec => {
115
+ match msg {
116
+ Ok(v) => {
117
+ match v {
118
+ Ok(_) => {
119
+ tracing::info!("All good from one_shot?");
120
+ Err(ServerError::Closed)
121
+ }
122
+ Err(e) => {
123
+ Err(e)
124
+ }
125
+ }
126
+ }
127
+ Err(e) => {
128
+ tracing::error!("-->{e}");
129
+ Err(ServerError::Unknown)
130
+ }
131
+ }
132
+ }
133
+ }
134
+ })
135
+ }
136
+ }
137
+
138
+ pub fn get_available_port() -> Option<u16> {
139
+ TcpListener::bind("127.0.0.1:0")
140
+ .and_then(|listener| listener.local_addr())
141
+ .map(|socket_addr| socket_addr.port())
142
+ .ok()
143
+ }
144
+
145
+ #[derive(actix::Message)]
146
+ #[rtype(result = "()")]
147
+ pub struct Listening {
148
+ addr: SocketAddr,
149
+ }
150
+
151
+ impl actix::Handler<Listening> for ServerActor {
152
+ type Result = ();
153
+
154
+ fn handle(&mut self, msg: Listening, _ctx: &mut Self::Context) -> Self::Result {
155
+ self.addr = Some(msg.addr);
156
+ }
157
+ }
@@ -0,0 +1,42 @@
1
+ use crate::server::actor::ServerActor;
2
+ use crate::server::handler_routes_updated::RoutesUpdated;
3
+ use actix::AsyncContext;
4
+ use actix_rt::Arbiter;
5
+ use anyhow::anyhow;
6
+ use bsnext_input::route_manifest::RoutesManifest;
7
+ use bsnext_input::server_config::ServerConfig;
8
+
9
+ #[derive(actix::Message, Clone)]
10
+ #[rtype(result = "anyhow::Result<()>")]
11
+ pub struct Patch {
12
+ pub server_config: ServerConfig,
13
+ }
14
+
15
+ impl actix::Handler<Patch> for ServerActor {
16
+ type Result = anyhow::Result<()>;
17
+
18
+ fn handle(&mut self, msg: Patch, ctx: &mut Self::Context) -> Self::Result {
19
+ let addr = ctx.address();
20
+ tracing::trace!("Handler<PatchOne> for ServerActor");
21
+ let app_state = self
22
+ .app_state
23
+ .as_ref()
24
+ .ok_or(anyhow!("could not access state"))?;
25
+ let app_state_clone = app_state.clone();
26
+ let routes = msg.server_config.routes.clone();
27
+ let next_manifest = RoutesManifest::new(&routes);
28
+ let changeset = self.routes_manifest.changeset_for(&next_manifest);
29
+ self.routes_manifest = RoutesManifest::new(&routes);
30
+
31
+ let update_dn = async move {
32
+ let mut mut_routes = app_state_clone.routes.write().await;
33
+ *mut_routes = routes;
34
+ addr.do_send(RoutesUpdated {
35
+ change_set: changeset,
36
+ })
37
+ };
38
+
39
+ Arbiter::current().spawn(update_dn);
40
+ Ok(())
41
+ }
42
+ }
@@ -0,0 +1,27 @@
1
+ use crate::server::actor::ServerActor;
2
+ use crate::server::handler_change::Change;
3
+ use actix::AsyncContext;
4
+ use bsnext_input::route_manifest::RouteChangeSet;
5
+
6
+ #[derive(actix::Message)]
7
+ #[rtype(result = "()")]
8
+ pub struct RoutesUpdated {
9
+ pub change_set: RouteChangeSet,
10
+ }
11
+
12
+ impl actix::Handler<RoutesUpdated> for ServerActor {
13
+ type Result = ();
14
+
15
+ fn handle(&mut self, msg: RoutesUpdated, ctx: &mut Self::Context) -> Self::Result {
16
+ tracing::trace!(changeset=?msg.change_set, id = ?self.config.identity);
17
+ for id in &msg.change_set.changed {
18
+ ctx.address().do_send(Change::fs(&id.path))
19
+ }
20
+ for id in &msg.change_set.removed {
21
+ ctx.address().do_send(Change::fs_removed(&id.path))
22
+ }
23
+ for id in &msg.change_set.added {
24
+ ctx.address().do_send(Change::fs_added(&id.path))
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,38 @@
1
+ use crate::server::actor::ServerActor;
2
+ use actix::ActorContext;
3
+ use std::future::Future;
4
+ use std::pin::Pin;
5
+
6
+ #[derive(actix::Message)]
7
+ #[rtype(result = "()")]
8
+ pub struct Stop;
9
+
10
+ impl actix::Handler<Stop> for ServerActor {
11
+ type Result = Pin<Box<dyn Future<Output = ()>>>;
12
+
13
+ fn handle(&mut self, _msg: Stop, ctx: &mut Self::Context) -> Self::Result {
14
+ tracing::trace!("{:?} [ServerActor::Stop2]", self.addr);
15
+
16
+ // don't accept any more messages
17
+ let Some(signals) = self.signals.take() else {
18
+ todo!("should be unreachable. close signal can only be sent once")
19
+ };
20
+ let Some(handle) = signals.axum_server_handle else {
21
+ todo!("cannot get here handle cannnot be absent can it??")
22
+ };
23
+ tracing::trace!("{:?} using handle to shutdown", self.config.identity);
24
+ handle.shutdown();
25
+ ctx.stop();
26
+ if let Some(complete_msg_receiver) = signals.complete_mdg_receiver {
27
+ tracing::debug!("{:?} confirmed closed via signal", self.addr);
28
+ Box::pin(async move {
29
+ match complete_msg_receiver.await {
30
+ Ok(_) => {}
31
+ Err(e) => tracing::error!("failed to get complete message {e}"),
32
+ }
33
+ })
34
+ } else {
35
+ todo!("cannot get here?")
36
+ }
37
+ }
38
+ }
@@ -0,0 +1,10 @@
1
+ pub mod actor;
2
+ pub mod error;
3
+ pub mod handler_change;
4
+ pub mod handler_listen;
5
+ pub mod handler_patch;
6
+ pub mod handler_routes_updated;
7
+ pub mod handler_stop;
8
+ pub mod router;
9
+ pub mod signals;
10
+ pub mod state;
@@ -0,0 +1,112 @@
1
+ use crate::panic_handler::handle_panic;
2
+
3
+ use axum::extract::{Request, State};
4
+ use axum::http::Uri;
5
+ use axum::middleware::{from_fn_with_state, Next};
6
+ use axum::response::IntoResponse;
7
+ use axum::routing::{get, MethodRouter};
8
+ use axum::{http, Extension, Router};
9
+
10
+ use axum::body::Body;
11
+ use http::header::CONTENT_TYPE;
12
+ use http::{HeaderValue, StatusCode};
13
+ use hyper_tls::HttpsConnector;
14
+ use hyper_util::client::legacy::connect::HttpConnector;
15
+ use hyper_util::client::legacy::Client;
16
+ use hyper_util::rt::TokioExecutor;
17
+ use mime_guess::mime;
18
+ use std::sync::Arc;
19
+ use std::time::Duration;
20
+ use tower::ServiceBuilder;
21
+ use tower_http::catch_panic::CatchPanicLayer;
22
+
23
+ use crate::dir_loader::serve_dir_loader;
24
+ use crate::meta::MetaData;
25
+ use crate::not_found::not_found_service::not_found_loader;
26
+ use crate::not_found::route_list::create_route_list_html;
27
+ use crate::raw_loader::raw_loader;
28
+ use crate::server::state::ServerState;
29
+ use crate::ws::ws_handler;
30
+ use tower_http::timeout::TimeoutLayer;
31
+ use tower_http::trace::TraceLayer;
32
+
33
+ mod tests;
34
+
35
+ pub fn make_router(state: &Arc<ServerState>) -> Router {
36
+ let https = HttpsConnector::new();
37
+ let client: Client<HttpsConnector<HttpConnector>, Body> =
38
+ Client::builder(TokioExecutor::new()).build(https);
39
+ let router = Router::new()
40
+ .merge(built_ins(state.clone()))
41
+ .merge(dynamic_loaders(state.clone()));
42
+ router
43
+ .layer(TraceLayer::new_for_http())
44
+ .layer(TimeoutLayer::new(Duration::from_secs(10)))
45
+ .layer(Extension(client))
46
+ }
47
+
48
+ pub fn built_ins(state: Arc<ServerState>) -> Router {
49
+ async fn handler(State(app): State<Arc<ServerState>>, _uri: Uri) -> impl IntoResponse {
50
+ // let v = app.lock().await;
51
+ let routes = app.routes.read().await;
52
+ let markup = create_route_list_html(&routes);
53
+ (
54
+ [(
55
+ CONTENT_TYPE,
56
+ HeaderValue::from_static(mime::TEXT_HTML_UTF_8.as_ref()),
57
+ )],
58
+ markup,
59
+ )
60
+ .into_response()
61
+ }
62
+ async fn js_handler(_uri: Uri) -> impl IntoResponse {
63
+ let markup = include_str!("../../../../bsnext_client/dist/index.js");
64
+ (
65
+ [(
66
+ CONTENT_TYPE,
67
+ HeaderValue::from_static(mime::APPLICATION_JAVASCRIPT_UTF_8.as_ref()),
68
+ )],
69
+ markup,
70
+ )
71
+ .into_response()
72
+ }
73
+
74
+ route("/__bsnext", get(handler))
75
+ .route("/__bs_js", get(js_handler))
76
+ .route("/__bs_ws", get(ws_handler))
77
+ .with_state(state.clone())
78
+ }
79
+
80
+ fn route(path: &str, method_router: MethodRouter<Arc<ServerState>>) -> Router<Arc<ServerState>> {
81
+ Router::new().route(path, method_router)
82
+ }
83
+
84
+ pub fn dynamic_loaders(state: Arc<ServerState>) -> Router {
85
+ Router::new()
86
+ .layer(
87
+ ServiceBuilder::new()
88
+ .layer(from_fn_with_state(state.clone(), tagging_layer))
89
+ .layer(from_fn_with_state(state.clone(), not_found_loader))
90
+ .layer(from_fn_with_state(state.clone(), raw_loader))
91
+ .layer(from_fn_with_state(state.clone(), serve_dir_loader)),
92
+ )
93
+ .layer(CatchPanicLayer::custom(handle_panic))
94
+ .with_state(state.clone())
95
+ }
96
+
97
+ async fn tagging_layer(
98
+ req: Request,
99
+ next: Next,
100
+ ) -> Result<impl IntoResponse, (StatusCode, String)> {
101
+ let r = next.run(req).await;
102
+ if let Some(_metadata) = r.extensions().get::<MetaData>() {
103
+ // todo:
104
+ }
105
+ Ok(r)
106
+ }
107
+
108
+ #[derive(serde::Deserialize, Debug)]
109
+ pub enum Encoding {
110
+ Br,
111
+ Zip,
112
+ }
@@ -0,0 +1,204 @@
1
+ #[cfg(test)]
2
+ mod test {
3
+
4
+ use crate::dto::ClientEvent;
5
+ use crate::server::router::make_router;
6
+ use crate::server::state::ServerState;
7
+ use axum::body::Body;
8
+ use axum::extract::Request;
9
+ use axum::response::Response;
10
+ use bsnext_input::route::{CorsOpts, Route, RouteKind};
11
+ use bsnext_input::server_config::{Identity, ServerConfig};
12
+ use http::HeaderValue;
13
+ use std::sync::Arc;
14
+ use tokio::sync::RwLock;
15
+ use tower::ServiceExt;
16
+
17
+ impl From<ServerConfig> for ServerState {
18
+ fn from(val: ServerConfig) -> ServerState {
19
+ let (sender, _) = tokio::sync::broadcast::channel::<ClientEvent>(10);
20
+ ServerState {
21
+ routes: Arc::new(RwLock::new(val.routes.clone())),
22
+ client_sender: Arc::new(sender),
23
+ }
24
+ }
25
+ }
26
+
27
+ async fn to_resp_body(res: Response) -> String {
28
+ use http_body_util::BodyExt;
29
+ let (_parts, body) = res.into_parts();
30
+ let b = body.collect().await.unwrap();
31
+ let b = b.to_bytes();
32
+ let as_str = std::str::from_utf8(&b).unwrap();
33
+ as_str.to_owned()
34
+ }
35
+
36
+ #[tokio::test]
37
+ async fn test_handlers() -> Result<(), anyhow::Error> {
38
+ let state: ServerState = ServerConfig {
39
+ identity: Identity::Address {
40
+ bind_address: "127.0.0.1:3000".to_string(),
41
+ },
42
+ routes: vec![Route {
43
+ path: "/hello".to_string(),
44
+ kind: RouteKind::html("🐥"),
45
+ ..Default::default()
46
+ }],
47
+ ..Default::default()
48
+ }
49
+ .into();
50
+
51
+ let app = make_router(&Arc::new(state));
52
+ let req = Request::get("/hello").body(Body::empty()).unwrap();
53
+ let res = app.oneshot(req).await.unwrap();
54
+
55
+ assert_eq!(res.headers().get("content-length").unwrap(), "4");
56
+ assert_eq!(
57
+ res.headers().get("content-type").unwrap(),
58
+ "text/html; charset=utf-8"
59
+ );
60
+
61
+ let body = to_resp_body(res).await;
62
+ assert_eq!(body, "🐥");
63
+ Ok(())
64
+ }
65
+
66
+ #[tokio::test]
67
+ async fn test_handlers_raw() -> Result<(), anyhow::Error> {
68
+ let state: ServerState = ServerConfig {
69
+ identity: Identity::Address {
70
+ bind_address: "127.0.0.1".to_string(),
71
+ },
72
+ routes: vec![Route {
73
+ path: "/styles.css".to_string(),
74
+ kind: RouteKind::Raw {
75
+ raw: "body{}".into(),
76
+ },
77
+ ..Default::default()
78
+ }],
79
+ ..Default::default()
80
+ }
81
+ .into();
82
+
83
+ let app = make_router(&Arc::new(state));
84
+ let req = Request::get("/styles.css").body(Body::empty()).unwrap();
85
+ let res = app.oneshot(req).await.unwrap();
86
+
87
+ assert_eq!(res.headers().get("content-length").unwrap(), "6");
88
+ assert_eq!(res.headers().get("content-type").unwrap(), "text/css");
89
+
90
+ let body = to_resp_body(res).await;
91
+ assert_eq!(body, "body{}");
92
+ Ok(())
93
+ }
94
+ #[tokio::test]
95
+ async fn test_cors_handlers() -> Result<(), anyhow::Error> {
96
+ let state: ServerState = ServerConfig {
97
+ identity: Identity::Address {
98
+ bind_address: "127.0.0.1:3000".to_string(),
99
+ },
100
+ routes: vec![
101
+ Route {
102
+ path: "/".to_string(),
103
+ cors_opts: Some(CorsOpts::Cors(true)),
104
+ kind: RouteKind::html("home"),
105
+ ..Default::default()
106
+ },
107
+ Route {
108
+ path: "/hello".to_string(),
109
+ kind: RouteKind::html("🐥"),
110
+ ..Default::default()
111
+ },
112
+ ],
113
+ ..Default::default()
114
+ }
115
+ .into();
116
+
117
+ let app = make_router(&Arc::new(state));
118
+ let req = Request::get("/").body(Body::empty()).unwrap();
119
+ let res = app.oneshot(req).await.unwrap();
120
+ let h = res.headers().get("access-control-allow-origin");
121
+ let v = res.headers().get("vary");
122
+
123
+ assert_eq!(h, Some(HeaderValue::from_str("*").as_ref().unwrap()));
124
+ assert_eq!(
125
+ v,
126
+ Some(
127
+ HeaderValue::from_str(
128
+ "origin, access-control-request-method, access-control-request-headers"
129
+ )
130
+ .as_ref()
131
+ .unwrap()
132
+ )
133
+ );
134
+
135
+ let body = to_resp_body(res).await;
136
+ assert_eq!(body, "home");
137
+ Ok(())
138
+ }
139
+ #[tokio::test]
140
+ async fn test_not_found_handler() -> Result<(), anyhow::Error> {
141
+ let state: ServerState = ServerConfig {
142
+ identity: Identity::Address {
143
+ bind_address: "127.0.0.1:3000".to_string(),
144
+ },
145
+ routes: vec![Route {
146
+ path: "/".to_string(),
147
+ cors_opts: Some(CorsOpts::Cors(true)),
148
+ kind: RouteKind::html("home"),
149
+ ..Default::default()
150
+ }],
151
+ ..Default::default()
152
+ }
153
+ .into();
154
+
155
+ let app = make_router(&Arc::new(state));
156
+ let req = Request::get("/abc").body(Body::empty()).unwrap();
157
+ let res = app.oneshot(req).await.unwrap();
158
+ let status = res.status().as_u16();
159
+
160
+ assert_eq!(
161
+ res.headers().get("content-type").unwrap(),
162
+ "text/html; charset=utf-8"
163
+ );
164
+
165
+ let body = to_resp_body(res).await;
166
+
167
+ assert!(body.contains("<title>BSNEXT</title>"));
168
+ assert_eq!(status, 404);
169
+ Ok(())
170
+ }
171
+ #[tokio::test]
172
+ async fn test_route_list() -> Result<(), anyhow::Error> {
173
+ let state: ServerState = ServerConfig {
174
+ identity: Identity::Address {
175
+ bind_address: "127.0.0.1:3000".to_string(),
176
+ },
177
+ routes: vec![Route {
178
+ path: "/abc".to_string(),
179
+ cors_opts: Some(CorsOpts::Cors(true)),
180
+ kind: RouteKind::html("home"),
181
+ ..Default::default()
182
+ }],
183
+ watchers: vec![],
184
+ }
185
+ .into();
186
+
187
+ let app = make_router(&Arc::new(state));
188
+ let req = Request::get("/__bsnext").body(Body::empty()).unwrap();
189
+ let res = app.oneshot(req).await.unwrap();
190
+ let status = res.status().as_u16();
191
+
192
+ assert_eq!(
193
+ res.headers().get("content-type").unwrap(),
194
+ "text/html; charset=utf-8"
195
+ );
196
+
197
+ let body = to_resp_body(res).await;
198
+
199
+ assert!(body.contains("<title>BSNEXT</title>"));
200
+ assert!(body.contains("<code>/abc</code>"));
201
+ assert_eq!(status, 200);
202
+ Ok(())
203
+ }
204
+ }
@@ -0,0 +1,11 @@
1
+ use crate::dto::ClientEvent;
2
+ use axum_server::Handle;
3
+ use tokio::sync::broadcast;
4
+ use tokio::sync::oneshot::Receiver;
5
+
6
+ pub struct ServerSignals {
7
+ pub complete_mdg_receiver: Option<Receiver<()>>,
8
+ pub axum_server_handle: Option<Handle>,
9
+ pub client_sender: Option<broadcast::Sender<ClientEvent>>,
10
+ pub client_receiver: Option<broadcast::Receiver<ClientEvent>>,
11
+ }