# ===================================================================== # SMF2290 Pensum — server.ps1 # Lett statisk HTTP-server som fallback når Python/Node ikke er installert. # Krever bare PowerShell 5+ (innebygd i Windows 10/11). # ===================================================================== param( [int]$Port = 8765, [string]$Root = "." ) Add-Type -AssemblyName System.Web $RootAbs = (Resolve-Path -LiteralPath $Root).Path $listener = New-Object System.Net.HttpListener $listener.Prefixes.Add("http://127.0.0.1:$Port/") try { $listener.Start() } catch { Write-Host "Klarte ikke starte server pa port ${Port}: $($_.Exception.Message)" -ForegroundColor Red exit 1 } $mimeMap = @{ '.html' = 'text/html; charset=utf-8' '.htm' = 'text/html; charset=utf-8' '.css' = 'text/css; charset=utf-8' '.js' = 'application/javascript; charset=utf-8' '.mjs' = 'application/javascript; charset=utf-8' '.json' = 'application/json; charset=utf-8' '.md' = 'text/markdown; charset=utf-8' '.txt' = 'text/plain; charset=utf-8' '.svg' = 'image/svg+xml; charset=utf-8' '.png' = 'image/png' '.jpg' = 'image/jpeg' '.jpeg' = 'image/jpeg' '.gif' = 'image/gif' '.ico' = 'image/x-icon' '.webp' = 'image/webp' '.woff' = 'font/woff' '.woff2'= 'font/woff2' '.ttf' = 'font/ttf' '.otf' = 'font/otf' '.pdf' = 'application/pdf' } Write-Host "" Write-Host " Server kjorer pa http://localhost:$Port" -ForegroundColor Green Write-Host " Trykk Ctrl+C for a stoppe" -ForegroundColor DarkGray Write-Host "" try { while ($listener.IsListening) { $context = $listener.GetContext() $req = $context.Request $res = $context.Response try { $path = [System.Web.HttpUtility]::UrlDecode($req.Url.LocalPath) if ([string]::IsNullOrEmpty($path) -or $path -eq '/') { $path = '/index.html' } # Strip leading slash and normalize $relative = $path.TrimStart('/').Replace('/', [System.IO.Path]::DirectorySeparatorChar) $filePath = Join-Path $RootAbs $relative # Resolve to prevent directory traversal try { $resolved = [System.IO.Path]::GetFullPath($filePath) } catch { $resolved = $filePath } if (-not $resolved.StartsWith($RootAbs, [StringComparison]::OrdinalIgnoreCase)) { $res.StatusCode = 403 $body = [System.Text.Encoding]::UTF8.GetBytes("403 Forbidden") $res.OutputStream.Write($body, 0, $body.Length) } elseif (Test-Path -LiteralPath $resolved -PathType Container) { # Directory: try index.html $idx = Join-Path $resolved 'index.html' if (Test-Path -LiteralPath $idx -PathType Leaf) { $bytes = [System.IO.File]::ReadAllBytes($idx) $res.ContentType = 'text/html; charset=utf-8' $res.ContentLength64 = $bytes.Length $res.OutputStream.Write($bytes, 0, $bytes.Length) } else { $res.StatusCode = 404 $body = [System.Text.Encoding]::UTF8.GetBytes("404 Not Found") $res.OutputStream.Write($body, 0, $body.Length) } } elseif (Test-Path -LiteralPath $resolved -PathType Leaf) { $bytes = [System.IO.File]::ReadAllBytes($resolved) $ext = [System.IO.Path]::GetExtension($resolved).ToLower() $mime = if ($mimeMap.ContainsKey($ext)) { $mimeMap[$ext] } else { 'application/octet-stream' } $res.ContentType = $mime $res.ContentLength64 = $bytes.Length $res.OutputStream.Write($bytes, 0, $bytes.Length) } else { $res.StatusCode = 404 $body = [System.Text.Encoding]::UTF8.GetBytes("404 Not Found: $path") $res.OutputStream.Write($body, 0, $body.Length) } } catch { try { $res.StatusCode = 500 $body = [System.Text.Encoding]::UTF8.GetBytes("500 Internal Server Error: " + $_.Exception.Message) $res.OutputStream.Write($body, 0, $body.Length) } catch {} } finally { try { $res.Close() } catch {} } } } finally { try { $listener.Stop() } catch {} try { $listener.Close() } catch {} }