@browsersync/bslive 0.0.7 → 0.0.13

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 (155) hide show
  1. package/Cargo.toml +31 -2
  2. package/bin.js +3 -2
  3. package/bslive/Cargo.toml +16 -1
  4. package/bslive/build.rs +1 -1
  5. package/bslive/src/lib.rs +125 -4
  6. package/bsnext/Cargo.toml +21 -0
  7. package/bsnext/src/main.rs +116 -0
  8. package/crates/bsnext_client/Cargo.toml +8 -0
  9. package/crates/bsnext_client/build.rs +53 -0
  10. package/crates/bsnext_client/generated/dto.ts +147 -0
  11. package/crates/bsnext_client/generated/schema.ts +237 -0
  12. package/crates/bsnext_client/inject/dist/index.js +6540 -0
  13. package/crates/bsnext_client/inject/package.json +12 -0
  14. package/crates/bsnext_client/inject/src/console.ts +25 -0
  15. package/crates/bsnext_client/inject/src/index.ts +73 -0
  16. package/crates/bsnext_client/package-lock.json +2145 -0
  17. package/crates/bsnext_client/package.json +27 -0
  18. package/crates/bsnext_client/src/lib.rs +11 -0
  19. package/crates/bsnext_client/tsconfig.json +22 -0
  20. package/crates/bsnext_client/ui/dist/index.css +78 -0
  21. package/crates/bsnext_client/ui/dist/index.js +997 -0
  22. package/crates/bsnext_client/ui/index.html +18 -0
  23. package/crates/bsnext_client/ui/input.yml +19 -0
  24. package/crates/bsnext_client/ui/package.json +18 -0
  25. package/crates/bsnext_client/ui/src/components/bs-debug.ts +27 -0
  26. package/crates/bsnext_client/ui/src/components/bs-header.ts +33 -0
  27. package/crates/bsnext_client/ui/src/components/bs-icon.ts +101 -0
  28. package/crates/bsnext_client/ui/src/components/bs-server-detail.ts +21 -0
  29. package/crates/bsnext_client/ui/src/components/bs-server-identity.ts +24 -0
  30. package/crates/bsnext_client/ui/src/components/bs-server-list.ts +39 -0
  31. package/crates/bsnext_client/ui/src/index.ts +39 -0
  32. package/crates/bsnext_client/ui/styles/base.css.ts +17 -0
  33. package/crates/bsnext_client/ui/styles/reset.css +52 -0
  34. package/crates/bsnext_client/ui/styles/style.css +29 -0
  35. package/crates/bsnext_client/ui/svg/wordmark-white.svg +38 -0
  36. package/crates/bsnext_core/Cargo.toml +44 -0
  37. package/crates/bsnext_core/src/common_layers.rs +62 -0
  38. package/crates/bsnext_core/src/dir_loader.rs +230 -0
  39. package/crates/bsnext_core/src/dto.rs +341 -0
  40. package/crates/bsnext_core/src/handlers/mod.rs +1 -0
  41. package/crates/bsnext_core/src/handlers/proxy.rs +95 -0
  42. package/crates/bsnext_core/src/lib.rs +12 -0
  43. package/crates/bsnext_core/src/meta/mod.rs +5 -0
  44. package/crates/bsnext_core/src/not_found/mod.rs +1 -0
  45. package/crates/bsnext_core/src/not_found/not_found_service.rs +35 -0
  46. package/crates/bsnext_core/src/panic_handler.rs +32 -0
  47. package/crates/bsnext_core/src/raw_loader.rs +196 -0
  48. package/crates/bsnext_core/src/server/actor.rs +92 -0
  49. package/crates/bsnext_core/src/server/error.rs +41 -0
  50. package/crates/bsnext_core/src/server/handler_change.rs +85 -0
  51. package/crates/bsnext_core/src/server/handler_listen.rs +163 -0
  52. package/crates/bsnext_core/src/server/handler_patch.rs +42 -0
  53. package/crates/bsnext_core/src/server/handler_routes_updated.rs +27 -0
  54. package/crates/bsnext_core/src/server/handler_stop.rs +38 -0
  55. package/crates/bsnext_core/src/server/mod.rs +10 -0
  56. package/crates/bsnext_core/src/server/router/assets.rs +39 -0
  57. package/crates/bsnext_core/src/server/router/mod.rs +123 -0
  58. package/crates/bsnext_core/src/server/router/pub_api.rs +39 -0
  59. package/crates/bsnext_core/src/server/router/snapshots/bsnext_core__server__router__tests__test__handlers.snap +9 -0
  60. package/crates/bsnext_core/src/server/router/tests.rs +209 -0
  61. package/crates/bsnext_core/src/server/signals.rs +11 -0
  62. package/crates/bsnext_core/src/server/state.rs +23 -0
  63. package/crates/bsnext_core/src/servers_supervisor/actor.rs +199 -0
  64. package/crates/bsnext_core/src/servers_supervisor/file_changed_handler.rs +41 -0
  65. package/crates/bsnext_core/src/servers_supervisor/get_servers_handler.rs +24 -0
  66. package/crates/bsnext_core/src/servers_supervisor/input_changed_handler.rs +21 -0
  67. package/crates/bsnext_core/src/servers_supervisor/mod.rs +6 -0
  68. package/crates/bsnext_core/src/servers_supervisor/start_handler.rs +86 -0
  69. package/crates/bsnext_core/src/servers_supervisor/stop_handler.rs +26 -0
  70. package/crates/bsnext_core/src/ws/mod.rs +164 -0
  71. package/crates/bsnext_example/Cargo.toml +10 -0
  72. package/crates/bsnext_example/src/basic.rs +51 -0
  73. package/crates/bsnext_example/src/lib.rs +26 -0
  74. package/crates/bsnext_example/src/lit.rs +37 -0
  75. package/crates/bsnext_example/src/md.rs +18 -0
  76. package/crates/bsnext_fs/Cargo.toml +22 -0
  77. package/crates/bsnext_fs/src/actor.rs +123 -0
  78. package/crates/bsnext_fs/src/buffered_debounce.rs +166 -0
  79. package/crates/bsnext_fs/src/filter.rs +39 -0
  80. package/crates/bsnext_fs/src/inner_fs_event_handler.rs +94 -0
  81. package/crates/bsnext_fs/src/lib.rs +141 -0
  82. package/crates/bsnext_fs/src/remove_path_handler.rs +46 -0
  83. package/crates/bsnext_fs/src/stop_handler.rs +15 -0
  84. package/crates/bsnext_fs/src/stream.rs +167 -0
  85. package/crates/bsnext_fs/src/test/mod.rs +213 -0
  86. package/crates/bsnext_fs/src/watch_path_handler.rs +67 -0
  87. package/crates/bsnext_fs/src/watcher.rs +349 -0
  88. package/crates/bsnext_input/Cargo.toml +25 -0
  89. package/crates/bsnext_input/src/input_test/mod.rs +185 -0
  90. package/crates/bsnext_input/src/input_test/snapshots/bsnext_input__input_test__deserialize_3_headers.snap +29 -0
  91. package/crates/bsnext_input/src/input_test/snapshots/bsnext_input__input_test__deserialize_3_headers_control.snap +25 -0
  92. package/crates/bsnext_input/src/lib.rs +153 -0
  93. package/crates/bsnext_input/src/md.rs +541 -0
  94. package/crates/bsnext_input/src/paths.rs +64 -0
  95. package/crates/bsnext_input/src/route.rs +189 -0
  96. package/crates/bsnext_input/src/route_manifest.rs +186 -0
  97. package/crates/bsnext_input/src/server_config.rs +88 -0
  98. package/crates/bsnext_input/src/target.rs +7 -0
  99. package/crates/bsnext_input/src/watch_opt_test/mod.rs +68 -0
  100. package/crates/bsnext_input/src/yml.rs +1 -0
  101. package/crates/bsnext_output/Cargo.toml +16 -0
  102. package/crates/bsnext_output/src/json.rs +11 -0
  103. package/crates/bsnext_output/src/lib.rs +25 -0
  104. package/crates/bsnext_output/src/pretty.rs +147 -0
  105. package/crates/bsnext_resp/Cargo.toml +13 -0
  106. package/crates/bsnext_resp/src/js/snippet.html +1 -0
  107. package/crates/bsnext_resp/src/js/ws.js +1 -0
  108. package/crates/bsnext_resp/src/lib.rs +79 -0
  109. package/crates/bsnext_system/Cargo.toml +29 -0
  110. package/crates/bsnext_system/src/args.rs +47 -0
  111. package/crates/bsnext_system/src/lib.rs +227 -0
  112. package/crates/bsnext_system/src/monitor.rs +241 -0
  113. package/crates/bsnext_system/src/monitor_any_watchables.rs +133 -0
  114. package/crates/bsnext_system/src/start_kind/snapshots/bsnext_system__start_kind__start_from_paths__test__test-2.snap +12 -0
  115. package/crates/bsnext_system/src/start_kind/snapshots/bsnext_system__start_kind__start_from_paths__test__test.snap +30 -0
  116. package/crates/bsnext_system/src/start_kind/start_from_example.rs +49 -0
  117. package/crates/bsnext_system/src/start_kind/start_from_inputs.rs +67 -0
  118. package/crates/bsnext_system/src/start_kind/start_from_paths.rs +51 -0
  119. package/crates/bsnext_system/src/start_kind.rs +56 -0
  120. package/crates/bsnext_system/src/startup.rs +51 -0
  121. package/crates/bsnext_tracing/Cargo.toml +10 -0
  122. package/crates/bsnext_tracing/src/lib.rs +127 -0
  123. package/examples/basic/input.yml +5 -0
  124. package/examples/basic/public/index.html +15 -0
  125. package/examples/basic/public/reset.css +52 -0
  126. package/examples/basic/public/script.js +1 -0
  127. package/examples/basic/public/styles.css +3 -0
  128. package/examples/kitchen-sink/a-file.md +1 -0
  129. package/examples/kitchen-sink/api/1.json +1 -0
  130. package/examples/kitchen-sink/api/2.json +3 -0
  131. package/examples/kitchen-sink/app.js +1 -0
  132. package/examples/kitchen-sink/input.html +185 -0
  133. package/examples/kitchen-sink/input.yml +133 -0
  134. package/examples/kitchen-sink/styles-2.css +3 -0
  135. package/examples/lit/index.html +21 -0
  136. package/examples/lit/input.yml +5 -0
  137. package/examples/lit/lit.js +82 -0
  138. package/examples/lit/package-lock.json +62 -0
  139. package/examples/lit/package.json +15 -0
  140. package/examples/md-single/frontmatter.md +35 -0
  141. package/examples/md-single/md-single.md +35 -0
  142. package/examples/openai/.nvm +1 -0
  143. package/examples/openai/build.mjs +21 -0
  144. package/examples/openai/index.html +13 -0
  145. package/examples/openai/input.yml +59 -0
  146. package/examples/openai/package-lock.json +720 -0
  147. package/examples/openai/package.json +21 -0
  148. package/examples/openai/src/index.js +21 -0
  149. package/examples/openai/sse/01.txt +8 -0
  150. package/examples/proxy-simple/input.yml +9 -0
  151. package/examples/single/input.yaml +26 -0
  152. package/index.d.ts +2 -0
  153. package/index.js +2 -1
  154. package/package.json +17 -17
  155. package/run.sh +6 -0
