Mountain/Vine/Server/Initialize.rs
1//! # Initialize (Vine Server)
2//!
3//! Contains the logic to initialize and start the Mountain gRPC server.
4//!
5//! This module provides the entry point for starting Vine's gRPC servers:
6//! - **MountainServiceServer**: Listens for connections from Cocoon sidecar
7//! - **CocoonServiceServer**: Listens for connections from Mountain
8//! (bidirectional)
9//!
10//! ## Initialization Process
11//!
12//! 1. Validates socket addresses
13//! 2. Retrieves ApplicationRunTime from Tauri state
14//! 3. Creates service implementations with runtime dependencies
15//! 4. Spawns server tasks as background tokio tasks
16//! 5. Servers begin listening on specified ports
17//!
18//! ## Server Configuration
19//!
20//! - **Mountaln Service**: Typically on port 50051 (configurable)
21//! - **Cocoon Service**: Typically on port 50052 (configurable)
22//! - Both servers support compression and message size limits
23//!
24//! ## Error Handling
25//!
26//! Initialization failures are logged and returned to the caller.
27//! Once started, servers run independently and log their own errors.
28//!
29//! ## Lifecycle
30//!
31//! Servers run as detached tokio tasks. They will:
32//! - Start immediately when spawned
33//! - Continue until process termination or tokio runtime shutdown
34//! - Log errors to the logging system
35//! - Not automatically restart on failure (caller should implement retry logic
36//! if needed)
37
38use std::{net::SocketAddr, sync::Arc};
39
40use tauri::{AppHandle, Manager};
41use tonic::transport::Server;
42
43use super::MountainVinegRPCService::MountainVinegRPCService;
44use crate::{
45 RPC::CocoonService::CocoonServiceImpl,
46 RunTime::ApplicationRunTime::ApplicationRunTime,
47 Vine::{
48 Error::VineError,
49 Generated::{cocoon_service_server::CocoonServiceServer, mountain_service_server::MountainServiceServer},
50 },
51 dev_log,
52};
53
54/// Server configuration constants
55#[allow(dead_code)]
56mod ServerConfig {
57
58 use std::time::Duration;
59
60 /// Default port for MountainService server
61 pub const DEFAULT_MOUNTAIN_PORT:u16 = 50051;
62
63 /// Default port for CocoonService server
64 pub const DEFAULT_COCOON_PORT:u16 = 50052;
65
66 /// Maximum concurrent connections per server
67 pub const MAX_CONNECTIONS:usize = 100;
68
69 /// Connection timeout duration
70 pub const CONNECTION_TIMEOUT:Duration = Duration::from_secs(30);
71
72 /// Default message size limit (4MB)
73 pub const MAX_MESSAGE_SIZE:usize = 4 * 1024 * 1024;
74}
75
76/// Validates a socket address string before parsing.
77///
78/// # Parameters
79/// - `AddressString`: The address string to validate
80/// - `ServerName`: Name of the server for error messages
81///
82/// # Returns
83/// - `Ok(SocketAddr)`: Validated and parsed socket address
84/// - `Err(VineError)`: Invalid address format
85fn ValidateSocketAddress(AddressString:&str, ServerName:&str) -> Result<SocketAddr, VineError> {
86 if AddressString.is_empty() {
87 return Err(VineError::InvalidMessageFormat(format!(
88 "{} address cannot be empty",
89 ServerName
90 )));
91 }
92
93 if AddressString.len() > 256 {
94 return Err(VineError::InvalidMessageFormat(format!(
95 "{} address exceeds maximum length (256 characters)",
96 ServerName
97 )));
98 }
99
100 match AddressString.parse::<SocketAddr>() {
101 Ok(addr) => {
102 // Validate port is within valid range
103 if addr.port() < 1024 {
104 dev_log!(
105 "grpc",
106 "warn: [VineServer] {} using privileged port {}, this may require elevated privileges",
107 ServerName,
108 addr.port()
109 );
110 }
111
112 Ok(addr)
113 },
114
115 Err(e) => Err(VineError::AddressParseError(e)),
116 }
117}
118
119/// Initializes and starts the gRPC servers on background tasks.
120///
121/// This function retrieves the core `ApplicationRunTime` from Tauri's managed
122/// state, instantiates the gRPC service implementations
123/// (`MountainVinegRPCService` and `CocoonServiceServer`), and uses `tonic` to
124/// serve them at the specified addresses.
125///
126/// # Parameters
127/// - `ApplicationHandle`: The Tauri application handle
128/// - `MountainAddressString`: The address and port to bind the Mountain server
129/// to (e.g., `"[::1]:50051"`)
130/// - `CocoonAddressString`: The address and port to bind the Cocoon server to
131/// (e.g., `"[::1]:50052"`)
132///
133/// # Returns
134/// - `Ok(())`: Successfully initialized and started both servers
135/// - `Err(VineError)`: Initialization failed (invalid address, missing runtime,
136/// etc.)
137///
138/// # Errors
139///
140/// This function will return an error if:
141/// - Either socket address string is invalid or unparseable
142/// - ApplicationRunTime is not available in Tauri state
143/// - Server task spawning fails (rare)
144///
145/// # Example
146///
147/// ```rust,no_run
148/// # use Vine::Server::Initialize::Initialize;
149/// # use tauri::AppHandle;
150/// # async fn example(handle: AppHandle) -> Result<(), Box<dyn std::error::Error>> {
151/// Initialize(handle, "[::1]:50051".to_string(), "[::1]:50052".to_string())?;
152/// # Ok(())
153/// # }
154/// ```
155///
156/// # Notes
157///
158/// - Servers run as detached tokio tasks
159/// - Initialization is async-safe but function is synchronous
160/// - Servers log errors independently after startup
161/// - Use `Default` addresses for development (localhost with default ports)
162pub fn Initialize(
163 ApplicationHandle:AppHandle,
164
165 MountainAddressString:String,
166
167 CocoonAddressString:String,
168) -> Result<(), VineError> {
169 dev_log!("grpc", "[VineServer] Initializing Vine gRPC servers...");
170
171 crate::dev_log!("grpc", "initializing Vine gRPC servers");
172
173 // Validate and parse socket addresses
174 let MountainAddress = ValidateSocketAddress(&MountainAddressString, "MountainService")?;
175
176 let CocoonAddress = ValidateSocketAddress(&CocoonAddressString, "CocoonService")?;
177
178 dev_log!("grpc", "[VineServer] MountainService will bind to: {}", MountainAddress);
179
180 dev_log!(
181 "grpc",
182 "[VineServer] Cocoon expected on: {} (started by Cocoon process)",
183 CocoonAddress
184 );
185
186 crate::dev_log!("grpc", "Mountain={} Cocoon(remote)={}", MountainAddress, CocoonAddress);
187
188 // Retrieve ApplicationRunTime from Tauri managed state
189 let RunTime = ApplicationHandle
190 .try_state::<Arc<ApplicationRunTime>>()
191 .ok_or_else(|| {
192 let msg = "[VineServer] CRITICAL: ApplicationRunTime not found in Tauri state. Server cannot start.";
193
194 dev_log!("grpc", "error: {}", msg);
195
196 VineError::InternalLockError(msg.to_string())
197 })?
198 .inner()
199 .clone();
200
201 dev_log!("grpc", "[VineServer] ApplicationRunTime retrieved successfully");
202
203 // Create MountainService implementation (handles calls from Cocoon to Mountain)
204 let MountainService = MountainVinegRPCService::Create(ApplicationHandle.clone(), RunTime.clone());
205
206 // Create CocoonService implementation (handles calls from Mountain to Cocoon)
207 let cocoon_service_impl = CocoonServiceImpl::new(RunTime.Environment.clone());
208
209 dev_log!("grpc", "[VineServer] Service implementations created");
210
211 // Spawn Mountain server to run in the background
212 let MountainServerName = MountainAddress.to_string();
213
214 tokio::spawn(async move {
215 dev_log!(
216 "grpc",
217 "[VineServer] Starting MountainService gRPC server on {}",
218 MountainServerName
219 );
220
221 let ServerResult = Server::builder()
222 .add_service(
223 MountainServiceServer::new(MountainService)
224 .max_decoding_message_size(ServerConfig::MAX_MESSAGE_SIZE)
225 .max_encoding_message_size(ServerConfig::MAX_MESSAGE_SIZE),
226 )
227 .serve(MountainAddress)
228 .await;
229
230 match ServerResult {
231 Ok(_) => {
232 dev_log!("grpc", "[VineServer] MountainService server shut down gracefully");
233 },
234 Err(e) => {
235 dev_log!("grpc", "error: [VineServer] MountainService gRPC server error: {}", e);
236 },
237 }
238 });
239
240 // NOTE: CocoonService gRPC server is NOT started by Mountain.
241 // Port 50052 is reserved for Cocoon's own gRPC server (started by
242 // Cocoon's Effect-TS bootstrap, Stage 5). Mountain connects to Cocoon
243 // as a CLIENT on 50052 via Vine::Client::ConnectToSideCar.
244 // Starting CocoonServiceServer here would cause EADDRINUSE when Cocoon
245 // tries to bind the same port.
246 let _ = cocoon_service_impl; // suppress unused variable warning
247
248 dev_log!(
249 "grpc",
250 "[VineServer] MountainService gRPC server initialized on {}",
251 MountainAddress
252 );
253
254 Ok(())
255}