diff --git a/stdlib/src/web/mod.rs b/stdlib/src/web/mod.rs index 4fdf9c26..fd4a368c 100644 --- a/stdlib/src/web/mod.rs +++ b/stdlib/src/web/mod.rs @@ -37,10 +37,7 @@ pub fn web_render_html_pub(ctx: &RuntimeContext) -> String { renderer::render_complete_document(&blocks, &event_handlers) } -fn web_start( - ctx: &mut RuntimeContext, - args: Vec, -) -> Result { +fn check_network_capability(ctx: &RuntimeContext) -> Result<(), RuntimeError> { if !ctx.config.capabilities.contains(&Capability::Network) { return Err(RuntimeError::new( techscript_runtime::error::RuntimeErrorKind::InvalidOperation( @@ -50,15 +47,32 @@ fn web_start( None, )); } - let port = args[0].try_into_int().map_err(|e| { - RuntimeError::new( - techscript_runtime::error::RuntimeErrorKind::InvalidOperation(e.to_string()), - None, - None, - ) - })? as u16; - let content = args[1].to_string(); + Ok(()) +} + +fn parse_port_arg(arg: &RuntimeValue) -> Result { + arg.try_into_int() + .map_err(|e| { + RuntimeError::new( + techscript_runtime::error::RuntimeErrorKind::InvalidOperation(e.to_string()), + None, + None, + ) + }) + .map(|p| p as u16) +} + +fn set_page_content(content: String) { *server::PAGE_CONTENT.lock().unwrap() = content; +} + +fn web_start( + ctx: &mut RuntimeContext, + args: Vec, +) -> Result { + check_network_capability(ctx)?; + let port = parse_port_arg(&args[0])?; + set_page_content(args[1].to_string()); server::start_webforge_server(port) } @@ -67,8 +81,7 @@ fn web_page( args: Vec, ) -> Result { let _path = args[0].to_string(); - let content = args[1].to_string(); - *server::PAGE_CONTENT.lock().unwrap() = content; + set_page_content(args[1].to_string()); Ok(RuntimeValue::Null) } @@ -76,22 +89,8 @@ fn web_serve( ctx: &mut RuntimeContext, args: Vec, ) -> Result { - if !ctx.config.capabilities.contains(&Capability::Network) { - return Err(RuntimeError::new( - techscript_runtime::error::RuntimeErrorKind::InvalidOperation( - "Security policy violation: Network capability is denied".to_string(), - ), - None, - None, - )); - } - let port = args[0].try_into_int().map_err(|e| { - RuntimeError::new( - techscript_runtime::error::RuntimeErrorKind::InvalidOperation(e.to_string()), - None, - None, - ) - })? as u16; + check_network_capability(ctx)?; + let port = parse_port_arg(&args[0])?; server::start_webforge_server(port) } @@ -107,8 +106,7 @@ fn web_set_content( _ctx: &mut RuntimeContext, args: Vec, ) -> Result { - let new_content = args[0].to_string(); - *server::PAGE_CONTENT.lock().unwrap() = new_content; + set_page_content(args[0].to_string()); Ok(RuntimeValue::Str("Content updated".to_string())) } @@ -116,15 +114,7 @@ fn web_fetch( ctx: &mut RuntimeContext, args: Vec, ) -> Result { - if !ctx.config.capabilities.contains(&Capability::Network) { - return Err(RuntimeError::new( - techscript_runtime::error::RuntimeErrorKind::InvalidOperation( - "Security policy violation: Network capability is denied".to_string(), - ), - None, - None, - )); - } + check_network_capability(ctx)?; let url = args[0].to_string(); if !server::is_safe_url(&url) { @@ -183,6 +173,7 @@ fn web_start_website( ctx: &mut RuntimeContext, args: Vec, ) -> Result { + check_network_capability(ctx)?; let port = if let Some(arg) = args.first() { arg.try_into_int().unwrap_or(3000) as u16 } else { diff --git a/stdlib/src/web/server.rs b/stdlib/src/web/server.rs index 07501278..6781e7c0 100644 --- a/stdlib/src/web/server.rs +++ b/stdlib/src/web/server.rs @@ -124,7 +124,15 @@ pub fn start_webforge_server(port: u16) -> Result { } SERVER_RUNNING.store(true, Ordering::SeqCst); - let server = match tiny_http::Server::http(format!("0.0.0.0:{}", port)) { + let server_res = tiny_http::Server::http(format!("0.0.0.0:{}", port)).or_else(|e| { + if port == 3000 { + tiny_http::Server::http("0.0.0.0:0") + } else { + Err(e) + } + }); + + let server = match server_res { Ok(s) => Mutex::new(s), Err(e) => { SERVER_RUNNING.store(false, Ordering::SeqCst);