diff --git a/src/engine.rs b/src/engine.rs index c5ffcccd..1b7098c6 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -374,9 +374,7 @@ impl Engine { }; let res = xs::nu::add_core_commands(&mut xe, store) .and_then(|()| xs::nu::add_read_commands(&mut xe, store, xs::nu::ReadMode::Stream)) - .and_then(|()| { - xs::nu::add_write_commands(&mut xe, store, xs::nu::AppendMode::Direct) - }); + .and_then(|()| xs::nu::add_write_commands(&mut xe, store, xs::nu::AppendMode::Direct)); self.state = xe.state; res.map_err(|e| Error::from(e.to_string())) } diff --git a/src/main.rs b/src/main.rs index d6f85379..be2443a5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -660,7 +660,7 @@ async fn main() -> Result<(), Box> { // Create cross.stream store if --store is specified #[cfg(feature = "cross-stream")] - let store = match args.store { + let mut store = match args.store { Some(ref path) => { match Store::init(path.clone(), args.services, args.expose.clone()).await { Ok(store) => Some(store), @@ -673,7 +673,7 @@ async fn main() -> Result<(), Box> { None => None, }; #[cfg(not(feature = "cross-stream"))] - let store: Option = None; + let mut store: Option = None; // Build $HTTP_NU options from CLI args let http_nu_options = HttpNuOptions { @@ -794,6 +794,10 @@ async fn main() -> Result<(), Box> { ) .await?; + if let Some(store) = &mut store { + store.shutdown().await; + } + shutdown(); log_handle.join().ok(); Ok(()) diff --git a/src/store.rs b/src/store.rs index cb0a8a44..a131c6f5 100644 --- a/src/store.rs +++ b/src/store.rs @@ -8,6 +8,7 @@ use tokio::sync::mpsc; pub struct Store { inner: xs::store::Store, path: std::path::PathBuf, + service_handle: Option>, } #[cfg(not(feature = "cross-stream"))] @@ -21,7 +22,11 @@ pub struct Store { impl Store { /// Wrap an existing xs::store::Store (no API server or services). pub fn from_inner(inner: xs::store::Store, path: std::path::PathBuf) -> Self { - Self { inner, path } + Self { + inner, + path, + service_handle: None, + } } /// Create the store and spawn the API server and optional services. @@ -42,6 +47,7 @@ impl Store { }); // Processors (actor, service, action) -- each gets its own subscription + let mut service_handle = None; if services { let s = inner.clone(); tokio::spawn(async move { @@ -51,11 +57,11 @@ impl Store { }); let s = inner.clone(); - tokio::spawn(async move { + service_handle = Some(tokio::spawn(async move { if let Err(e) = xs::processor::service::run(s).await { eprintln!("Service processor error: {e}"); } - }); + })); let s = inner.clone(); tokio::spawn(async move { @@ -65,7 +71,37 @@ impl Store { }); } - Ok(Self { inner, path }) + Ok(Self { + inner, + path, + service_handle, + }) + } + + /// Runs the xs.stopping shutdown protocol: appends the xs.stopping frame so + /// the service processor gracefully terminates any child processes it + /// spawned, then waits for it to finish. Bounded by a timeout so a stuck + /// processor can't hang server exit. + pub async fn shutdown(&mut self) { + let Some(handle) = self.service_handle.take() else { + return; + }; + + if let Err(e) = self + .inner + .append(xs::store::Frame::builder("xs.stopping").build()) + { + eprintln!("Error appending xs.stopping frame: {e}"); + handle.abort(); + return; + } + + if tokio::time::timeout(std::time::Duration::from_secs(3), handle) + .await + .is_err() + { + eprintln!("Service processor did not stop within timeout"); + } } /// Add store commands (.cat, .append, .cas, .last, etc.) to the engine. @@ -205,6 +241,10 @@ impl Store { unreachable!("Store is never constructed without cross-stream feature") } + pub async fn shutdown(&mut self) { + unreachable!("Store is never constructed without cross-stream feature") + } + pub async fn topic_source( &self, _topic: &str, diff --git a/tests/cert.pem b/tests/cert.pem index a824cd7c..81f0cbb7 100644 --- a/tests/cert.pem +++ b/tests/cert.pem @@ -1,29 +1,30 @@ -----BEGIN CERTIFICATE----- -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 +Mo93qreQyFlvUZEg1GfEExfAik54TQ0Q9PTT -----END CERTIFICATE----- diff --git a/tests/combined.pem b/tests/combined.pem index 8698f2a8..959ea3fc 100644 --- a/tests/combined.pem +++ b/tests/combined.pem @@ -1,81 +1,82 @@ -----BEGIN CERTIFICATE----- -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 +Mo93qreQyFlvUZEg1GfEExfAik54TQ0Q9PTT -----END CERTIFICATE----- -----BEGIN PRIVATE KEY----- -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 -g2rP/enadpNRaJbg4Bove6NeVRdlZiE= +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 +nLY/nmfPKFz4VOyCHJJjBby7oepQ -----END PRIVATE KEY----- diff --git a/tests/gen-combined-cert.sh b/tests/gen-combined-cert.sh new file mode 100755 index 00000000..4010ee05 --- /dev/null +++ b/tests/gen-combined-cert.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Regenerates the self-signed TLS test fixtures in this directory: +# cert.pem, key.pem, and combined.pem (cert.pem + key.pem concatenated). +# +# Used by server_test.rs: combined.pem is passed to `http-nu --tls`, and +# cert.pem is used as the curl --cacert to trust it. Run this from the repo +# root whenever the cert expires (openssl x509 -in tests/cert.pem -noout +# -dates): +# +# ./tests/gen-combined-cert.sh + +set -euo pipefail +cd "$(dirname "$0")" + +openssl req -x509 -newkey rsa:4096 -sha256 -days 36500 -nodes \ + -keyout key.pem -out cert.pem \ + -subj "/CN=localhost" \ + -addext "subjectAltName=DNS:localhost,IP:127.0.0.1" + +cat cert.pem key.pem > combined.pem + +echo "Regenerated cert.pem, key.pem, combined.pem" +openssl x509 -in cert.pem -noout -dates diff --git a/tests/key.pem b/tests/key.pem index b895b569..3b60dcaa 100644 --- a/tests/key.pem +++ b/tests/key.pem @@ -1,52 +1,52 @@ -----BEGIN PRIVATE KEY----- -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 -g2rP/enadpNRaJbg4Bove6NeVRdlZiE= +MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQCkh9dfqAdGId+n +k/8+tZqGp2EY+IbvY/Z3Hrjv1b8yovem6vddaESHBF8uH1++hympzTv8IawL3mgS +K7/8iXeiuciMjBiXa9p4EKW/Cx7hGuiCnYA9Y61AhI1oKqh3P0QixSSNLnY40erf +64PgMBBimDBdsg1NrcXEEPZcFhb6N0Jb2RqeZRuTvZYphg2tVpNlRF/ga7RAoLVb +pJAuN7ajV/XG12/ojrJ9cUCRuQSR9I9HDQ5/x9+URaLDKbJc1nWno/xGxrNRWihj +3RflJLWqKMxp2D6rQK8+KxXWwknoPz0boF040Hhs+KDcCt5V+AtvOPwD/mC0hznM +9IEqsZkdEJQBAbF+6IzHllcAkljqCWkF3wj57Bx/4tN1pYD8wUuYcDWAlYGfvS9m +fPwGO/+gFIbFdANVb60D9uOBTn0ztNrGfmICrMZJ8j0FlAvFovQpA+y6wuzxnqLR +UEnGV2GkO6O/H3BWcAhBu2iC0IWoJ7B/PIuMHrbm0TYEYGNcb2hipyYliRzHpIln +7RIAPqlXI7R6g3JXmIVUmZRLufQ99fIpsHgB9Ub9FdA1zHxGiTrKHdgWvrik9y7b +LMjLtqAvFmaxRQ22cqYM59Fbyamn1pFnxr+YNmfDngwdJeu3D8IqKzh6Qp/TU5Hp +X4jO1bGURouU1fivEFqcA8Z79g9ACwIDAQABAoICACeSXnwsyzcUfZjuERVxk01w +o3tghv+70LMcygkxJS/WH29fkwg9xEGfDQ9oT1xhYOVsrdE5/gnKv8gjCqWAIKyc +N36KUX3V3HFFO8AjuhUVJjgIDqh1OV3Lo8EahXNM3aPpO53TP27yGiYqHIlGtiIo +jXVzlhyJP4glNkO2smcS9Fxn7q2LDuNtVVj+W3rF2GSiWdb9wJhMruHwRhc/oidO +SBg/h7Rs4cQnAYsq70z1f6JsowCbv4wHZZrABBytHhYobExPjSBzaUR/kDg2KJVZ +A7UvY/9Hav6uWS3Sg/kegKGM4+7+wQAgmnviB1Cw/HsPPxnszDCPV5kSc6Cjn3oJ +zavDgS4LNuk7nN+iASpseTllQfw/VAOqOBtOJ6EyFmpDEkyWNECacYb89iU/qhyS +XjBrNyR9F11r0/17ZZUx1kbhD76QK8Qfg8csudxQI16AkuNOT3Q+oYayq99Uqv9y +isOzXTwpoHatu7vbmLvibBFD/7xDdxL87BO3s9oCoIhveRWwYo3ue3sGpmAXaU33 +S91LaOO1rhWNpopM+aNfND///WjEHMHW+C2bqhqS1qqg6pH+c5iL2OcMmmN6rtRL +Ji+7sCh7hxaVjTnjLSsXztKnnEd2rwX5VbG+OEl4IlSUmxQVV7k4smVtPqzTs6h4 +NZ3Mzvw9ScmKTNNZ0s+hAoIBAQDVp3YPJeo12062OxZuz7UmVtFcEITJPWN1fanT +Wtgz/cUXmnevz52CYlMo3iyTiyxPc7nwxDo9YKPExkuy3zTnBuOX/Ml4CaTJ1CQz +tB762xxTvhncnvfgCXVpVGdGZAt3WQG1cWn8uia2g9bvHotf/6aMuBGpDtsbK3OY +pbOg1MfVJ8EAEtZ4zkI1NQAeZgTsivRnF/mmeqWzgcvGQiW4mxjiW69bh4TRUah1 +ZkFWhjIfG/NBDaxtOMndVw+mCxxbkKCXteIgBI0U8i82WFkKh3G+C5jksR/74+1t +W5E9Yhl9Xb6bT1eFEHIrzOLZw0JCe5LIuIt0JLydB8h4AEqhAoIBAQDFI+pmyqsl +GJyrnbvSPIRzJLCJcc19n1cu+zHmYu1tHMWXE293vNxigiX9YQME3nAK9rEG7Z0B +83a6JIdy0YsDuWKv+kg0vVrf7WeNTA/2u/QTZpKnKcX3clF2zfhwNv+8C9FpsDHP +Z/0mCNrknfV3SkXZo4Oh7qeTqGkKud8snq4faiQ6sBuG5toY/qHAqVuTqoA8MuIK +iWBFl7fSLNKoQuH+k1MzfmR/nJB6LfbCvEz30jQfFA8MysMwznYz3oHw4wcGGjMo +12bRfHpjVy7FHx+xhab35HHQ8/Iai4YqRnUlwGlJyP/fTiI5+iPlYWTNAxJkOZnu +HR/BVOuo5lcrAoIBACSamCNoP4QSmp2l6eX6N/LllODNA7LmjY+ng9zuB3SA2a99 +i1vXmgioh87MoZ51jZXfIdAaDlBNqkEgJ//xNcMO7xcu++VQ4aaibcFd7qDTwTqn +qw6dPaJofHEbQMZShZRg72YClEH0MoW6eKxm80HzLrmVH5FzNh6lhxxz5x561fGy +6eH1WEFCNR2ggvCyUc2k0NKIzQC0qobiieLhmkzSZaUCSDzKLvsdnlretfr46sVh +qH9QtGXSAOOm8stjw+v8D0dupdsObI1WILiS0k53fgktO3iS0AP7vMWrlmqJ0Awm +b210Xm4xrx3J8GOWjFePjEpPM7UmRhoxJytIAYECggEAUjXyED/AM04aN68lnjzC +d0n7nLWqnhKOxvbWDl9+55uyApiGSYam2SWxNgL4wVUJoGjT1jHM4f1nq6SEH6um +rzzmHMDgz62PcgaMwm3P0nL7DAQgcDmJZvFza3nletZUu1SqVcq2WungtbXHosME +lGSS0HnR8PuXQopek6yDlxXtQwh+Z0L9l7o5hWbaiX+WwvO9Wl1VylMTpLHqZGlw +x95sZD+H2NeCjXz6+xij6P2hk09DkRpKVGlSEM5omKTXx17FlRPj9couBHpGh5rZ +1DZwfxVfVc0ccajpc4xvvL15KO8eeTirC7KHr2nTAlslMCFxz/39ZUEb9wG07NBx +1QKCAQBsDFzHAI1jzOmE/uiic+qb9cO4QTe+BtmTjVl4D5j0Se3pRjM2sARiyJAE +yGEv7bDLf78PYiRrkMcRPAi9UaEKRF2fmAxKtbovu882i0X2x76EsajJRlP87GGo +cF4Qn4k9Ap43Fs0sRcEL9D9+RKNjPChnxmw0ZZiRYm0RNabQUdD63QzrD6AHte+P +N1172Gtv1saVMwfn2ilWYXcHStN/3sixMbOIsI22YCgYtaL9b//dXHeaWi4C7YCt +urmwq9LgKbmHd3YYWcYQR1hjUovRemoagAr5/IK6UtHxSVeNCi/eHX7VgUT64Chw +nLY/nmfPKFz4VOyCHJJjBby7oepQ -----END PRIVATE KEY-----