Tools/WebServer.ps1

function Engine
{
    param (
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]
        $Engine,

        [Parameter()]
        [scriptblock]
        $ScriptBlock = $null
    )

    $PodeSession.ViewEngine = @{
        'Extension' = $Engine.ToLowerInvariant();
        'Script' = $ScriptBlock;
    }
}

function Start-WebServer
{
    param (
        [switch]
        $Https
    )

    try
    {
        # create the listener on http and/or https
        $listener = New-Object System.Net.HttpListener
        $protocol = 'http'
        if ($Https) {
            $protocol = 'https'
        }

        $_ip = "$($PodeSession.IP.Address)"
        if ($_ip -ieq '0.0.0.0') {
            $_ip = '*'
        }

        $listener.Prefixes.Add("$($protocol)://$($_ip):$($PodeSession.Port)/")

        # start listener
        $listener.Start()

        # state where we're running
        Write-Host "Listening on $($protocol)://$($PodeSession.IP.Name):$($PodeSession.Port)/" -ForegroundColor Yellow

        # loop for http request
        while ($listener.IsListening)
        {
            # get request and response
            $task = $listener.GetContextAsync()
            $task.Wait($PodeSession.CancelToken.Token)

            $context = $task.Result
            $request = $context.Request
            $response = $context.Response

            # clear session
            $PodeSession.Web = @{}
            $PodeSession.Web.Response = $response
            $PodeSession.Web.Request = $request

            # get url path and method
            $path = ($request.RawUrl -isplit "\?")[0]
            $method = $request.HttpMethod.ToLowerInvariant()

            # setup the base request to log later
            $logObject = @{
                'Host' = $request.RemoteEndPoint.Address.IPAddressToString;
                'RfcUserIdentity' = '-';
                'User' = '-';
                'Date' = [DateTime]::Now.ToString('dd/MMM/yyyy:HH:mm:ss zzz');
                'Request' = @{
                    'Method' = $method.ToUpperInvariant();
                    'Resource' = $path;
                    'Protocol' = "HTTP/$($request.ProtocolVersion)";
                    'Referrer' = $request.UrlReferrer;
                    'Agent' = $request.UserAgent;
                };
                'Response' = @{
                    'StatusCode' = '-';
                    'StautsDescription' = '-'
                    'Size' = '-';
                };
            }

            # check to see if the path is a file, so we can check the public folder
            if ((Split-Path -Leaf -Path $path).IndexOf('.') -ne -1) {
                $path = Join-ServerRoot 'public' $path
                Write-ToResponseFromFile -Path $path
            }

            else {
                # ensure the path has a route
                $route = Get-PodeRoute -HttpMethod $method -Route $path
                if ($route -eq $null -or $route.Logic -eq $null) {
                    status 404
                }

                # run the scriptblock
                else {
                    # read and parse any post data
                    $stream = $request.InputStream
                    $reader = New-Object -TypeName System.IO.StreamReader -ArgumentList $stream, $request.ContentEncoding
                    $data = $reader.ReadToEnd()
                    $reader.Close()

                    switch ($request.ContentType) {
                        { $_ -ilike '*json*' } {
                            $data = ($data | ConvertFrom-Json)
                        }

                        { $_ -ilike '*xml*' } {
                            $data = ($data | ConvertFrom-Xml)
                        }
                    }

                    # set session data
                    $PodeSession.Web.Data = $data
                    $PodeSession.Web.Query = $request.QueryString
                    $PodeSession.Web.Parameters = $route.Parameters

                    # invoke route
                    Invoke-Command -ScriptBlock $route.Logic -ArgumentList $PodeSession.Web
                }
            }

            # close response stream (check if exists, as closing the writer closes this stream on unix)
            if ($response.OutputStream) {
                $response.OutputStream.Close()
            }

            # add the log object to the list
            $logObject.Response.StatusCode = $response.StatusCode
            $logObject.Response.StatusDescription = $response.StatusDescription

            if ($response.ContentLength64 -gt 0) {
                $logObject.Response.Size = $response.ContentLength64
            }

            $PodeSession.RequestsToLog.Add($logObject) | Out-Null
        }
    }
    catch [System.OperationCanceledException] {
        Close-Pode -Exit
    }
    finally {
        if ($listener -ne $null) {
            $listener.Stop()
        }
    }
}