@@ -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,39 @@
1
+ use crate::server::state::ServerState;
2
+ use axum::response::IntoResponse;
3
+ use axum::routing::get;
4
+ use axum::Router;
5
+ use bsnext_client::{UI_CSS, UI_JS};
6
+ use http::header::CONTENT_TYPE;
7
+ use http::HeaderValue;
8
+ use mime_guess::mime;
9
+ use std::sync::Arc;
10
+
11
+ pub fn pub_ui_assets(_: Arc<ServerState>) -> Router<Arc<ServerState>> {
12
+ Router::new()
13
+ .route(
14
+ "/dist/index.js",
15
+ get(|| async {
16
+ (
17
+ [(
18
+ CONTENT_TYPE,
19
+ HeaderValue::from_static(mime::APPLICATION_JAVASCRIPT.as_ref()),
20
+ )],
21
+ UI_JS,
22
+ )
23
+ .into_response()
24
+ }),
25
+ )
26
+ .route(
27
+ "/dist/index.css",
28
+ get(|| async {
29
+ (
30
+ [(
31
+ CONTENT_TYPE,
32
+ HeaderValue::from_static(mime::TEXT_CSS.as_ref()),
33
+ )],
34
+ UI_CSS,
35
+ )
36
+ .into_response()
37
+ }),
38
+ )
39
+ }
@@ -0,0 +1,123 @@
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::dto::{RouteDTO, ServerDesc};
25
+ use crate::meta::MetaData;
26
+ use crate::not_found::not_found_service::not_found_loader;
27
+ use crate::raw_loader::raw_loader;
28
+ use crate::server::router::assets::pub_ui_assets;
29
+ use crate::server::router::pub_api::pub_api;
30
+ use crate::server::state::ServerState;
31
+ use crate::ws::ws_handler;
32
+ use bsnext_client::html_with_base;
33
+ use tower_http::timeout::TimeoutLayer;
34
+ use tower_http::trace::TraceLayer;
35
+
36
+ mod assets;
37
+ mod pub_api;
38
+ mod tests;
39
+
40
+ pub fn make_router(state: &Arc<ServerState>) -> Router {
41
+ let https = HttpsConnector::new();
42
+ let client: Client<HttpsConnector<HttpConnector>, Body> =
43
+ Client::builder(TokioExecutor::new()).build(https);
44
+ let router = Router::new()
45
+ .merge(built_ins(state.clone()))
46
+ .merge(dynamic_loaders(state.clone()));
47
+ router
48
+ .layer(TraceLayer::new_for_http())
49
+ .layer(TimeoutLayer::new(Duration::from_secs(10)))
50
+ .layer(Extension(client))
51
+ }
52
+
53
+ pub fn built_ins(state: Arc<ServerState>) -> Router {
54
+ async fn handler(State(app): State<Arc<ServerState>>, _uri: Uri) -> impl IntoResponse {
55
+ // let v = app.lock().await;
56
+ let routes = app.routes.read().await;
57
+ let _dto = ServerDesc {
58
+ routes: routes.iter().map(RouteDTO::from).collect(),
59
+ id: app.id.to_string(),
60
+ };
61
+ let markup = html_with_base("/__bs_assets/ui/");
62
+ (
63
+ [(
64
+ CONTENT_TYPE,
65
+ HeaderValue::from_static(mime::TEXT_HTML_UTF_8.as_ref()),
66
+ )],
67
+ markup,
68
+ )
69
+ .into_response()
70
+ }
71
+ async fn js_handler(_uri: Uri) -> impl IntoResponse {
72
+ let markup = include_str!("../../../../bsnext_client/inject/dist/index.js");
73
+ (
74
+ [(
75
+ CONTENT_TYPE,
76
+ HeaderValue::from_static(mime::APPLICATION_JAVASCRIPT_UTF_8.as_ref()),
77
+ )],
78
+ markup,
79
+ )
80
+ .into_response()
81
+ }
82
+
83
+ route("/__bslive", get(handler))
84
+ .route("/__bs_js", get(js_handler))
85
+ .nest("/__bs_api", pub_api(state.clone()))
86
+ .nest("/__bs_assets/ui", pub_ui_assets(state.clone()))
87
+ .route("/__bs_ws", get(ws_handler))
88
+ .with_state(state.clone())
89
+ }
90
+
91
+ fn route(path: &str, method_router: MethodRouter<Arc<ServerState>>) -> Router<Arc<ServerState>> {
92
+ Router::new().route(path, method_router)
93
+ }
94
+
95
+ pub fn dynamic_loaders(state: Arc<ServerState>) -> Router {
96
+ Router::new()
97
+ .layer(
98
+ ServiceBuilder::new()
99
+ .layer(from_fn_with_state(state.clone(), tagging_layer))
100
+ .layer(from_fn_with_state(state.clone(), not_found_loader))
101
+ .layer(from_fn_with_state(state.clone(), raw_loader))
102
+ .layer(from_fn_with_state(state.clone(), serve_dir_loader)),
103
+ )
104
+ .layer(CatchPanicLayer::custom(handle_panic))
105
+ .with_state(state.clone())
106
+ }
107
+
108
+ async fn tagging_layer(
109
+ req: Request,
110
+ next: Next,
111
+ ) -> Result<impl IntoResponse, (StatusCode, String)> {
112
+ let r = next.run(req).await;
113
+ if let Some(_metadata) = r.extensions().get::<MetaData>() {
114
+ // todo:
115
+ }
116
+ Ok(r)
117
+ }
118
+
119
+ #[derive(serde::Deserialize, Debug)]
120
+ pub enum Encoding {
121
+ Br,
122
+ Zip,
123
+ }
@@ -0,0 +1,39 @@
1
+ use crate::dto::{RouteDTO, ServerDesc};
2
+ use crate::server::state::ServerState;
3
+ use crate::servers_supervisor::get_servers_handler::GetServersMessage;
4
+ use axum::extract::State;
5
+ use axum::response::IntoResponse;
6
+ use axum::routing::get;
7
+ use axum::{Json, Router};
8
+ use http::{StatusCode, Uri};
9
+ use std::sync::Arc;
10
+
11
+ async fn all_servers_handler(State(app): State<Arc<ServerState>>, _uri: Uri) -> impl IntoResponse {
12
+ match &app.parent {
13
+ None => unreachable!("cannot get here"),
14
+ Some(parent) => match parent.send(GetServersMessage).await {
15
+ Ok(servers) => Json(servers).into_response(),
16
+ Err(err) => (
17
+ StatusCode::INTERNAL_SERVER_ERROR,
18
+ format!("Could not fetch servers: {}", err),
19
+ )
20
+ .into_response(),
21
+ },
22
+ }
23
+ }
24
+
25
+ async fn get_current_server(State(app): State<Arc<ServerState>>, _uri: Uri) -> impl IntoResponse {
26
+ let routes = app.routes.read().await;
27
+ let dto = ServerDesc {
28
+ routes: routes.iter().map(RouteDTO::from).collect(),
29
+ id: app.id.to_string(),
30
+ };
31
+ Json(dto)
32
+ }
33
+
34
+ pub fn pub_api(state: Arc<ServerState>) -> Router<Arc<ServerState>> {
35
+ Router::new()
36
+ .route("/servers", get(all_servers_handler))
37
+ .route("/me", get(get_current_server))
38
+ .with_state(state.clone())
39
+ }
@@ -0,0 +1,9 @@
1
+ ---
2
+ source: crates/bsnext_core/src/server/router/tests.rs
3
+ expression: res.headers()
4
+ ---
5
+ {
6
+ "content-type": "text/html; charset=utf-8",
7
+ "content-length": "4",
8
+ "a": "b",
9
+ }
@@ -0,0 +1,209 @@
1
+ #[cfg(test)]
2
+ mod test {
3
+ use crate::dto::ClientEvent;
4
+ use crate::server::router::make_router;
5
+ use crate::server::state::ServerState;
6
+ use axum::body::Body;
7
+ use axum::extract::Request;
8
+ use axum::response::Response;
9
+ use bsnext_input::route::{CorsOpts, Route, RouteKind};
10
+ use bsnext_input::server_config::{Identity, ServerConfig};
11
+ use http::HeaderValue;
12
+ use std::collections::BTreeMap;
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
+ id: val.identity.as_id(),
23
+ parent: None,
24
+ client_sender: Arc::new(sender),
25
+ }
26
+ }
27
+ }
28
+
29
+ async fn to_resp_body(res: Response) -> String {
30
+ use http_body_util::BodyExt;
31
+ let (_parts, body) = res.into_parts();
32
+ let b = body.collect().await.unwrap();
33
+ let b = b.to_bytes();
34
+ let as_str = std::str::from_utf8(&b).unwrap();
35
+ as_str.to_owned()
36
+ }
37
+
38
+ #[tokio::test]
39
+ async fn test_handlers() -> Result<(), anyhow::Error> {
40
+ let headers: BTreeMap<String, String> =
41
+ [("a".to_string(), "b".to_string())].into_iter().collect();
42
+ let state: ServerState = ServerConfig {
43
+ identity: Identity::Address {
44
+ bind_address: "127.0.0.1:3000".to_string(),
45
+ },
46
+ routes: vec![Route {
47
+ path: "/hello".to_string(),
48
+ kind: RouteKind::html("🐥"),
49
+ headers: Some(headers),
50
+ ..Default::default()
51
+ }],
52
+ ..Default::default()
53
+ }
54
+ .into();
55
+
56
+ let app = make_router(&Arc::new(state));
57
+ let req = Request::get("/hello").body(Body::empty()).unwrap();
58
+ let res = app.oneshot(req).await.unwrap();
59
+
60
+ insta::assert_debug_snapshot!(res.headers());
61
+ assert_eq!(
62
+ res.headers().get("content-type").unwrap(),
63
+ "text/html; charset=utf-8"
64
+ );
65
+
66
+ let body = to_resp_body(res).await;
67
+ assert_eq!(body, "🐥");
68
+ Ok(())
69
+ }
70
+
71
+ #[tokio::test]
72
+ async fn test_handlers_raw() -> Result<(), anyhow::Error> {
73
+ let state: ServerState = ServerConfig {
74
+ identity: Identity::Address {
75
+ bind_address: "127.0.0.1".to_string(),
76
+ },
77
+ routes: vec![Route {
78
+ path: "/styles.css".to_string(),
79
+ kind: RouteKind::Raw {
80
+ raw: "body{}".into(),
81
+ },
82
+ ..Default::default()
83
+ }],
84
+ ..Default::default()
85
+ }
86
+ .into();
87
+
88
+ let app = make_router(&Arc::new(state));
89
+ let req = Request::get("/styles.css").body(Body::empty()).unwrap();
90
+ let res = app.oneshot(req).await.unwrap();
91
+
92
+ assert_eq!(res.headers().get("content-length").unwrap(), "6");
93
+ assert_eq!(res.headers().get("content-type").unwrap(), "text/css");
94
+
95
+ let body = to_resp_body(res).await;
96
+ assert_eq!(body, "body{}");
97
+ Ok(())
98
+ }
99
+ #[tokio::test]
100
+ async fn test_cors_handlers() -> Result<(), anyhow::Error> {
101
+ let state: ServerState = ServerConfig {
102
+ identity: Identity::Address {
103
+ bind_address: "127.0.0.1:3000".to_string(),
104
+ },
105
+ routes: vec![
106
+ Route {
107
+ path: "/".to_string(),
108
+ cors_opts: Some(CorsOpts::Cors(true)),
109
+ kind: RouteKind::html("home"),
110
+ ..Default::default()
111
+ },
112
+ Route {
113
+ path: "/hello".to_string(),
114
+ kind: RouteKind::html("🐥"),
115
+ ..Default::default()
116
+ },
117
+ ],
118
+ ..Default::default()
119
+ }
120
+ .into();
121
+
122
+ let app = make_router(&Arc::new(state));
123
+ let req = Request::get("/").body(Body::empty()).unwrap();
124
+ let res = app.oneshot(req).await.unwrap();
125
+ let h = res.headers().get("access-control-allow-origin");
126
+ let v = res.headers().get("vary");
127
+
128
+ assert_eq!(h, Some(HeaderValue::from_str("*").as_ref().unwrap()));
129
+ assert_eq!(
130
+ v,
131
+ Some(
132
+ HeaderValue::from_str(
133
+ "origin, access-control-request-method, access-control-request-headers"
134
+ )
135
+ .as_ref()
136
+ .unwrap()
137
+ )
138
+ );
139
+
140
+ let body = to_resp_body(res).await;
141
+ assert_eq!(body, "home");
142
+ Ok(())
143
+ }
144
+ #[tokio::test]
145
+ async fn test_not_found_handler() -> Result<(), anyhow::Error> {
146
+ let state: ServerState = ServerConfig {
147
+ identity: Identity::Address {
148
+ bind_address: "127.0.0.1:3000".to_string(),
149
+ },
150
+ routes: vec![Route {
151
+ path: "/".to_string(),
152
+ cors_opts: Some(CorsOpts::Cors(true)),
153
+ kind: RouteKind::html("home"),
154
+ ..Default::default()
155
+ }],
156
+ ..Default::default()
157
+ }
158
+ .into();
159
+
160
+ let app = make_router(&Arc::new(state));
161
+ let req = Request::get("/abc").body(Body::empty()).unwrap();
162
+ let res = app.oneshot(req).await.unwrap();
163
+ let status = res.status().as_u16();
164
+
165
+ assert_eq!(
166
+ res.headers().get("content-type").unwrap(),
167
+ "text/html; charset=utf-8"
168
+ );
169
+
170
+ let body = to_resp_body(res).await;
171
+
172
+ assert!(body.contains("<title>Browsersync LIVE</title>"));
173
+ assert_eq!(status, 404);
174
+ Ok(())
175
+ }
176
+ #[tokio::test]
177
+ async fn test_route_list() -> Result<(), anyhow::Error> {
178
+ let state: ServerState = ServerConfig {
179
+ identity: Identity::Address {
180
+ bind_address: "127.0.0.1:3000".to_string(),
181
+ },
182
+ routes: vec![Route {
183
+ path: "/abc".to_string(),
184
+ cors_opts: Some(CorsOpts::Cors(true)),
185
+ kind: RouteKind::html("home"),
186
+ ..Default::default()
187
+ }],
188
+ watchers: vec![],
189
+ }
190
+ .into();
191
+
192
+ let app = make_router(&Arc::new(state));
193
+ let req = Request::get("/__bslive").body(Body::empty()).unwrap();
194
+ let res = app.oneshot(req).await.unwrap();
195
+ let status = res.status().as_u16();
196
+
197
+ assert_eq!(
198
+ res.headers().get("content-type").unwrap(),
199
+ "text/html; charset=utf-8"
200
+ );
201
+
202
+ let body = to_resp_body(res).await;
203
+
204
+ assert!(body.contains("<title>Browsersync LIVE</title>"));
205
+ assert!(body.contains("<base href=\"/__bs_assets/ui/\" />"));
206
+ assert_eq!(status, 200);
207
+ Ok(())
208
+ }
209
+ }
@@ -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
+ }
@@ -0,0 +1,23 @@
1
+ use crate::dto::ClientEvent;
2
+ use crate::servers_supervisor::actor::ServersSupervisor;
3
+ use actix::Addr;
4
+ use bsnext_input::route::Route;
5
+ use std::fmt::Formatter;
6
+ use std::sync::Arc;
7
+ use tokio::sync::{broadcast, RwLock};
8
+
9
+ #[derive(Clone)]
10
+ pub struct ServerState {
11
+ pub routes: Arc<RwLock<Vec<Route>>>,
12
+ pub id: u64,
13
+ pub parent: Option<Addr<ServersSupervisor>>,
14
+ pub client_sender: Arc<broadcast::Sender<ClientEvent>>,
15
+ }
16
+
17
+ impl std::fmt::Debug for ServerState {
18
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
19
+ f.debug_struct("AppState")
20
+ .field("routes", &"Arc<RwLock<Vec<Route>>>")
21
+ .finish()
22
+ }
23
+ }