From 70fab5c40060e1d287feb67a58ae1cdb499f8f03 Mon Sep 17 00:00:00 2001 From: Mark Kraus Date: Sun, 10 Dec 2017 12:14:58 -0600 Subject: [PATCH 1/2] [feature] Replace Remaining HttpBin.org Tests with WebListener --- .../WebCmdlets.Tests.ps1 | 196 ++++++++--------- .../Modules/WebListener/WebListener.psm1 | 36 +-- .../WebListener/Controllers/GetController.cs | 20 +- .../Controllers/ResponseController.cs | 7 + test/tools/WebListener/README.md | 205 +++++++++++++++--- test/tools/WebListener/Startup.cs | 28 ++- .../tools/WebListener/Views/Home/Index.cshtml | 4 + 7 files changed, 340 insertions(+), 156 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 index bf77837d4fe..cf1adca48b8 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 @@ -3,7 +3,7 @@ # # This is a Pester test suite which validate the Web cmdlets. # -# Note: These tests use data from http://httpbin.org/ +# Note: These tests use data from WebListener # # Invokes the given command via script block invocation. @@ -642,7 +642,7 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" { # patch Returns PATCH data. # put Returns PUT data. # delete Returns DELETE data - $testMethods = @("GET", "POST", "PATCH", "PUT", "DELETE") + $testMethods = @("POST", "PATCH", "PUT", "DELETE") $contentTypes = @("text/plain", "application/xml", "application/json") foreach ($contentType in $contentTypes) @@ -650,20 +650,11 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" { foreach ($method in $testMethods) { # Operation options - $operation = $method.ToLower() - $uri = "http://httpbin.org/$operation" + $uri = Get-WebListenerUrl -Test $method $body = GetTestData -contentType $contentType + $command = "Invoke-WebRequest -Uri $uri -Body '$body' -Method $method -ContentType $contentType" - if ($method -eq "GET") - { - $command = "Invoke-WebRequest -Uri $uri" - } - else - { - $command = "Invoke-WebRequest -Uri $uri -Body '$body' -Method $method -ContentType $contentType -TimeoutSec 5" - } - - It "$command" { + It "Invoke-WebRequest -Uri $uri -Method $method -ContentType $contentType -Body [body data]" { $result = ExecuteWebCommand -command $command ValidateResponse -response $result @@ -671,21 +662,15 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" { # Validate response content $jsonContent = $result.Output.Content | ConvertFrom-Json $jsonContent.url | Should Match $uri - - # For a GET request, there is no data property to validate. - if ($method -ne "GET") + $jsonContent.headers.'Content-Type' | Should Match $contentType + # Validate that the response Content.data field is the same as what we sent. + if ($contentType -eq "application/xml") + { + $jsonContent.data | Should Be $body + } + else { - $jsonContent.headers.'Content-Type' | Should Match $contentType - - # Validate that the response Content.data field is the same as what we sent. - if ($contentType -eq "application/xml") - { - $jsonContent.data | Should Be $body - } - else - { - $jsonContent.data | Should Match $body - } + $jsonContent.data | Should Match $body } } } @@ -760,7 +745,8 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" { It "Validate Invoke-WebRequest default ContentType for CustomMethod POST" { - $command = "Invoke-WebRequest -Uri 'http://httpbin.org/post' -CustomMethod POST -Body 'testparam=testvalue'" + $uri = Get-WebListenerUrl -Test 'Post' + $command = "Invoke-WebRequest -Uri '$uri' -CustomMethod POST -Body 'testparam=testvalue'" $result = ExecuteWebCommand -command $command ($result.Output.Content | ConvertFrom-Json).form.testparam | Should Be "testvalue" } @@ -775,14 +761,20 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" { It "Validate Invoke-WebRequest returns HTTP errors in exception" { - $command = "Invoke-WebRequest -Uri http://httpbin.org/status/418" + $query = @{ + body = "I am a teapot!!!" + statuscode = 418 + responsephrase = "I am a teapot" + } + $uri = Get-WebListenerUrl -Test 'Response' -Query $query + $command = "Invoke-WebRequest -Uri '$uri'" $result = ExecuteWebCommand -command $command - $result.Error.ErrorDetails.Message | Should Match "\-=\[ teapot \]" + $result.Error.ErrorDetails.Message | Should be $query.body $result.Error.Exception | Should BeOfType Microsoft.PowerShell.Commands.HttpResponseException $result.Error.Exception.Response.StatusCode | Should Be 418 - $result.Error.Exception.Response.ReasonPhrase | Should Be "I'm a teapot" - $result.Error.Exception.Message | Should Match ": 418 \(I'm a teapot\)\." + $result.Error.Exception.Response.ReasonPhrase | Should Be $query.responsephrase + $result.Error.Exception.Message | Should Match ": 418 \($($query.responsephrase)\)\." $result.Error.FullyQualifiedErrorId | Should Be "WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand" } @@ -1622,7 +1614,9 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" { It "Validate Invoke-RestMethod error with -Proxy option - ''" -TestCases $testCase { param($proxy_address, $name, $protocol) - $command = "Invoke-RestMethod -Uri '${protocol}://httpbin.org/' -Proxy '${proxy_address}'" + # A non-loopback URI is required but the external resource will not be accessed + $uri = 'http://httpbin.org' + $command = "Invoke-RestMethod -Uri '$uri' -Proxy '${proxy_address}'" $result = ExecuteWebCommand -command $command $result.Error.FullyQualifiedErrorId | Should Be "WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand" @@ -1679,7 +1673,7 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" { # patch Returns PATCH data. # put Returns PUT data. # delete Returns DELETE data - $testMethods = @("GET", "POST", "PATCH", "PUT", "DELETE") + $testMethods = @("POST", "PATCH", "PUT", "DELETE") $contentTypes = @("text/plain", "application/xml", "application/json") foreach ($contentType in $contentTypes) @@ -1687,40 +1681,26 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" { foreach ($method in $testMethods) { # Operation options - $operation = $method.ToLower() - $uri = "http://httpbin.org/$operation" + $uri = Get-WebListenerUrl -Test $method $body = GetTestData -contentType $contentType + $command = "Invoke-RestMethod -Uri $uri -Body '$body' -Method $method -ContentType $contentType" - if ($method -eq "GET") - { - $command = "Invoke-RestMethod -Uri $uri" - } - else - { - $command = "Invoke-RestMethod -Uri $uri -Body '$body' -Method $method -ContentType $contentType -TimeoutSec 5" - } - - It "$command" { + It "Invoke-RestMethod -Uri $uri -Method $method -ContentType $contentType -Body [body data]" { $result = ExecuteWebCommand -command $command # Validate response $result.Output.url | Should Match $uri + $result.Output.headers.'Content-Type' | Should Match $contentType - # For a GET request, there is no data property to validate. - if ($method -ne "GET") + # Validate that the response Content.data field is the same as what we sent. + if ($contentType -eq "application/xml") { - $result.Output.headers.'Content-Type' | Should Match $contentType - - # Validate that the response Content.data field is the same as what we sent. - if ($contentType -eq "application/xml") - { - $result.Output.data | Should Be $body - } - else - { - $result.Output.data | Should Match $body - } + $result.Output.data | Should Be $body + } + else + { + $result.Output.data | Should Match $body } } } @@ -1794,7 +1774,8 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" { It "Validate Invoke-RestMethod default ContentType for CustomMethod POST" { - $command = "Invoke-RestMethod -Uri 'http://httpbin.org/post' -CustomMethod POST -Body 'testparam=testvalue'" + $uri = Get-WebListenerUrl -Test 'Post' + $command = "Invoke-RestMethod -Uri '$uri' -CustomMethod POST -Body 'testparam=testvalue'" $result = ExecuteWebCommand -command $command $result.Output.form.testparam | Should Be "testvalue" } @@ -1809,14 +1790,20 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" { It "Validate Invoke-RestMethod returns HTTP errors in exception" { - $command = "Invoke-RestMethod -Uri http://httpbin.org/status/418" + $query = @{ + body = "I am a teapot!!!" + statuscode = 418 + responsephrase = "I am a teapot" + } + $uri = Get-WebListenerUrl -Test 'Response' -Query $query + $command = "Invoke-RestMethod -Uri '$uri'" $result = ExecuteWebCommand -command $command - $result.Error.ErrorDetails.Message | Should Match "\-=\[ teapot \]" + $result.Error.ErrorDetails.Message | Should Be $query.body $result.Error.Exception | Should BeOfType Microsoft.PowerShell.Commands.HttpResponseException $result.Error.Exception.Response.StatusCode | Should Be 418 - $result.Error.Exception.Response.ReasonPhrase | Should Be "I'm a teapot" - $result.Error.Exception.Message | Should Match ": 418 \(I'm a teapot\)\." + $result.Error.Exception.Response.ReasonPhrase | Should Be $query.responsephrase + $result.Error.Exception.Message | Should Match ": 418 \($($query.responsephrase)\)\." $result.Error.FullyQualifiedErrorId | Should Be "WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand" } @@ -2489,48 +2476,50 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" { Describe "Validate Invoke-WebRequest and Invoke-RestMethod -InFile" -Tags "Feature" { Context "InFile parameter negative tests" { - - $testCases = @( + BeforeAll { + $uri = Get-WebListenerUrl -Test 'Post' + $testCases = @( #region INVOKE-WEBREQUEST - @{ - Name = 'Validate error for Invoke-WebRequest -InFile ""' - ScriptBlock = {Invoke-WebRequest -Uri http://httpbin.org/post -Method Post -InFile ""} - ExpectedFullyQualifiedErrorId = 'WebCmdletInFileNotFilePathException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand' - } + @{ + Name = 'Validate error for Invoke-WebRequest -InFile ""' + ScriptBlock = {Invoke-WebRequest -Uri $uri -Method Post -InFile ""} + ExpectedFullyQualifiedErrorId = 'WebCmdletInFileNotFilePathException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand' + } - @{ - Name = 'Validate error for Invoke-WebRequest -InFile' - ScriptBlock = {Invoke-WebRequest -Uri http://httpbin.org/post -Method Post -InFile} - ExpectedFullyQualifiedErrorId = 'MissingArgument,Microsoft.PowerShell.Commands.InvokeWebRequestCommand' - } + @{ + Name = 'Validate error for Invoke-WebRequest -InFile' + ScriptBlock = {Invoke-WebRequest -Uri $uri -Method Post -InFile} + ExpectedFullyQualifiedErrorId = 'MissingArgument,Microsoft.PowerShell.Commands.InvokeWebRequestCommand' + } - @{ - Name = "Validate error for Invoke-WebRequest -InFile $TestDrive\content.txt" - ScriptBlock = {Invoke-WebRequest -Uri http://httpbin.org/post -Method Post -InFile $TestDrive\content.txt} - ExpectedFullyQualifiedErrorId = 'PathNotFound,Microsoft.PowerShell.Commands.InvokeWebRequestCommand' - } + @{ + Name = "Validate error for Invoke-WebRequest -InFile $TestDrive\content.txt" + ScriptBlock = {Invoke-WebRequest -Uri $uri -Method Post -InFile $TestDrive\content.txt} + ExpectedFullyQualifiedErrorId = 'PathNotFound,Microsoft.PowerShell.Commands.InvokeWebRequestCommand' + } #endregion #region INVOKE-RESTMETHOD - @{ - Name = "Validate error for Invoke-RestMethod -InFile ''" - ScriptBlock = {Invoke-RestMethod -Uri http://httpbin.org/post -Method Post -InFile ''} - ExpectedFullyQualifiedErrorId = 'WebCmdletInFileNotFilePathException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand' - } + @{ + Name = "Validate error for Invoke-RestMethod -InFile ''" + ScriptBlock = {Invoke-RestMethod -Uri $uri -Method Post -InFile ''} + ExpectedFullyQualifiedErrorId = 'WebCmdletInFileNotFilePathException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand' + } - @{ - Name = "Validate error for Invoke-RestMethod -InFile " - ScriptBlock = {Invoke-RestMethod -Uri http://httpbin.org/post -Method Post -InFile} - ExpectedFullyQualifiedErrorId = 'MissingArgument,Microsoft.PowerShell.Commands.InvokeRestMethodCommand' - } + @{ + Name = "Validate error for Invoke-RestMethod -InFile " + ScriptBlock = {Invoke-RestMethod -Uri $uri -Method Post -InFile} + ExpectedFullyQualifiedErrorId = 'MissingArgument,Microsoft.PowerShell.Commands.InvokeRestMethodCommand' + } - @{ - Name = "Validate error for Invoke-RestMethod -InFile $TestDrive\content.txt" - ScriptBlock = {Invoke-RestMethod -Uri http://httpbin.org/post -Method Post -InFile $TestDrive\content.txt} - ExpectedFullyQualifiedErrorId = 'PathNotFound,Microsoft.PowerShell.Commands.InvokeRestMethodCommand' - } + @{ + Name = "Validate error for Invoke-RestMethod -InFile $TestDrive\content.txt" + ScriptBlock = {Invoke-RestMethod -Uri $uri -Method Post -InFile $TestDrive\content.txt} + ExpectedFullyQualifiedErrorId = 'PathNotFound,Microsoft.PowerShell.Commands.InvokeRestMethodCommand' + } #endregion - ) + ) + } It "" -TestCases $testCases { param ($scriptblock, $expectedFullyQualifiedErrorId) @@ -2551,18 +2540,19 @@ Describe "Validate Invoke-WebRequest and Invoke-RestMethod -InFile" -Tags "Featu BeforeAll { $filePath = Join-Path $TestDrive test.txt - New-Item -Path $filePath -Value "hello" -ItemType File -Force + New-Item -Path $filePath -Value "hello=world" -ItemType File -Force + $uri = Get-WebListenerUrl -Test 'Post' } It "Invoke-WebRequest -InFile" { - $result = Invoke-WebRequest -InFile $filePath -Uri http://httpbin.org/post -Method Post + $result = Invoke-WebRequest -InFile $filePath -Uri $uri -Method Post $content = $result.Content | ConvertFrom-Json - $content.form | Should Match "hello" + $content.form.hello[0] | Should Match "world" } It "Invoke-RestMethod -InFile" { - $result = Invoke-RestMethod -InFile $filePath -Uri http://httpbin.org/post -Method Post - $result.form | Should Match "hello" + $result = Invoke-RestMethod -InFile $filePath -Uri $uri -Method Post + $result.form.hello[0] | Should Match "world" } } } diff --git a/test/tools/Modules/WebListener/WebListener.psm1 b/test/tools/Modules/WebListener/WebListener.psm1 index 981f09f2bbd..b34ac9256e3 100644 --- a/test/tools/Modules/WebListener/WebListener.psm1 +++ b/test/tools/Modules/WebListener/WebListener.psm1 @@ -1,4 +1,4 @@ -Class WebListener +Class WebListener { [int]$HttpPort [int]$HttpsPort @@ -8,7 +8,7 @@ Class WebListener WebListener () { } - [String] GetStatus() + [String] GetStatus() { return $This.Job.JobStateInfo.State } @@ -16,23 +16,23 @@ Class WebListener [WebListener]$WebListener -function Get-WebListener +function Get-WebListener { [CmdletBinding(ConfirmImpact = 'Low')] [OutputType([WebListener])] param() - process + process { return [WebListener]$Script:WebListener } } -function Start-WebListener +function Start-WebListener { [CmdletBinding(ConfirmImpact = 'Low')] [OutputType([WebListener])] - param + param ( [ValidateRange(1,65535)] [int]$HttpPort = 8083, @@ -46,8 +46,8 @@ function Start-WebListener [ValidateRange(1,65535)] [int]$TlsPort = 8086 ) - - process + + process { $runningListener = Get-WebListener if ($null -ne $runningListener -and $runningListener.GetStatus() -eq 'Running') @@ -60,7 +60,7 @@ function Start-WebListener $serverPfx = 'ServerCert.pfx' $serverPfxPassword = 'password' $initCompleteMessage = 'Now listening on' - + $serverPfxPath = Join-Path $MyInvocation.MyCommand.Module.ModuleBase $serverPfx $timeOut = (get-date).AddSeconds($initTimeoutSeconds) $Job = Start-Job { @@ -69,7 +69,7 @@ function Start-WebListener dotnet $using:appDll $using:serverPfxPath $using:serverPfxPassword $using:HttpPort $using:HttpsPort $using:Tls11Port $using:TlsPort } $Script:WebListener = [WebListener]@{ - HttpPort = $HttpPort + HttpPort = $HttpPort HttpsPort = $HttpsPort Tls11Port = $Tls11Port TlsPort = $TlsPort @@ -83,8 +83,8 @@ function Start-WebListener $isRunning = $initStatus -match $initCompleteMessage } while (-not $isRunning -and (get-date) -lt $timeOut) - - if (-not $isRunning) + + if (-not $isRunning) { $Job | Stop-Job -PassThru | Receive-Job $Job | Remove-Job @@ -94,13 +94,13 @@ function Start-WebListener } } -function Stop-WebListener +function Stop-WebListener { [CmdletBinding(ConfirmImpact = 'Low')] [OutputType([Void])] param() - - process + + process { $Script:WebListener.job | Stop-Job -PassThru | Remove-Job $Script:WebListener = $null @@ -131,10 +131,14 @@ function Get-WebListenerUrl { 'Cert', 'Compression', 'Delay', + 'Delete', 'Encoding', 'Get', 'Home', 'Multipart', + 'Patch', + 'Post', + 'Put', 'Redirect', 'Response', 'ResponseHeaders', @@ -173,7 +177,7 @@ function Get-WebListenerUrl { { $Uri.Path = '{0}/{1}' -f $Test, $TestValue } - else + else { $Uri.Path = $Test } diff --git a/test/tools/WebListener/Controllers/GetController.cs b/test/tools/WebListener/Controllers/GetController.cs index ccd082ef13f..2b1d92ae6c9 100644 --- a/test/tools/WebListener/Controllers/GetController.cs +++ b/test/tools/WebListener/Controllers/GetController.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; @@ -29,8 +30,25 @@ public JsonResult Index() {"args" , args}, {"headers", headers}, {"origin" , Request.HttpContext.Connection.RemoteIpAddress.ToString()}, - {"url" , UriHelper.GetDisplayUrl(Request)} + {"url" , UriHelper.GetDisplayUrl(Request)}, + {"method" , Request.Method} }; + + if(Request.HasFormContentType){ + Hashtable form = new Hashtable(); + foreach (var key in Request.Form.Keys) + { + form.Add(key,Request.Form[key]); + } + output["form"] = form; + } + + string data = new StreamReader(Request.Body).ReadToEnd(); + if (!String.IsNullOrEmpty(data)) + { + output["data"] = data; + } + return Json(output); } public IActionResult Error() diff --git a/test/tools/WebListener/Controllers/ResponseController.cs b/test/tools/WebListener/Controllers/ResponseController.cs index c2688cf0556..cfef8d1aaf2 100644 --- a/test/tools/WebListener/Controllers/ResponseController.cs +++ b/test/tools/WebListener/Controllers/ResponseController.cs @@ -12,6 +12,7 @@ using mvc.Models; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Microsoft.AspNetCore.Http.Features; namespace mvc.Controllers { @@ -36,6 +37,12 @@ public String Index() Response.StatusCode = statusCode; } + StringValues responsePhrase; + if ( Request.Query.TryGetValue("responsephrase", out responsePhrase)) + { + Response.HttpContext.Features.Get().ReasonPhrase = responsePhrase.FirstOrDefault(); + } + StringValues body; if (Request.Query.TryGetValue("body", out body)) { diff --git a/test/tools/WebListener/README.md b/test/tools/WebListener/README.md index efa200a3e61..8e2320ce119 100644 --- a/test/tools/WebListener/README.md +++ b/test/tools/WebListener/README.md @@ -2,9 +2,9 @@ ASP.NET Core 2.0 app for testing HTTP and HTTPS Requests. -# Run with `dotnet` +## Run with `dotnet` -``` +```bash dotnet restore dotnet publish --output bin --configuration Release cd bin @@ -13,7 +13,7 @@ dotnet WebListener.dll ServerCert.pfx password 8083 8084 8085 8086 The test site can then be accessed via `http://localhost:8083/`, `https://localhost:8084/`, `https://localhost:8085/`, or `https://localhost:8086/`. -The `WebListener.dll` takes 6 arguments: +The `WebListener.dll` takes 6 arguments: * The path to the Server Certificate * The Server Certificate Password @@ -22,7 +22,7 @@ The `WebListener.dll` takes 6 arguments: * The TCP Port to bind on for HTTPS using TLS 1.1 * The TCP Port to bind on for HTTPS using TLS 1.0 -# Run With WebListener Module +## Run With WebListener Module ```powershell Import-Module .\build.psm1 @@ -30,13 +30,13 @@ Publish-PSTestTools $Listener = Start-WebListener -HttpPort 8083 -HttpsPort 8084 -Tls11Port 8085 -TlsPort = 8086 ``` -# Tests +## Tests -## / or /Home/ +### / or /Home/ Returns a static HTML page containing links and descriptions of the available tests in WebListener. This can be used as a default or general test where no specific test functionality or return data is required. -## /Auth/Basic/ +### /Auth/Basic/ Provides a mock Basic authentication challenge. If a basic authorization header is sent, then the same results as /Get/ are returned. @@ -51,7 +51,7 @@ Invoke-RestMethod -Uri $uri -Credential $credential -SkipCertificateCheck "headers":{ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.15063; en-US) PowerShell/6.0.0", "Connection": "Keep-Alive", - "Authorization": "Basic dGVzdHVzZXI6dGVzdHBhc3N3b3Jk", + "Authorization": "Basic dGVzdHVzZXI6dGVzdHBhc3N3b3Jk", "Host": "localhost:8084" }, "origin": "127.0.0.1", @@ -60,7 +60,7 @@ Invoke-RestMethod -Uri $uri -Credential $credential -SkipCertificateCheck } ``` -## /Auth/Negotiate/ +### /Auth/Negotiate/ Provides a mock Negotiate authentication challenge. If a basic authorization header is sent, then the same results as /Get/ are returned. @@ -74,7 +74,7 @@ Invoke-RestMethod -Uri $uri -UseDefaultCredential -SkipCertificateCheck "headers":{ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.15063; en-US) PowerShell/6.0.0", "Connection": "Keep-Alive", - "Authorization": "Negotiate jjaguasgtisi7tiqkagasjjajvs", + "Authorization": "Negotiate jjaguasgtisi7tiqkagasjjajvs", "Host": "localhost:8084" }, "origin": "127.0.0.1", @@ -83,7 +83,7 @@ Invoke-RestMethod -Uri $uri -UseDefaultCredential -SkipCertificateCheck } ``` -## /Auth/NTLM/ +### /Auth/NTLM/ Provides a mock NTLM authentication challenge. If a basic authorization header is sent, then the same results as /Get/ are returned. @@ -97,7 +97,7 @@ Invoke-RestMethod -Uri $uri -UseDefaultCredential -SkipCertificateCheck "headers":{ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.15063; en-US) PowerShell/6.0.0", "Connection": "Keep-Alive", - "Authorization": "NTLM jjaguasgtisi7tiqkagasjjajvs", + "Authorization": "NTLM jjaguasgtisi7tiqkagasjjajvs", "Host": "localhost:8084" }, "origin": "127.0.0.1", @@ -106,17 +106,18 @@ Invoke-RestMethod -Uri $uri -UseDefaultCredential -SkipCertificateCheck } ``` -## /Cert/ +### /Cert/ Returns a JSON object containing the details of the Client Certificate if one is provided in the request. ```powershell $certificate = Get-WebListenerClientCertificate -$uri = Get-WebListenerUrl -Test 'Cert' -Https +$uri = Get-WebListenerUrl -Test 'Cert' -Https Invoke-RestMethod -Uri $uri -Certificate $certificate ``` Response when certificate is provided in request: + ```json { "Status": "OK", @@ -131,13 +132,15 @@ Response when certificate is provided in request: ``` Response when certificate is not provided in request: + ```json { "Status": "FAILED" } ``` -## /Compression/Deflate/ +### /Compression/Deflate/ + Returns the same results as the Get test with deflate compression. ```powershell @@ -157,7 +160,8 @@ Invoke-RestMethod -Uri $uri } ``` -## /Compression/Gzip/ +### /Compression/Gzip/ + Returns the same results as the Get test with gzip compression. ```powershell @@ -177,7 +181,7 @@ Invoke-RestMethod -Uri $uri } ``` -## /Delay/ +### /Delay/ Returns the same results as the Get test. If a number is supplied, the server will wait that many seconds before returning a response. This can be used to test timeouts. @@ -191,7 +195,6 @@ After 5 Seconds: ```json { "args": { - }, "origin": "127.0.0.1", "headers": { @@ -202,7 +205,33 @@ After 5 Seconds: } ``` -## /Encoding/Utf8/ +### /Delete/ + +Returns the same results as the Get test. Will only accept the `DELETE` request method. + +```powershell +$uri = Get-WebListenerUrl -Test 'Delete' +$Body = @{id = 12345} | ConvertTo-Json -Compress +Invoke-RestMethod -Uri $uri -Body $body -Method 'Delete' +``` + +```json +{ + "method": "DELETE", + "headers": { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.15063; en-US) PowerShell/6.0.0", + "Connection": "Keep-Alive", + "Host": "localhost:8083", + "Content-Length": "12" + }, + "origin": "127.0.0.1", + "url": "http://localhost:8083/Delete", + "args": {}, + "data": "{\"id\":12345}" +} +``` + +### /Encoding/Utf8/ Returns page containing UTF-8 data. @@ -211,8 +240,7 @@ $uri = Get-WebListenerUrl -Test 'Encoding' -TestValue 'Utf8' Invoke-RestMethod -Uri $uri ``` - -## /Get/ +### /Get/ Returns a JSON object containing the Request URL, Request Headers, GET Query Fields and Values, and Origin IP. This emulates the functionality of [HttpBin's get test](https://httpbin.org/get). @@ -223,25 +251,28 @@ Invoke-RestMethod -Uri $uri -Body @{TestField = 'TestValue'} ```json { - "url": "http://localhost:8083/Get/?TestField=TestValue", + "origin": "127.0.0.1", + "url": "http://localhost:8083/Get?TestField=TestValue", + "method": "GET", "args": { "TestField": "TestValue" }, "headers": { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.15063; en-US) PowerShell/6.0.0", "Connection": "Keep-Alive", - "User-Agent": "Mozilla/5.0 (Windows NT; Microsoft Windows 10.0.15063 ; en-US) WindowsPowerShell/6.0.0", "Host": "localhost:8083" - }, - "origin": "127.0.0.1" + } } ``` -## /Multipart/ +### /Multipart/ + +#### GET -### GET Provides an HTML form for `multipart/form-data` submission. -### POST +#### POST + Accepts a `multipart/form-data` submission and returns a JSON object containing information about the submission including the items and files submitted. ```powershell @@ -285,7 +316,115 @@ Invoke-RestMethod -Uri $uri -Body $multipartData -Method 'POST' } ``` -## /Redirect/ +### /Patch/ + +Returns the same results as the Get test. Will only accept the `PATCH` request method. + +```powershell +$uri = Get-WebListenerUrl -Test 'Patch' +$Body = @{id = 12345} | ConvertTo-Json -Compress +Invoke-RestMethod -Uri $uri -Body $body -Method 'Patch' +``` + +```json +{ + "method": "PATCH", + "headers": { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.15063; en-US) PowerShell/6.0.0", + "Connection": "Keep-Alive", + "Host": "localhost:8083", + "Content-Length": "12" + }, + "origin": "127.0.0.1", + "url": "http://localhost:8083/Patch", + "args": {}, + "data": "{\"id\":12345}" +} +``` + +### /Post/ + +Returns the same results as the Get test. Will only accept the `POST` request method. If the POST request is sent with a forms based content type the body will be interpreted as a form instead of raw data. + +```powershell +$uri = Get-WebListenerUrl -Test 'Post' +$Body = @{id = 12345} +Invoke-RestMethod -Uri $uri -Body $body -Method 'Post' +``` + +```json +{ + "method": "POST", + "headers": { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.15063; en-US) PowerShell/6.0.0", + "Connection": "Keep-Alive", + "Host": "localhost:8083", + "Content-Type": "application/x-www-form-urlencoded", + "Content-Length": "8" + }, + "form": { + "id": [ + "12345" + ] + }, + "origin": "127.0.0.1", + "url": "http://localhost:8083/Post", + "args": {} +} +``` + +Otherwise, the body will be interpreted as raw data. + +```powershell +$uri = Get-WebListenerUrl -Test 'Post' +$Body = @{id = 12345} | ConvertTo-Json -Compress +Invoke-RestMethod -Uri $uri -Body $body -Method 'Post' -ContentType 'application/json' +``` + +```json +{ + "method": "POST", + "headers": { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.15063; en-US) PowerShell/6.0.0", + "Connection": "Keep-Alive", + "Host": "localhost:8083", + "Content-Type": "application/json", + "Content-Length": "12" + }, + "origin": "127.0.0.1", + "url": "http://localhost:8083/Post", + "args": {}, + "data": "{\"id\":12345}" +} +``` + +### /Put/ + +Returns the same results as the Get test. Will only accept the `PUT` request method. + +```powershell +$uri = Get-WebListenerUrl -Test 'Put' +$Body = @{id = 12345} | ConvertTo-Json -Compress +Invoke-RestMethod -Uri $uri -Body $body -Method 'Put' +``` + +```json +{ + "method": "PUT", + "headers": { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.15063; en-US) PowerShell/6.0.0", + "Connection": "Keep-Alive", + "Host": "localhost:8083", + "Content-Length": "12" + }, + "origin": "127.0.0.1", + "url": "http://localhost:8083/Put", + "args": {}, + "data": "{\"id\":12345}" +} +``` + +### /Redirect/ Will 302 redirect to `/Get/`. If a number is supplied, redirect will occur that many times. Can be used to test maximum redirects. @@ -320,6 +459,7 @@ Location: /Redirect/1 ``` Request 2: + ```none GET http://localhost:8083/Redirect/1 HTTP/1.1 Connection: Keep-Alive @@ -328,6 +468,7 @@ Host: localhost:8083 ``` Response 2: + ```none HTTP/1.1 302 Found Date: Fri, 15 Sep 2017 10:46:41 GMT @@ -342,7 +483,7 @@ Location: /Get/

