Allow using a folder path in WebCmdlet's OutFile parameter (#19007)

This commit is contained in:
CarloToso
2023-03-21 13:06:50 +05:00
committed by GitHub
parent 4dfcbb313e
commit 5b1dedac61
6 changed files with 72 additions and 4 deletions
@@ -135,8 +135,12 @@ namespace Microsoft.PowerShell.Commands
}
}
else if (ShouldSaveToOutFile)
{
StreamHelper.SaveStreamToFile(baseResponseStream, QualifiedOutFile, this, response.Content.Headers.ContentLength.GetValueOrDefault(), _cancelToken.Token);
{
string outFilePath = WebResponseHelper.GetOutFilePath(response, _qualifiedOutFile);
WriteVerbose(string.Create(System.Globalization.CultureInfo.InvariantCulture, $"File Name: {Path.GetFileName(_qualifiedOutFile)}"));
StreamHelper.SaveStreamToFile(baseResponseStream, outFilePath, this, response.Content.Headers.ContentLength.GetValueOrDefault(), _cancelToken.Token);
}
if (!string.IsNullOrEmpty(StatusCodeVariable))
@@ -484,6 +484,8 @@ namespace Microsoft.PowerShell.Commands
internal string QualifiedOutFile => QualifyFilePath(OutFile);
internal string _qualifiedOutFile;
internal bool ShouldCheckHttpStatus => !SkipHttpErrorCheck;
/// <summary>
@@ -852,7 +854,7 @@ namespace Microsoft.PowerShell.Commands
}
// Output ??
if (PassThru && OutFile is null)
if (PassThru.IsPresent && OutFile is null)
{
ErrorRecord error = GetValidationError(WebCmdletStrings.OutFileMissing, "WebCmdletOutFileMissingException", nameof(PassThru));
ThrowTerminatingError(error);
@@ -864,6 +866,15 @@ namespace Microsoft.PowerShell.Commands
ErrorRecord error = GetValidationError(WebCmdletStrings.OutFileMissing, "WebCmdletOutFileMissingException", nameof(Resume));
ThrowTerminatingError(error);
}
_qualifiedOutFile = ShouldSaveToOutFile ? QualifiedOutFile : null;
// OutFile must not be a directory to use Resume.
if (Resume.IsPresent && Directory.Exists(_qualifiedOutFile))
{
ErrorRecord error = GetValidationError(WebCmdletStrings.ResumeNotFilePath, "WebCmdletResumeNotFilePathException", _qualifiedOutFile);
ThrowTerminatingError(error);
}
}
internal virtual void PrepareSession()
@@ -1093,6 +1104,7 @@ namespace Microsoft.PowerShell.Commands
if (Resume.IsPresent)
{
FileInfo fileInfo = new(QualifiedOutFile);
if (fileInfo.Exists)
{
request.Headers.Range = new RangeHeaderValue(fileInfo.Length, null);
@@ -56,7 +56,11 @@ namespace Microsoft.PowerShell.Commands
if (ShouldSaveToOutFile)
{
StreamHelper.SaveStreamToFile(responseStream, QualifiedOutFile, this, response.Content.Headers.ContentLength.GetValueOrDefault(), _cancelToken.Token);
string outFilePath = WebResponseHelper.GetOutFilePath(response, _qualifiedOutFile);
WriteVerbose(string.Create(System.Globalization.CultureInfo.InvariantCulture, $"File Name: {Path.GetFileName(_qualifiedOutFile)}"));
StreamHelper.SaveStreamToFile(responseStream, outFilePath, this, response.Content.Headers.ContentLength.GetValueOrDefault(), _cancelToken.Token);
}
}
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net.Http;
namespace Microsoft.PowerShell.Commands
@@ -34,6 +35,14 @@ namespace Microsoft.PowerShell.Commands
return headers;
}
internal static string GetOutFilePath(HttpResponseMessage response, string _qualifiedOutFile)
{
// Get file name from last segment of Uri
string lastUriSegment = System.Net.WebUtility.UrlDecode(response.RequestMessage.RequestUri.Segments[^1]);
return Directory.Exists(_qualifiedOutFile) ? Path.Join(_qualifiedOutFile, lastUriSegment) : _qualifiedOutFile;
}
internal static string GetProtocol(HttpResponseMessage response) => string.Create(CultureInfo.InvariantCulture, $"HTTP/{response.Version}");
internal static int GetStatusCode(HttpResponseMessage response) => (int)response.StatusCode;
@@ -203,6 +203,9 @@
</data>
<data name="ReadResponseProgressStatus" xml:space="preserve">
<value>Downloaded: {0} of {1}</value>
</data>
<data name="ResumeNotFilePath" xml:space="preserve">
<value>The Resume switch can only be used if OutFile targets a file but it resolves to a directory: {0}.</value>
</data>
<data name="SessionConflict" xml:space="preserve">
<value>The cmdlet cannot run because the following conflicting parameters are specified: Session and SessionVariable. Specify either Session or SessionVariable, then retry.</value>
@@ -735,6 +735,19 @@ Describe "Invoke-WebRequest tests" -Tags "Feature", "RequireAdminOnWindows" {
$jsonContent.headers.Host | Should -Be $uri.Authority
}
It "Invoke-WebRequest -OutFile folder Downloads the file and names it" {
$uri = Get-WebListenerUrl -Test 'Get'
$content = Invoke-WebRequest -Uri $uri
$outFile = Join-Path $TestDrive $content.BaseResponse.RequestMessage.RequestUri.Segments[-1]
# ensure the file does not exist
Remove-Item -Force -ErrorAction Ignore -Path $outFile
Invoke-WebRequest -Uri $uri -OutFile $TestDrive
Test-Path $outFile | Should -Be $true
Get-Item $outFile | Select-Object -ExpandProperty Length | Should -Be $content.Content.Length
}
It "Invoke-WebRequest should fail if -OutFile is <Name>." -TestCases @(
@{ Name = "empty"; Value = [string]::Empty }
@{ Name = "null"; Value = $null }
@@ -2000,6 +2013,11 @@ Describe "Invoke-WebRequest tests" -Tags "Feature", "RequireAdminOnWindows" {
Should -Throw -ErrorId 'WebCmdletOutFileMissingException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand'
}
It "Invoke-WebRequest -Resume should fail if -OutFile folder" {
{ Invoke-WebRequest -Resume -Uri $resumeUri -OutFile $TestDrive -ErrorAction Stop } |
Should -Throw -ErrorId 'WebCmdletResumeNotFilePathException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand'
}
It "Invoke-WebRequest -Resume Downloads the whole file when the file does not exist" {
$response = Invoke-WebRequest -Uri $resumeUri -OutFile $outFile -Resume -PassThru
@@ -2691,6 +2709,19 @@ Describe "Invoke-RestMethod tests" -Tags "Feature", "RequireAdminOnWindows" {
$jsonContent.headers.Host | Should -Be $uri.Authority
}
It "Invoke-RestMethod -OutFile folder Downloads the file and names it" {
$uri = Get-WebListenerUrl -Test 'Get'
$content = Invoke-WebRequest -Uri $uri
$outFile = Join-Path $TestDrive $content.BaseResponse.RequestMessage.RequestUri.Segments[-1]
# ensure the file does not exist
Remove-Item -Force -ErrorAction Ignore -Path $outFile
Invoke-RestMethod -Uri $uri -OutFile $TestDrive
Test-Path $outFile | Should -Be $true
Get-Item $outFile | Select-Object -ExpandProperty Length | Should -Be $content.Content.Length
}
It "Invoke-RestMethod should fail if -OutFile is <Name>." -TestCases @(
@{ Name = "empty"; Value = [string]::Empty }
@{ Name = "null"; Value = $null }
@@ -3881,6 +3912,11 @@ Describe "Invoke-RestMethod tests" -Tags "Feature", "RequireAdminOnWindows" {
Should -Throw -ErrorId 'WebCmdletOutFileMissingException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand'
}
It "Invoke-RestMethod -Resume should fail if -OutFile folder" {
{ Invoke-RestMethod -Resume -Uri $resumeUri -OutFile $TestDrive -ErrorAction Stop } |
Should -Throw -ErrorId 'WebCmdletResumeNotFilePathException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand'
}
It "Invoke-RestMethod -Resume Downloads the whole file when the file does not exist" {
# ensure the file does not exist
Remove-Item -Force -ErrorAction 'SilentlyContinue' -Path $outFile