bootstrap.ps1
. "$PSScriptRoot\.env.ps1" . "$PSScriptRoot\utils\object-helper.ps1" . "$PSScriptRoot\utils\converters.ps1" . "$PSScriptRoot\utils\events.ps1" . "$PSScriptRoot\modules\session.ps1" . "$PSScriptRoot\modules\expose.ps1" . "$PSScriptRoot\modules\accounts.ps1" . "$PSScriptRoot\modules\domain.ps1" . "$PSScriptRoot\modules\printers.ps1" $app = [PSCustomObject]@{ events = [PSCustomObject]@{ server_exposed = "server_exposed" } } Add-Property $app "listener" (New-Object System.Net.HttpListener) Add-Property $app "handlers" (New-Object System.Collections.ArrayList) Add-Method $app "stop" { Write-Host "INFO: Stopping application..." if ($app.forwarding) { Stop-Process -Id $app.forwarding.id } $app.listener.stop() } Add-Method $app "listen" { param( [hashtable] $options = @{ port = $expose.port expose = $false } ) $portBusy = netstat -an | Select-String ":$($options.port)" if ($portBusy) { Write-Warning "Port $(($options.port)) is busy." while (netstat -an | Select-String ":$((++$options.port))") { $null } Write-Warning "Continuing with port $($options.port)." } $addresses = @( "http://*.serveo.net:$($options.port)/", "http://*:$($options.port)/", "http://$(($session.getLocalhost())):$($options.port)/" ) for ($i = 0; $i -lt $addresses.Count; $i++) { $app.listener.Prefixes.Add($addresses[$i]) } $app.listener.Start() Write-Host "INFO: Listening on port $($options.port)..." if ($options.expose) { $expose.port = $options.port Send-Event $app.events.server_exposed $expose.init() } try { while ($app.listener.isListening) { $context = $app.listener.GetContextAsync() | Wait-Task $script:req = $context.Request $script:res = $context.Response $script:payload = $null Write-Host "REQUEST: [$($req.HttpMethod)] $($req.RawUrl)" -NoNewline $app.getPayload() $app.setCORSHeaders() $app.handlers.forEach({ Invoke-Command -ScriptBlock $_ }) $app.handleCORSRequests() $app.returnNotFound() Write-Host " $($res.StatusCode)" } } finally { $app.stop() } } Add-Method $app "request" { param( [Parameter(Mandatory = $true)] [string] $method, [Parameter(Mandatory = $true)] [string] $path, [Parameter(Mandatory = $true)] [scriptblock] $callback ) $handler = @" if (`$req.HttpMethod -eq '$($method)' -and `$req.RawUrl -eq '$($path)') { `$type = 'text/plain' `$status = 200 try { if (`$payload) { `$result = Invoke-Command -ScriptBlock { $($callback) } -ArgumentList `$payload } else { `$result = Invoke-Command -ScriptBlock { $($callback) } } } catch { `$result = `"There was an error while doing a $($method) request to '$($path)':`n`$_`" Write-Warning `$result `$status = 500 } if (`$null -eq `$result) { `$result = @{} } if (`$result -is [hashtable] -or `$result -is [object[]]) { `$result = `$result | ConvertTo-EnumsAsStrings | ConvertTo-Json `$type = 'application/json' } `$buffer = [System.Text.Encoding]::UTF8.GetBytes(`$result) `$res.StatusCode = `$status `$res.ContentType = `$type `$res.ContentLength64 = `$buffer.Length `$res.OutputStream.Write(`$buffer, 0, `$buffer.Length) `$res.OutputStream.Close() } "@ $app.handlers.Add([scriptblock]::Create($handler)) > $null } Add-Method $app "getPayload" { $payloadSize = $req.ContentLength64 if ($payloadSize -gt 0) { $buffer = New-Object byte[]($payloadSize) $req.InputStream.Read($buffer, 0, $payloadSize) $data = [System.Text.Encoding]::UTF8.GetString($buffer) try { $data = $data | ConvertFrom-Json } finally { $null } $script:payload = $data } return $null } Add-Method $app "setCORSHeaders" { $res.AddHeader("Access-Control-Allow-Origin", "*") $res.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") $res.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With") $res.AddHeader("Access-Control-Max-Age", "1728000") } Add-Method $app "handleCORSRequests" { if ($req.HttpMethod -eq "OPTIONS") { $res.StatusCode = 200 $res.ContentType = "text/plain" $res.OutputStream.Close() } } Add-Method $app "returnNotFound" { $httpResponseWasSent = $res.ContentLength64 -gt 0 if (-not $httpResponseWasSent) { $buffer = [System.Text.Encoding]::UTF8.GetBytes("Error 404: Not found.") $res.StatusCode = 404 $res.ContentType = "text/plain" $res.ContentLength64 = $buffer.Length $res.OutputStream.Write($buffer, 0, $buffer.Length) $res.OutputStream.Close() } } Add-Method $app "get" { param( [Parameter(Mandatory = $true)] [string] $path, [Parameter(Mandatory = $true)] [scriptblock] $callback ) $app.request('GET', $path, $callback) } Add-Method $app "post" { param( [Parameter(Mandatory = $true)] [string] $path, [Parameter(Mandatory = $true)] [scriptblock] $callback ) $app.request('POST', $path, $callback) } |