diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/ContentHelper.CoreClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/ContentHelper.CoreClr.cs index 261b4c98b7..4268bc63a9 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/ContentHelper.CoreClr.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/ContentHelper.CoreClr.cs @@ -7,6 +7,7 @@ Copyright (c) Microsoft Corporation. All rights reserved. using System.Text; using System.Linq; using System.Net.Http; +using System.Net.Http.Headers; namespace Microsoft.PowerShell.Commands { @@ -37,11 +38,30 @@ namespace Microsoft.PowerShell.Commands raw.AppendFormat("{0} {1} {2}", protocol, statusCode, statusDescription); raw.AppendLine(); } - - foreach (var entry in response.Headers) + + HttpHeaders[] headerCollections = { - raw.AppendFormat("{0}: {1}", entry.Key, entry.Value.FirstOrDefault()); - raw.AppendLine(); + response.Headers, + response.Content == null ? null : response.Content.Headers + }; + + foreach (var headerCollection in headerCollections) + { + if (headerCollection == null) + { + continue; + } + foreach (var header in headerCollection) + { + // Headers may have multiple entries with different values + foreach (var headerValue in header.Value) + { + raw.Append(header.Key); + raw.Append(": "); + raw.Append(headerValue); + raw.AppendLine(); + } + } } raw.AppendLine(); diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseObject.CoreClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseObject.CoreClr.cs index 79fc400af7..9e4b52bfd0 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseObject.CoreClr.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseObject.CoreClr.cs @@ -36,6 +36,13 @@ namespace Microsoft.PowerShell.Commands { headers[entry.Key] = entry.Value; } + if (BaseResponse.Content != null) + { + foreach (var entry in BaseResponse.Content.Headers) + { + headers[entry.Key] = entry.Value; + } + } return headers; } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 index 786ebb4cb1..1ae16e8a68 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 @@ -1183,6 +1183,45 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" { #endregion charset encoding tests + #region Content Header Inclusion + It "Verifies Invoke-WebRequest includes Content headers in Headers property" { + $uri = "http://localhost:8080/PowerShell?test=response&contenttype=text/plain&output=OK" + $command = "Invoke-WebRequest -Uri '$uri'" + $result = ExecuteWebCommand -command $command + ValidateResponse $result + + $result.Output.Headers.'Content-Type' | Should Be 'text/plain' + $result.Output.Headers.'Content-Length' | Should Be 2 + } + + It "Verifies Invoke-WebRequest includes Content headers in RawContent property" { + $uri = "http://localhost:8080/PowerShell?test=response&contenttype=text/plain&output=OK" + $command = "Invoke-WebRequest -Uri '$uri'" + $result = ExecuteWebCommand -command $command + ValidateResponse $result + + $result.Output.RawContent | Should Match ([regex]::Escape('Content-Type: text/plain')) + $result.Output.RawContent | Should Match ([regex]::Escape('Content-Length: 2')) + } + + It "Verifies Invoke-WebRequest Supports Multiple response headers with same name" { + $headers = @{ + 'X-Fake-Header' = 'testvalue01','testvalue02' + } | ConvertTo-Json -Compress + $uri = "http://localhost:8080/PowerShell?test=response&contenttype=text/plain&output=OK&headers=$headers" + $command = "Invoke-WebRequest -Uri '$uri'" + $result = ExecuteWebCommand -command $command + ValidateResponse $result + + $result.Output.Headers.'X-Fake-Header'.Count | Should Be 2 + $result.Output.Headers.'X-Fake-Header'.Contains('testvalue01') | Should Be $True + $result.Output.Headers.'X-Fake-Header'.Contains('testvalue02') | Should Be $True + $result.Output.RawContent | Should Match ([regex]::Escape('X-Fake-Header: testvalue01')) + $result.Output.RawContent | Should Match ([regex]::Escape('X-Fake-Header: testvalue02')) + } + + #endregion Content Header Inclusion + BeforeEach { if ($env:http_proxy) { $savedHttpProxy = $env:http_proxy diff --git a/test/tools/Modules/HttpListener/HttpListener.psm1 b/test/tools/Modules/HttpListener/HttpListener.psm1 index 8fc048c8cc..b682235617 100644 --- a/test/tools/Modules/HttpListener/HttpListener.psm1 +++ b/test/tools/Modules/HttpListener/HttpListener.psm1 @@ -133,6 +133,20 @@ Function Start-HTTPListener { $statusCode = $queryItems["statuscode"] $contentType = $queryItems["contenttype"] $output = $queryItems["output"] + + # Pass a JSON collection to the 'headers' field + # /PowerShell?test=response&headers={"Pragma":"no-cache","X-Fake-Header":["testvalue01","testvalue02"]} + # In PowerShell: + # $headers = @{Pragma='no-cache';'X-Fake-Header'='testvalue01','testvalue02'} | ConvertTo-Json -Compress + # $uri = "http://localhost:8080/PowerShell?test=response&headers=$headers" + if ($queryItems['headers']) + { + $headerCollection = $queryItems['headers'] | ConvertFrom-Json + foreach ($header in $headerCollection.psobject.Properties.name) + { + $outputHeader.add($header,$headerCollection.$header) + } + } } # Echo the request as the output. @@ -284,7 +298,10 @@ Function Start-HTTPListener { $response.Headers.Clear() foreach ($header in $outputHeader.Keys) { - $response.Headers.Add($header, $outputHeader[$header]) + foreach ($headerValue in $outputHeader.$header) + { + $response.Headers.Add($header, $headerValue) + } } if ($null -ne $output)