You should be redirected automatically to target URL: /Get/. If not click the link. ``` -## /Response/ +### /Response/ Will return a response crafted from the query string. The following four fields are supported: @@ -377,13 +518,13 @@ Response Body: {"key1": "value1"} ``` -## /ResponseHeaders/ +### /ResponseHeaders/ Will return the response headers passed in query string. The response body will be the supplied headers as a JSON object. ```powershell $uri = Get-WebListenerUrl -Test 'ResponseHeaders' -Query @{'Content-Type' = 'custom'; 'x-header-01' = 'value01'; 'x-header-02' = 'value02'} -Invoke-RestMethod -Uri $uri +Invoke-RestMethod -Uri $uri ``` Response Headers: diff --git a/test/tools/WebListener/Startup.cs b/test/tools/WebListener/Startup.cs index 00553c5e247..1c2417a45cd 100644 --- a/test/tools/WebListener/Startup.cs +++ b/test/tools/WebListener/Startup.cs @@ -4,6 +4,8 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.Routing.Constraints; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -46,13 +48,31 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env) routes.MapRoute( name: "redirect", template: "Redirect/{count?}", - defaults: new {controller = "Redirect", action = "Index"} - ); + defaults: new {controller = "Redirect", action = "Index"}); routes.MapRoute( name: "delay", template: "Delay/{seconds?}", - defaults: new {controller = "Delay", action = "Index"} - ); + defaults: new {controller = "Delay", action = "Index"}); + routes.MapRoute( + name: "post", + template: "Post", + defaults: new {controller = "Get", action = "Index"}, + constraints: new RouteValueDictionary(new { httpMethod = new HttpMethodRouteConstraint("POST") })); + routes.MapRoute( + name: "put", + template: "Put", + defaults: new {controller = "Get", action = "Index"}, + constraints: new RouteValueDictionary(new { httpMethod = new HttpMethodRouteConstraint("PUT") })); + routes.MapRoute( + name: "patch", + template: "Patch", + defaults: new {controller = "Get", action = "Index"}, + constraints: new RouteValueDictionary(new { httpMethod = new HttpMethodRouteConstraint("PATCH") })); + routes.MapRoute( + name: "delete", + template: "Delete", + defaults: new {controller = "Get", action = "Index"}, + constraints: new RouteValueDictionary(new { httpMethod = new HttpMethodRouteConstraint("DELETE") })); }); } } diff --git a/test/tools/WebListener/Views/Home/Index.cshtml b/test/tools/WebListener/Views/Home/Index.cshtml index 253ed6879e4..a9c9a8818fd 100644 --- a/test/tools/WebListener/Views/Home/Index.cshtml +++ b/test/tools/WebListener/Views/Home/Index.cshtml @@ -8,9 +8,13 @@

  • /Compression/Deflate/ - Returns deflate compressed response
  • /Compression/Gzip/ - Returns gzip compressed response
  • /Delay/{seconds} - Delays response for seconds seconds.
  • +
  • /Delete/ - returns data from a DELETE request
  • /Encoding/Utf8/ - Returns page containing UTF-8 data.
  • /Get/ - Emulates functionality of https://httpbin.org/get by returning GET headers, Arguments, and Request URL
  • /Multipart/ - Multipart/form-data submission testing
  • +
  • /Patch/ - returns data from a PATCH request
  • +
  • /Post/ - returns data from a POST request
  • +
  • /Put/ - returns data from a PUT request
  • /Redirect/{count} - 302 redirect count times.
  • /Response/?statuscode=<StatusCode>&body=<ResponseBody>&contenttype=<ResponseContentType>&headers=<JsonHeadersObject> - Returns the given response.
  • /ResponseHeaders/?key=val - Returns given response headers.
  • From 466d01997be2698b728f251a8faa6d6b9aceee51 Mon Sep 17 00:00:00 2001 From: Mark Kraus Date: Mon, 11 Dec 2017 15:44:27 -0600 Subject: [PATCH 2/2] [Feature] Address PR Feedback --- .../WebCmdlets.Tests.ps1 | 31 +++++++------------ .../WebListener/Controllers/GetController.cs | 3 +- test/tools/WebListener/README.md | 2 ++ 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 index cf1adca48b8..98e86925975 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 @@ -664,14 +664,7 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" { $jsonContent.url | Should Match $uri $jsonContent.headers.'Content-Type' | Should Match $contentType # Validate that the response Content.data field is the same as what we sent. - if ($contentType -eq "application/xml") - { - $jsonContent.data | Should Be $body - } - else - { - $jsonContent.data | Should Match $body - } + $jsonContent.data | Should Be $body } } } @@ -748,7 +741,9 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" { $uri = Get-WebListenerUrl -Test 'Post' $command = "Invoke-WebRequest -Uri '$uri' -CustomMethod POST -Body 'testparam=testvalue'" $result = ExecuteWebCommand -command $command - ($result.Output.Content | ConvertFrom-Json).form.testparam | Should Be "testvalue" + $jsonResult = $result.Output.Content | ConvertFrom-Json + $jsonResult.form.testparam | Should Be "testvalue" + $jsonResult.Headers.'Content-Type' | Should Be "application/x-www-form-urlencoded" } It "Validate Invoke-WebRequest body is converted to query params for CustomMethod GET" { @@ -770,8 +765,8 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" { $command = "Invoke-WebRequest -Uri '$uri'" $result = ExecuteWebCommand -command $command - $result.Error.ErrorDetails.Message | Should be $query.body - $result.Error.Exception | Should BeOfType Microsoft.PowerShell.Commands.HttpResponseException + $result.Error.ErrorDetails.Message | Should Be $query.body + $result.Error.Exception | Should BeOfType 'Microsoft.PowerShell.Commands.HttpResponseException' $result.Error.Exception.Response.StatusCode | Should Be 418 $result.Error.Exception.Response.ReasonPhrase | Should Be $query.responsephrase $result.Error.Exception.Message | Should Match ": 418 \($($query.responsephrase)\)\." @@ -1694,14 +1689,7 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" { $result.Output.headers.'Content-Type' | Should Match $contentType # Validate that the response Content.data field is the same as what we sent. - if ($contentType -eq "application/xml") - { - $result.Output.data | Should Be $body - } - else - { - $result.Output.data | Should Match $body - } + $result.Output.data | Should Be $body } } } @@ -1778,6 +1766,7 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" { $command = "Invoke-RestMethod -Uri '$uri' -CustomMethod POST -Body 'testparam=testvalue'" $result = ExecuteWebCommand -command $command $result.Output.form.testparam | Should Be "testvalue" + $result.Output.Headers.'Content-Type' | Should Be "application/x-www-form-urlencoded" } It "Validate Invoke-RestMethod body is converted to query params for CustomMethod GET" { @@ -1800,7 +1789,7 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" { $result = ExecuteWebCommand -command $command $result.Error.ErrorDetails.Message | Should Be $query.body - $result.Error.Exception | Should BeOfType Microsoft.PowerShell.Commands.HttpResponseException + $result.Error.Exception | Should BeOfType 'Microsoft.PowerShell.Commands.HttpResponseException' $result.Error.Exception.Response.StatusCode | Should Be 418 $result.Error.Exception.Response.ReasonPhrase | Should Be $query.responsephrase $result.Error.Exception.Message | Should Match ": 418 \($($query.responsephrase)\)\." @@ -2547,11 +2536,13 @@ Describe "Validate Invoke-WebRequest and Invoke-RestMethod -InFile" -Tags "Featu It "Invoke-WebRequest -InFile" { $result = Invoke-WebRequest -InFile $filePath -Uri $uri -Method Post $content = $result.Content | ConvertFrom-Json + $content.form.hello.Count | Should Be 1 $content.form.hello[0] | Should Match "world" } It "Invoke-RestMethod -InFile" { $result = Invoke-RestMethod -InFile $filePath -Uri $uri -Method Post + $result.form.hello.Count | Should Be 1 $result.form.hello[0] | Should Match "world" } } diff --git a/test/tools/WebListener/Controllers/GetController.cs b/test/tools/WebListener/Controllers/GetController.cs index 2b1d92ae6c9..59df91e0f68 100644 --- a/test/tools/WebListener/Controllers/GetController.cs +++ b/test/tools/WebListener/Controllers/GetController.cs @@ -34,7 +34,8 @@ public JsonResult Index() {"method" , Request.Method} }; - if(Request.HasFormContentType){ + if (Request.HasFormContentType) + { Hashtable form = new Hashtable(); foreach (var key in Request.Form.Keys) { diff --git a/test/tools/WebListener/README.md b/test/tools/WebListener/README.md index 8e2320ce119..4be07b52287 100644 --- a/test/tools/WebListener/README.md +++ b/test/tools/WebListener/README.md @@ -491,10 +491,12 @@ Will return a response crafted from the query string. The following four fields * `statuscode` - the HTTP Status Code to return * `contenttype` - The `Content-Type` response header * `headers` - a JSON string containing response headers. `Content-Type` will be ignored in `headers`. Use `contenttype` instead. +* `responsephrase` - the HTTP response phrase to return ```powershell $Query = @{ statsucode = 200 + responsephrase = 'OK' contenttype = 'application/json' body = '{"key1": "value1"}' headers = @{