diff --git a/src/router.ps1 b/src/router.ps1 index 3b31bbd..b3139be 100644 --- a/src/router.ps1 +++ b/src/router.ps1 @@ -4,93 +4,106 @@ function Invoke-TasksCompleted { Send-Text $Context "Tasks completion acknowledged." } - function Dispatch-Request { param($Context) - # Guard against null contexts (prevents "null-valued expression" crashes) if ($null -eq $Context -or $null -eq $Context.Request -or $null -eq $Context.Request.Url) { - try { - if ($Context -and $Context.Response) { - $Context.Response.StatusCode = 400 - $Context.Response.Close() + return + } + + try { + $path = $Context.Request.Url.AbsolutePath.TrimStart('/') + + switch -Regex ($path) { + + '^$' { + $html = Get-UIHtml -Page 'onboard' + Send-HTML $Context $html + return } + + '^samy\.js$' { + Send-RemoteAsset -Context $Context -Url $Script:SamyJsUrl -ContentType 'application/javascript; charset=utf-8' + return + } + + '^samy\.css$' { + Send-RemoteAsset -Context $Context -Url $Script:SamyCssUrl -ContentType 'text/css; charset=utf-8' + return + } + + '^SVS_logo\.svg$' { + Send-RemoteAsset -Context $Context -Url $Script:SamyTopLogoUrl -ContentType 'image/svg+xml' + return + } + + '^SAMY\.png$' { + Send-RemoteAsset -Context $Context -Url $Script:SamyBgLogoUrl -ContentType 'image/png' + return + } + + '^SVS_Favicon\.ico$' { + Send-RemoteAsset -Context $Context -Url $Script:SamyFaviconUrl -ContentType 'image/x-icon' + return + } + } + + if ($path -eq 'quit') { + Write-LogHybrid "Shutdown requested" "Info" "Server" -LogToEvent + Send-Text $Context "Server shutting down." + $Global:Listener.Stop() + return + } + + if ($Context.Request.HttpMethod -eq 'POST' -and $path -eq 'tasksCompleted') { Invoke-TasksCompleted $Context; return } + if ($Context.Request.HttpMethod -eq 'POST' -and $path -eq 'getpw') { Invoke-FetchSites $Context; return } + if ($Context.Request.HttpMethod -eq 'POST' -and $path -eq 'renameComputer') { Invoke-RenameComputer $Context; return } + if ($Context.Request.HttpMethod -eq 'POST' -and $path -eq 'getprinters') { Invoke-GetPrinters $Context; return } + if ($Context.Request.HttpMethod -eq 'POST' -and $path -eq 'installprinters'){ Invoke-InstallPrinters $Context; return } + + if ($path -in @('onboard', 'offboard', 'devices')) { + $html = Get-UIHtml -Page $path + Send-HTML $Context $html + return + } + + $task = $Global:SamyTasks | Where-Object Name -EQ $path | Select-Object -First 1 + if ($task) { + $fn = Get-TaskHandlerName -Task $task + if ([string]::IsNullOrWhiteSpace($fn)) { + $Context.Response.StatusCode = 500 + Send-Text $Context "Task '$($task.Label)' is missing a handler (HandlerFn/Handler/Fn/Name)." + return + } + + $cmd = Get-Command -Name $fn -ErrorAction SilentlyContinue + if (-not $cmd) { + $Context.Response.StatusCode = 500 + Send-Text $Context "Handler not found: $fn" + return + } + + if ($cmd.Parameters.ContainsKey('Context')) { & $fn -Context $Context } + else { & $fn } + return + } + + $Context.Response.StatusCode = 404 + Send-Text $Context '404 - Not Found' + } + catch { + # Always return something so clients don't hang + try { + $Context.Response.StatusCode = 500 + $msg = "Dispatch error: $($_.Exception.Message)" + $bytes = [Text.Encoding]::UTF8.GetBytes($msg) + $Context.Response.ContentType = 'text/plain; charset=utf-8' + $Context.Response.ContentLength64 = $bytes.Length + $Context.Response.OutputStream.Write($bytes, 0, $bytes.Length) } catch { } - return } - - # Normalize path - $path = $Context.Request.Url.AbsolutePath.TrimStart('/') - - switch -Regex ($path) { - - '^$' { - $html = Get-UIHtml -Page 'onboard' - return Send-HTML $Context $html - } - - '^samy\.js$' { - return Send-RemoteAsset -Context $Context -Url $Script:SamyJsUrl -ContentType 'application/javascript; charset=utf-8' - } - - '^samy\.css$' { - return Send-RemoteAsset -Context $Context -Url $Script:SamyCssUrl -ContentType 'text/css; charset=utf-8' - } - - '^SVS_logo\.svg$' { - return Send-RemoteAsset -Context $Context -Url $Script:SamyTopLogoUrl -ContentType 'image/svg+xml' - } - - '^SAMY\.png$' { - return Send-RemoteAsset -Context $Context -Url $Script:SamyBgLogoUrl -ContentType 'image/png' - } - - '^SVS_Favicon\.ico$' { - return Send-RemoteAsset -Context $Context -Url $Script:SamyFaviconUrl -ContentType 'image/x-icon' - } + finally { + try { $Context.Response.OutputStream.Close() } catch { } + try { $Context.Response.Close() } catch { } } - - if ($path -eq 'quit') { - Write-LogHybrid "Shutdown requested" "Info" "Server" -LogToEvent - Send-Text $Context "Server shutting down." - $Global:Listener.Stop() - return - } - - if ($Context.Request.HttpMethod -eq 'POST' -and $path -eq 'tasksCompleted') { Invoke-TasksCompleted $Context; return } - if ($Context.Request.HttpMethod -eq 'POST' -and $path -eq 'getpw') { Invoke-FetchSites $Context; return } - if ($Context.Request.HttpMethod -eq 'POST' -and $path -eq 'renameComputer') { Invoke-RenameComputer $Context; return } - if ($Context.Request.HttpMethod -eq 'POST' -and $path -eq 'getprinters') { Invoke-GetPrinters $Context; return } - if ($Context.Request.HttpMethod -eq 'POST' -and $path -eq 'installprinters'){ Invoke-InstallPrinters $Context; return } - - # Only locally-generated pages (root is handled above) - if ($path -in @('onboard', 'offboard', 'devices')) { - $html = Get-UIHtml -Page $path - Send-HTML $Context $html - return - } - - $task = $Global:SamyTasks | Where-Object Name -EQ $path | Select-Object -First 1 - if ($task) { - $fn = Get-TaskHandlerName -Task $task - if ([string]::IsNullOrWhiteSpace($fn)) { - $Context.Response.StatusCode = 500 - Send-Text $Context "Task '$($task.Label)' is missing a handler (HandlerFn/Handler/Fn/Name)." - return - } - - $cmd = Get-Command -Name $fn -ErrorAction SilentlyContinue - if (-not $cmd) { - $Context.Response.StatusCode = 500 - Send-Text $Context "Handler not found: $fn" - return - } - - if ($cmd.Parameters.ContainsKey('Context')) { & $fn -Context $Context } - else { & $fn } - return - } - - $Context.Response.StatusCode = 404 - Send-Text $Context '404 - Not Found' }