From 401a47508167792e85dc6c846e2e3bd2fba673c1 Mon Sep 17 00:00:00 2001 From: Dimitar Milov Date: Tue, 27 Apr 2021 10:22:51 +0300 Subject: [PATCH] Implement wait server to start in the integrations tests BeforeAll Fix Binary Mode HTTP Header DateTime format. It is UTC time formatted in ISO 8601 format including milliseconds. Bump revision of the module version Signed-off-by: Dimitar Milov --- .../CloudEvents.Sdk.psd1 | 2 +- .../CloudEvents.Sdk.psm1 | 2 +- test/integration/HttpIntegration.Tests.ps1 | 483 ++++++++++-------- test/integration/HttpServer.ps1 | 247 ++++----- test/integration/ProtocolConstants.ps1 | 3 +- test/unit/ConvertFrom-HttpMessage.Tests.ps1 | 4 +- test/unit/ConvertTo-HttpMessage.Tests.ps1 | 8 +- 7 files changed, 395 insertions(+), 354 deletions(-) diff --git a/src/CloudEventsPowerShell/CloudEvents.Sdk.psd1 b/src/CloudEventsPowerShell/CloudEvents.Sdk.psd1 index bcaa783..558ce15 100644 --- a/src/CloudEventsPowerShell/CloudEvents.Sdk.psd1 +++ b/src/CloudEventsPowerShell/CloudEvents.Sdk.psd1 @@ -9,7 +9,7 @@ RootModule = 'CloudEvents.Sdk.psm1' # Version number of this module. -ModuleVersion = '0.2.0' +ModuleVersion = '0.2.1' # Supported PSEditions CompatiblePSEditions = @('Core') diff --git a/src/CloudEventsPowerShell/CloudEvents.Sdk.psm1 b/src/CloudEventsPowerShell/CloudEvents.Sdk.psm1 index bd03947..f878b8c 100644 --- a/src/CloudEventsPowerShell/CloudEvents.Sdk.psm1 +++ b/src/CloudEventsPowerShell/CloudEvents.Sdk.psm1 @@ -556,7 +556,7 @@ PROCESS { $headers.Add(($HttpHeaderPrefix + $attribute.Key), $attribute.Value.ToString()) } elseif ($attribute.Value -is [DateTime]) { - $headers.Add(($HttpHeaderPrefix + $attribute.Key), $attribute.Value.ToString("u")) + $headers.Add(($HttpHeaderPrefix + $attribute.Key), $attribute.Value.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ')) } elseif ($attribute.Value -is [Uri] -or $attribute.Value -is [int]) { $headers.Add(($HttpHeaderPrefix + $attribute.Key), $attribute.Value.ToString()) diff --git a/test/integration/HttpIntegration.Tests.ps1 b/test/integration/HttpIntegration.Tests.ps1 index 950b6fd..f58225a 100644 --- a/test/integration/HttpIntegration.Tests.ps1 +++ b/test/integration/HttpIntegration.Tests.ps1 @@ -4,277 +4,306 @@ # ************************************************************************** param( - [Parameter()] - [ValidateScript({Test-Path $_})] - [string] - $CloudEventsModulePath) + [Parameter()] + [ValidateScript( { Test-Path $_ })] + [string] + $CloudEventsModulePath) Describe "Client-Server Integration Tests" { - Context "Send And Receive CloudEvents over Http" { - BeforeAll { - $testServerUrl = 'http://localhost:52673/' + Context "Send And Receive CloudEvents over Http" { + BeforeAll { + $testServerUrl = 'http://localhost:52673/' - $serverProcess = $null + $serverProcess = $null - . (Join-Path $PSScriptRoot 'ProtocolConstants.ps1') + . (Join-Path $PSScriptRoot 'ProtocolConstants.ps1') - # Starts CloudEvent Test Server - $usePowerShell = (Get-Process -Id $pid).ProcessName - $serverScript = Join-Path $PSScriptRoot 'HttpServer.ps1' - $serverProcessArguments = "-Command $serverScript -CloudEventsModulePath '$CloudEventsModulePath' -ServerUrl '$testServerUrl'" + # Starts CloudEvent Test Server + $usePowerShell = (Get-Process -Id $pid).ProcessName + $serverScript = Join-Path $PSScriptRoot 'HttpServer.ps1' + $serverProcessArguments = "-Command $serverScript -CloudEventsModulePath '$CloudEventsModulePath' -ServerUrl '$testServerUrl'" - $serverProcess = Start-Process ` - -FilePath $usePowerShell ` - -ArgumentList $serverProcessArguments ` - -PassThru ` - -NoNewWindow - } + $serverProcess = Start-Process ` + -FilePath $usePowerShell ` + -ArgumentList $serverProcessArguments ` + -PassThru ` + -NoNewWindow - AfterAll { - # Requests Stop CloudEvent Test Server - $serverStopRequest = ` - New-CloudEvent ` - -Id ([Guid]::NewGuid()) ` - -Type $script:ServerStopType ` - -Source $script:ClientSource | ` - ConvertTo-HttpMessage ` - -ContentMode Structured + # Wait Server to Start + $serverPingRequest = ` + New-CloudEvent ` + -Id ([Guid]::NewGuid()) ` + -Type $script:ServerPingType ` + -Source $script:ClientSource | ` + ConvertTo-HttpMessage ` + -ContentMode Structured - Invoke-WebRequest ` - -Uri $testServerUrl ` - -Headers $serverStopRequest.Headers ` - -Body $serverStopRequest.Body | Out-Null + $serverReady = $false + $maxRetries = 10 + do { + try { + Invoke-WebRequest ` + -Uri $testServerUrl ` + -Headers $serverPingRequest.Headers ` + -Body $serverPingRequest.Body | Out-Null + $serverReady = $true + } catch { + Write-Verbose "Wait CloudEvents HTTP Test Server to start" + Start-Sleep -Seconds 1 + $maxRetries-- + } + } while (-not $serverReady -and $maxRetries -gt 0) + + if ($maxRetries -eq 0) { + throw "CloudEvents HTTP Test Server failed to start." + } + } - if ($serverProcess -ne $null -and ` - -not $serverProcess.HasExited) { - $serverProcess | Wait-Process - } - } + AfterAll { + # Requests Stop CloudEvent Test Server + $serverStopRequest = ` + New-CloudEvent ` + -Id ([Guid]::NewGuid()) ` + -Type $script:ServerStopType ` + -Source $script:ClientSource | ` + ConvertTo-HttpMessage ` + -ContentMode Structured - It 'Echo binary content mode cloud events' { - # Arrange - $cloudEvent = New-CloudEvent ` - -Type $script:EchoBinaryType ` - -Source $script:ClientSource ` - -Id 'integration-test-1' ` - -Time (Get-Date) | ` - Set-CloudEventJsonData -Data @{ - 'a1' = 'b' - 'a2' = 'c' - 'a3' = 'd' - } + Invoke-WebRequest ` + -Uri $testServerUrl ` + -Headers $serverStopRequest.Headers ` + -Body $serverStopRequest.Body | Out-Null - # Act + if ($serverProcess -ne $null -and ` + -not $serverProcess.HasExited) { + $serverProcess | Wait-Process + } + } - ## Convert CloudEvent to HTTP Message - $httpRequest = ConvertTo-HttpMessage ` - -CloudEvent $cloudEvent ` - -ContentMode Binary + It 'Echo binary content mode cloud events' { + # Arrange + $cloudEvent = New-CloudEvent ` + -Type $script:EchoBinaryType ` + -Source $script:ClientSource ` + -Id 'integration-test-1' ` + -Time (Get-Date) | ` + Set-CloudEventJsonData -Data @{ + 'a1' = 'b' + 'a2' = 'c' + 'a3' = 'd' + } - ## Invoke WebRequest with the HTTP Message - $httpResponse = Invoke-WebRequest ` - -Uri $testServerUrl ` - -Headers $httpRequest.Headers ` - -Body $httpRequest.Body + # Act - ## Convert HTTP Response to CloudEvent - $resultCloudEvent = ConvertFrom-HttpMessage ` - -Headers $httpResponse.Headers ` - -Body $httpResponse.Content + ## Convert CloudEvent to HTTP Message + $httpRequest = ConvertTo-HttpMessage ` + -CloudEvent $cloudEvent ` + -ContentMode Binary - # Assert + ## Invoke WebRequest with the HTTP Message + $httpResponse = Invoke-WebRequest ` + -Uri $testServerUrl ` + -Headers $httpRequest.Headers ` + -Body $httpRequest.Body - ## Assert echoed CloudEvent - $resultCloudEvent | Should -Not -Be $null - $resultCloudEvent.Source | Should -Be $script:ServerSource - $resultCloudEvent.Type | Should -Be $script:EchoBinaryType - $resultCloudEvent.Id | Should -Be $cloudEvent.Id - $resultCloudEvent.Time | Should -BeGreaterThan $cloudEvent.Time + ## Convert HTTP Response to CloudEvent + $resultCloudEvent = ConvertFrom-HttpMessage ` + -Headers $httpResponse.Headers ` + -Body $httpResponse.Content - ## Assert Result CloudEvent Data - ## Read Data as Json - $resultData = $resultCloudEvent | Read-CloudEventJsonData - $resultData.a1 | Should -Be 'b' - $resultData.a2 | Should -Be 'c' - $resultData.a3 | Should -Be 'd' - } + # Assert - It 'Echo binary content mode cloud events with XML data' { - # Arrange - $cloudEvent = New-CloudEvent ` - -Type $script:EchoBinaryType ` - -Source $script:ClientSource ` - -Id 'integration-test-2' ` - -Time (Get-Date) | ` - Set-CloudEventXmlData -Data @{ - 'a1' = @{ - 'a2' = 'c' - 'a3' = 'd' - } - } ` - -AttributesKeysInElementAttributes $false + ## Assert echoed CloudEvent + $resultCloudEvent | Should -Not -Be $null + $resultCloudEvent.Source | Should -Be $script:ServerSource + $resultCloudEvent.Type | Should -Be $script:EchoBinaryType + $resultCloudEvent.Id | Should -Be $cloudEvent.Id + $resultCloudEvent.Time | Should -BeGreaterThan $cloudEvent.Time - # Act + ## Assert Result CloudEvent Data + ## Read Data as Json + $resultData = $resultCloudEvent | Read-CloudEventJsonData + $resultData.a1 | Should -Be 'b' + $resultData.a2 | Should -Be 'c' + $resultData.a3 | Should -Be 'd' + } - ## Convert CloudEvent to HTTP Message - $httpRequest = ConvertTo-HttpMessage ` - -CloudEvent $cloudEvent ` - -ContentMode Binary + It 'Echo binary content mode cloud events with XML data' { + # Arrange + $cloudEvent = New-CloudEvent ` + -Type $script:EchoBinaryType ` + -Source $script:ClientSource ` + -Id 'integration-test-2' ` + -Time (Get-Date) | ` + Set-CloudEventXmlData -Data @{ + 'a1' = @{ + 'a2' = 'c' + 'a3' = 'd' + } + } ` + -AttributesKeysInElementAttributes $false - ## Invoke WebRequest with the HTTP Message - $httpResponse = Invoke-WebRequest ` - -Uri $testServerUrl ` - -Headers $httpRequest.Headers ` - -Body $httpRequest.Body + # Act - ## Convert HTTP Response to CloudEvent - $resultCloudEvent = ConvertFrom-HttpMessage ` - -Headers $httpResponse.Headers ` - -Body $httpResponse.Content + ## Convert CloudEvent to HTTP Message + $httpRequest = ConvertTo-HttpMessage ` + -CloudEvent $cloudEvent ` + -ContentMode Binary - # Assert + ## Invoke WebRequest with the HTTP Message + $httpResponse = Invoke-WebRequest ` + -Uri $testServerUrl ` + -Headers $httpRequest.Headers ` + -Body $httpRequest.Body - ## Assert echoed CloudEvent - $resultCloudEvent | Should -Not -Be $null - $resultCloudEvent.Source | Should -Be $script:ServerSource - $resultCloudEvent.Type | Should -Be $script:EchoBinaryType - $resultCloudEvent.Id | Should -Be $cloudEvent.Id - $resultCloudEvent.Time | Should -BeGreaterThan $cloudEvent.Time + ## Convert HTTP Response to CloudEvent + $resultCloudEvent = ConvertFrom-HttpMessage ` + -Headers $httpResponse.Headers ` + -Body $httpResponse.Content - ## Assert Result CloudEvent Data - ## Read Data as Xml - $resultData = $resultCloudEvent | Read-CloudEventXmlData -ConvertMode 'SkipAttributes' - $resultData -is [hashtable] | Should -Be $true - $resultData.a1 -is [hashtable] | Should -Be $true - $resultData.a1.a2 | Should -Be 'c' - $resultData.a1.a3 | Should -Be 'd' - } + # Assert - It 'Echo structured content mode cloud events' { - # Arrange - $cloudEvent = New-CloudEvent ` - -Type $script:EchoStructuredType ` - -Source $script:ClientSource ` - -Id 'integration-test-3' ` - -Time (Get-Date) | ` - Set-CloudEventJsonData -Data @{ - 'b1' = 'd' - 'b2' = 'e' - 'b3' = 'f' - } + ## Assert echoed CloudEvent + $resultCloudEvent | Should -Not -Be $null + $resultCloudEvent.Source | Should -Be $script:ServerSource + $resultCloudEvent.Type | Should -Be $script:EchoBinaryType + $resultCloudEvent.Id | Should -Be $cloudEvent.Id + $resultCloudEvent.Time | Should -BeGreaterThan $cloudEvent.Time - # Act + ## Assert Result CloudEvent Data + ## Read Data as Xml + $resultData = $resultCloudEvent | Read-CloudEventXmlData -ConvertMode 'SkipAttributes' + $resultData -is [hashtable] | Should -Be $true + $resultData.a1 -is [hashtable] | Should -Be $true + $resultData.a1.a2 | Should -Be 'c' + $resultData.a1.a3 | Should -Be 'd' + } - ## Convert CloudEvent to HTTP Message - $httpRequest = ConvertTo-HttpMessage ` - -CloudEvent $cloudEvent ` - -ContentMode Structured + It 'Echo structured content mode cloud events' { + # Arrange + $cloudEvent = New-CloudEvent ` + -Type $script:EchoStructuredType ` + -Source $script:ClientSource ` + -Id 'integration-test-3' ` + -Time (Get-Date) | ` + Set-CloudEventJsonData -Data @{ + 'b1' = 'd' + 'b2' = 'e' + 'b3' = 'f' + } - ## Invoke WebRequest with the HTTP Message - $httpResponse = Invoke-WebRequest ` - -Uri $testServerUrl ` - -Headers $httpRequest.Headers ` - -Body $httpRequest.Body + # Act - ## Convert HTTP Response to CloudEvent - $resultCloudEvent = ConvertFrom-HttpMessage ` - -Headers $httpResponse.Headers ` - -Body $httpResponse.Content + ## Convert CloudEvent to HTTP Message + $httpRequest = ConvertTo-HttpMessage ` + -CloudEvent $cloudEvent ` + -ContentMode Structured - # Assert + ## Invoke WebRequest with the HTTP Message + $httpResponse = Invoke-WebRequest ` + -Uri $testServerUrl ` + -Headers $httpRequest.Headers ` + -Body $httpRequest.Body - ## Assert echoed CloudEvent - $resultCloudEvent | Should -Not -Be $null - $resultCloudEvent.Source | Should -Be $script:ServerSource - $resultCloudEvent.Type | Should -Be $script:EchoStructuredType - $resultCloudEvent.Id | Should -Be $cloudEvent.Id - $resultCloudEvent.Time | Should -BeGreaterThan $cloudEvent.Time + ## Convert HTTP Response to CloudEvent + $resultCloudEvent = ConvertFrom-HttpMessage ` + -Headers $httpResponse.Headers ` + -Body $httpResponse.Content - ## Assert Result CloudEvent Data - ## Read Data as Json - $resultData = $resultCloudEvent | Read-CloudEventJsonData - $resultData.b1 | Should -Be 'd' - $resultData.b2 | Should -Be 'e' - $resultData.b3 | Should -Be 'f' - } + # Assert - It 'Echo structured content mode cloud events with XML data' { - # Arrange - $cloudEvent = New-CloudEvent ` - -Type $script:EchoStructuredType ` - -Source $script:ClientSource ` - -Id 'integration-test-4' ` - -Time (Get-Date) | ` - Set-CloudEventXmlData -Data @{ - 'b1' = @{ - 'b2' = 'e' - 'b3' = 'f' - } - } ` - -AttributesKeysInElementAttributes $false + ## Assert echoed CloudEvent + $resultCloudEvent | Should -Not -Be $null + $resultCloudEvent.Source | Should -Be $script:ServerSource + $resultCloudEvent.Type | Should -Be $script:EchoStructuredType + $resultCloudEvent.Id | Should -Be $cloudEvent.Id + $resultCloudEvent.Time | Should -BeGreaterThan $cloudEvent.Time - # Act + ## Assert Result CloudEvent Data + ## Read Data as Json + $resultData = $resultCloudEvent | Read-CloudEventJsonData + $resultData.b1 | Should -Be 'd' + $resultData.b2 | Should -Be 'e' + $resultData.b3 | Should -Be 'f' + } - ## Convert CloudEvent to HTTP Message - $httpRequest = ConvertTo-HttpMessage ` - -CloudEvent $cloudEvent ` - -ContentMode Structured + It 'Echo structured content mode cloud events with XML data' { + # Arrange + $cloudEvent = New-CloudEvent ` + -Type $script:EchoStructuredType ` + -Source $script:ClientSource ` + -Id 'integration-test-4' ` + -Time (Get-Date) | ` + Set-CloudEventXmlData -Data @{ + 'b1' = @{ + 'b2' = 'e' + 'b3' = 'f' + } + } ` + -AttributesKeysInElementAttributes $false - ## Invoke WebRequest with the HTTP Message - $httpResponse = Invoke-WebRequest ` - -Uri $testServerUrl ` - -Headers $httpRequest.Headers ` - -Body $httpRequest.Body + # Act - ## Convert HTTP Response to CloudEvent - $resultCloudEvent = ConvertFrom-HttpMessage ` - -Headers $httpResponse.Headers ` - -Body $httpResponse.Content + ## Convert CloudEvent to HTTP Message + $httpRequest = ConvertTo-HttpMessage ` + -CloudEvent $cloudEvent ` + -ContentMode Structured - # Assert + ## Invoke WebRequest with the HTTP Message + $httpResponse = Invoke-WebRequest ` + -Uri $testServerUrl ` + -Headers $httpRequest.Headers ` + -Body $httpRequest.Body - ## Assert echoed CloudEvent - $resultCloudEvent | Should -Not -Be $null - $resultCloudEvent.Source | Should -Be $script:ServerSource - $resultCloudEvent.Type | Should -Be $script:EchoStructuredType - $resultCloudEvent.Id | Should -Be $cloudEvent.Id - $resultCloudEvent.Time | Should -BeGreaterThan $cloudEvent.Time + ## Convert HTTP Response to CloudEvent + $resultCloudEvent = ConvertFrom-HttpMessage ` + -Headers $httpResponse.Headers ` + -Body $httpResponse.Content - ## Assert Result CloudEvent Data - ## Read Data as Xml - $resultData = $resultCloudEvent | Read-CloudEventXmlData -ConvertMode 'SkipAttributes' - $resultData -is [hashtable] | Should -Be $true - $resultData.b1 -is [hashtable] | Should -Be $true - $resultData.b1.b2 | Should -Be 'e' - $resultData.b1.b3 | Should -Be 'f' - } + # Assert - It 'Send cloud event expecting no result' { - # Arrange - $cloudEvent = New-CloudEvent ` - -Type 'no-content' ` - -Source $script:ClientSource ` - -Id 'integration-test-5' ` - -Time (Get-Date) | ` - Set-CloudEventData ` - -Data 'This is text data' ` - -DataContentType 'application/text' + ## Assert echoed CloudEvent + $resultCloudEvent | Should -Not -Be $null + $resultCloudEvent.Source | Should -Be $script:ServerSource + $resultCloudEvent.Type | Should -Be $script:EchoStructuredType + $resultCloudEvent.Id | Should -Be $cloudEvent.Id + $resultCloudEvent.Time | Should -BeGreaterThan $cloudEvent.Time - # Act + ## Assert Result CloudEvent Data + ## Read Data as Xml + $resultData = $resultCloudEvent | Read-CloudEventXmlData -ConvertMode 'SkipAttributes' + $resultData -is [hashtable] | Should -Be $true + $resultData.b1 -is [hashtable] | Should -Be $true + $resultData.b1.b2 | Should -Be 'e' + $resultData.b1.b3 | Should -Be 'f' + } - ## Convert CloudEvent to HTTP Message - $httpRequest = ConvertTo-HttpMessage ` - -CloudEvent $cloudEvent ` - -ContentMode Structured + It 'Send cloud event expecting no result' { + # Arrange + $cloudEvent = New-CloudEvent ` + -Type 'no-content' ` + -Source $script:ClientSource ` + -Id 'integration-test-5' ` + -Time (Get-Date) | ` + Set-CloudEventData ` + -Data 'This is text data' ` + -DataContentType 'application/text' - ## Invoke WebRequest with the HTTP Message - $httpResponse = Invoke-WebRequest ` - -Uri $testServerUrl ` - -Headers $httpRequest.Headers ` - -Body $httpRequest.Body + # Act - # Assert - $httpResponse.StatusCode | Should -Be ([int]([System.Net.HttpStatusCode]::NoContent)) - } - } + ## Convert CloudEvent to HTTP Message + $httpRequest = ConvertTo-HttpMessage ` + -CloudEvent $cloudEvent ` + -ContentMode Structured + + ## Invoke WebRequest with the HTTP Message + $httpResponse = Invoke-WebRequest ` + -Uri $testServerUrl ` + -Headers $httpRequest.Headers ` + -Body $httpRequest.Body + + # Assert + $httpResponse.StatusCode | Should -Be ([int]([System.Net.HttpStatusCode]::NoContent)) + } + } } \ No newline at end of file diff --git a/test/integration/HttpServer.ps1 b/test/integration/HttpServer.ps1 index fead90f..96ae1be 100644 --- a/test/integration/HttpServer.ps1 +++ b/test/integration/HttpServer.ps1 @@ -4,18 +4,18 @@ # ************************************************************************** param( - [Parameter(Mandatory = $true)] - [ValidateScript({Test-Path $_})] - [string] - $CloudEventsModulePath, + [Parameter(Mandatory = $true)] + [ValidateScript( { Test-Path $_ })] + [string] + $CloudEventsModulePath, - [Parameter( - Mandatory = $true, - ValueFromPipeline = $false, - ValueFromPipelineByPropertyName = $false)] - [ValidateNotNull()] - [string] - $ServerUrl + [Parameter( + Mandatory = $true, + ValueFromPipeline = $false, + ValueFromPipelineByPropertyName = $false)] + [ValidateNotNull()] + [string] + $ServerUrl ) . (Join-Path $PSScriptRoot 'ProtocolConstants.ps1') @@ -24,128 +24,139 @@ param( Import-Module $CloudEventsModulePath function Start-HttpCloudEventListener { -<# + <# .DESCRIPTION Starts a HTTP CloudEvent Listener on specified Url #> -[CmdletBinding()] -param( - [Parameter( - Mandatory = $true, - ValueFromPipeline = $false, - ValueFromPipelineByPropertyName = $false)] - [ValidateNotNull()] - [string] - $Url, + [CmdletBinding()] + param( + [Parameter( + Mandatory = $true, + ValueFromPipeline = $false, + ValueFromPipelineByPropertyName = $false)] + [ValidateNotNull()] + [string] + $Url, - [Parameter( - Mandatory = $false, - ValueFromPipeline = $false, - ValueFromPipelineByPropertyName = $false)] - [scriptblock] - $Handler -) + [Parameter( + Mandatory = $false, + ValueFromPipeline = $false, + ValueFromPipelineByPropertyName = $false)] + [scriptblock] + $Handler + ) - $listener = New-Object -Type 'System.Net.HttpListener' - $listener.AuthenticationSchemes = [System.Net.AuthenticationSchemes]::Anonymous - $listener.Prefixes.Add($Url) + $listener = New-Object -Type 'System.Net.HttpListener' + $listener.AuthenticationSchemes = [System.Net.AuthenticationSchemes]::Anonymous + $listener.Prefixes.Add($Url) - try { - $listener.Start() + try { + $listener.Start() - $context = $listener.GetContext() + $context = $listener.GetContext() - # Read Input Stream - $buffer = New-Object 'byte[]' -ArgumentList 1024 - $ms = New-Object 'IO.MemoryStream' - $read = 0 - while (($read = $context.Request.InputStream.Read($buffer, 0, 1024)) -gt 0) { - $ms.Write($buffer, 0, $read); - } - $bodyData = $ms.ToArray() - $ms.Dispose() + # Read Input Stream + $buffer = New-Object 'byte[]' -ArgumentList 1024 + $ms = New-Object 'IO.MemoryStream' + $read = 0 + while (($read = $context.Request.InputStream.Read($buffer, 0, 1024)) -gt 0) { + $ms.Write($buffer, 0, $read); + } + $bodyData = $ms.ToArray() + $ms.Dispose() - # Read Headers - $headers = @{} - for($i =0; $i -lt $context.Request.Headers.Count; $i++) { - $headers[$context.Request.Headers.GetKey($i)] = $context.Request.Headers.GetValues($i) - } + # Read Headers + $headers = @{} + for ($i = 0; $i -lt $context.Request.Headers.Count; $i++) { + $headers[$context.Request.Headers.GetKey($i)] = $context.Request.Headers.GetValues($i) + } - $cloudEvent = ConvertFrom-HttpMessage -Headers $headers -Body $bodyData + $cloudEvent = ConvertFrom-HttpMessage -Headers $headers -Body $bodyData - if ( $cloudEvent -ne $null ) { - $Handler.Invoke($cloudEvent, $context.Response) - $context.Response.Close(); - } else { - $context.Response.StatusCode = [int]([System.Net.HttpStatusCode]::BadRequest) - $context.Response.Close(); - } + if ( $cloudEvent -ne $null ) { + $Handler.Invoke($cloudEvent, $context.Response) + $context.Response.Close(); + } + else { + $context.Response.StatusCode = [int]([System.Net.HttpStatusCode]::BadRequest) + $context.Response.Close(); + } - } catch { - Write-Error $_ - $context.Response.StatusCode = [int]([System.Net.HttpStatusCode]::InternalServerError) - $context.Response.Close(); - } finally { - $listener.Stop() - } + } + catch { + Write-Error $_ + $context.Response.StatusCode = [int]([System.Net.HttpStatusCode]::InternalServerError) + $context.Response.Close(); + } + finally { + $listener.Stop() + } } $global:serverStopRequested = $false while ( -not $global:serverStopRequested ) { - Start-HttpCloudEventListener -Url $ServerUrl -Handler { - $requestCloudEvent = $args[0] - $response = $args[1] - - # When CloudEvent Type is 'echo-structured' or 'echo-binary' the Server responds - # with CloudEvent in corresponding content mode - if ( $requestCloudEvent.Type -eq $script:EchoBinaryType -or ` - $requestCloudEvent.Type -eq $script:EchoStructuredType ) { - - # Create Cloud Event for the response - $cloudEvent = New-CloudEvent ` - -Type $requestCloudEvent.Type ` - -Source $script:ServerSource ` - -Time (Get-Date) ` - -Id $requestCloudEvent.Id - - # Add Data to the new Cloud Event - $requestCloudEventJsonData = $requestCloudEvent | Read-CloudEventJsonData - $requestCloudEventXmlData = $requestCloudEvent | Read-CloudEventXmlData -ConvertMode 'SkipAttributes' - if ($requestCloudEventJsonData) { - $cloudEvent = $cloudEvent | Set-CloudEventJsonData ` - -Data $requestCloudEventJsonData - } elseif ($requestCloudEventXmlData) { - $cloudEvent = $cloudEvent | Set-CloudEventXmlData ` - -Data $requestCloudEventXmlData ` - -AttributesKeysInElementAttributes $false - } else { - $requestCloudEventData = $requestCloudEvent | Read-CloudEventData - $cloudEvent = $cloudEvent | Set-CloudEventData ` - -Data $requestCloudEventData ` - -DataContentType $requestCloudEvent.DataContentType - } - - # Convert Cloud Event to HTTP Response - $contentMode = $requestCloudEvent.Type.TrimStart('echo-') - $httpMessage = $cloudEvent | ConvertTo-HttpMessage -ContentMode $contentMode - - $response.Headers = New-Object 'System.Net.WebHeaderCollection' - foreach ($keyValue in $httpMessage.Headers.GetEnumerator()) { - $response.Headers.Add($keyValue.Key, $keyValue.Value) - } - $response.ContentLength64 = $httpMessage.Body.Length - $response.OutputStream.Write($httpMessage.Body, 0, $httpMessage.Body.Length) - $response.StatusCode = [int]([System.Net.HttpStatusCode]::OK) - - } else { - # No Content in all other cases - $response.StatusCode = [int]([System.Net.HttpStatusCode]::NoContent) - } - - if ( $requestCloudEvent.Type -eq $script:ServerStopType ) { - # Server Stop is requested - $global:serverStopRequested = $true - } - } + try { + Start-HttpCloudEventListener -Url $ServerUrl -Handler { + $requestCloudEvent = $args[0] + $response = $args[1] + + # When CloudEvent Type is 'echo-structured' or 'echo-binary' the Server responds + # with CloudEvent in corresponding content mode + if ( $requestCloudEvent.Type -eq $script:EchoBinaryType -or ` + $requestCloudEvent.Type -eq $script:EchoStructuredType ) { + + # Create Cloud Event for the response + $cloudEvent = New-CloudEvent ` + -Type $requestCloudEvent.Type ` + -Source $script:ServerSource ` + -Time (Get-Date) ` + -Id $requestCloudEvent.Id + + # Add Data to the new Cloud Event + $requestCloudEventJsonData = $requestCloudEvent | Read-CloudEventJsonData + $requestCloudEventXmlData = $requestCloudEvent | Read-CloudEventXmlData -ConvertMode 'SkipAttributes' + if ($requestCloudEventJsonData) { + $cloudEvent = $cloudEvent | Set-CloudEventJsonData ` + -Data $requestCloudEventJsonData + } + elseif ($requestCloudEventXmlData) { + $cloudEvent = $cloudEvent | Set-CloudEventXmlData ` + -Data $requestCloudEventXmlData ` + -AttributesKeysInElementAttributes $false + } + else { + $requestCloudEventData = $requestCloudEvent | Read-CloudEventData + $cloudEvent = $cloudEvent | Set-CloudEventData ` + -Data $requestCloudEventData ` + -DataContentType $requestCloudEvent.DataContentType + } + + # Convert Cloud Event to HTTP Response + $contentMode = $requestCloudEvent.Type.TrimStart('echo-') + $httpMessage = $cloudEvent | ConvertTo-HttpMessage -ContentMode $contentMode + + $response.Headers = New-Object 'System.Net.WebHeaderCollection' + foreach ($keyValue in $httpMessage.Headers.GetEnumerator()) { + $response.Headers.Add($keyValue.Key, $keyValue.Value) + } + $response.ContentLength64 = $httpMessage.Body.Length + $response.OutputStream.Write($httpMessage.Body, 0, $httpMessage.Body.Length) + $response.StatusCode = [int]([System.Net.HttpStatusCode]::OK) + + } + else { + # No Content in all other cases + $response.StatusCode = [int]([System.Net.HttpStatusCode]::NoContent) + } + + if ( $requestCloudEvent.Type -eq $script:ServerStopType ) { + # Server Stop is requested + $global:serverStopRequested = $true + } + } + } catch { + Write-Error $_ + break + } } \ No newline at end of file diff --git a/test/integration/ProtocolConstants.ps1 b/test/integration/ProtocolConstants.ps1 index 45b9400..569b516 100644 --- a/test/integration/ProtocolConstants.ps1 +++ b/test/integration/ProtocolConstants.ps1 @@ -7,4 +7,5 @@ New-Variable -Option Constant -Scope 'script' -Name 'ClientSource' -Value 'ps:te New-Variable -Option Constant -Scope 'script' -Name 'ServerSource' -Value 'ps:test:server' New-Variable -Option Constant -Scope 'script' -Name 'EchoBinaryType' -Value 'echo-binary' New-Variable -Option Constant -Scope 'script' -Name 'EchoStructuredType' -Value 'echo-structured' -New-Variable -Option Constant -Scope 'script' -Name 'ServerStopType' -Value 'server-stop' \ No newline at end of file +New-Variable -Option Constant -Scope 'script' -Name 'ServerStopType' -Value 'server-stop' +New-Variable -Option Constant -Scope 'script' -Name 'ServerPingType' -Value 'server-ping' \ No newline at end of file diff --git a/test/unit/ConvertFrom-HttpMessage.Tests.ps1 b/test/unit/ConvertFrom-HttpMessage.Tests.ps1 index 145338f..fb6bc6d 100644 --- a/test/unit/ConvertFrom-HttpMessage.Tests.ps1 +++ b/test/unit/ConvertFrom-HttpMessage.Tests.ps1 @@ -30,7 +30,7 @@ Describe "ConvertFrom-HttpMessage Function Tests" { 'Content-Type' = @($expectedDataContentType, 'charset=utf-8') 'ce-specversion' = $expectedSpecVersion 'ce-type' = $expectedType - 'ce-time' = $expectedTime.ToString("u") + 'ce-time' = $expectedTime.ToString('yyyy-MM-ddTHH:mm:ss.fffZ') 'ce-id' = $expectedId 'ce-source' = $expectedSource } @@ -139,7 +139,7 @@ Describe "ConvertFrom-HttpMessage Function Tests" { $structuredJsonBody = @{ 'specversion' = $expectedSpecVersion 'type' = $expectedType - 'time' = $expectedTime.ToString("u") + 'time' = $expectedTime.ToString('yyyy-MM-ddTHH:mm:ss.fffZ') 'id' = $expectedId 'source' = $expectedSource 'datacontenttype' = $expectedDataContentType diff --git a/test/unit/ConvertTo-HttpMessage.Tests.ps1 b/test/unit/ConvertTo-HttpMessage.Tests.ps1 index 7cec0e1..aa7b697 100644 --- a/test/unit/ConvertTo-HttpMessage.Tests.ps1 +++ b/test/unit/ConvertTo-HttpMessage.Tests.ps1 @@ -16,7 +16,7 @@ Describe "ConvertTo-HttpMessage Function Tests" { $expectedType = 'test' $expectedSource = 'urn:test' $expectedId = 'test-id-1' - $expectedTime = Get-Date -Year 2021 -Month 1 -Day 18 -Hour 12 -Minute 30 -Second 0 + $expectedTime = Get-Date -Year 2021 -Month 1 -Day 18 -Hour 12 -Minute 30 -Second 0 -Millisecond 0 $expectedDataContentType = 'application/json' $cloudEvent = New-CloudEvent ` @@ -44,7 +44,7 @@ Describe "ConvertTo-HttpMessage Function Tests" { $actual.Headers['ce-source'] | Should -Be $expectedSource $actual.Headers['ce-specversion'] | Should -Be $expectedSpecVersion $actual.Headers['ce-type'] | Should -Be $expectedType - $actual.Headers['ce-time'] | Should -Be '2021-01-18 12:30:00Z' + $actual.Headers['ce-time'] | Should -Be ($expectedTime.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ')) $actual.Headers['ce-id'] | Should -Be $expectedId ## Assert Body @@ -110,7 +110,7 @@ Describe "ConvertTo-HttpMessage Function Tests" { $expectedType = 'test' $expectedSource = 'urn:test' $expectedId = 'test-id-1' - $expectedTime = Get-Date -Year 2021 -Month 1 -Day 18 -Hour 12 -Minute 30 -Second 0 + $expectedTime = Get-Date -Year 2021 -Month 1 -Day 18 -Hour 12 -Minute 30 -Second 0 -Millisecond 0 $expectedDataContentType = 'application/json' $cloudEvent = New-CloudEvent ` @@ -141,7 +141,7 @@ Describe "ConvertTo-HttpMessage Function Tests" { $actual.Headers['ce-source'] | Should -Be $expectedSource $actual.Headers['ce-specversion'] | Should -Be $expectedSpecVersion $actual.Headers['ce-type'] | Should -Be $expectedType - $actual.Headers['ce-time'] | Should -Be '2021-01-18 12:30:00Z' + $actual.Headers['ce-time'] | Should -Be ($expectedTime.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ')) $actual.Headers['ce-id'] | Should -Be $expectedId ## Assert Body