Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
0e33ca5
Implement HttpVersion parameter for Invoke-RestMethod and Invoke-WebR…
hayhay27 Aug 1, 2021
a61cb24
Add HttpVersion parameter to Invoke-WebRequest
hayhay27 Aug 1, 2021
c4fdc68
Add HttpVersion parameter to Invoke-WebRequest
hayhay27 Aug 1, 2021
7335a4c
Apply suggestions from code review
hayhay27 Aug 1, 2021
fc17e44
Add HttpVersion parameter to Invoke-WebRequest
hayhay27 Aug 1, 2021
0c708d7
Add HttpVersion parameter to Invoke-WebRequest
hayhay27 Aug 1, 2021
886809a
Add HttpVersion parameter to Invoke-WebRequest
hayhay27 Aug 1, 2021
364b459
Add HttpVersion parameter to Invoke-WebRequest
hayhay27 Aug 1, 2021
6951148
Add HttpVersion parameter to Invoke-WebRequest
hayhay27 Aug 9, 2021
1c01483
Add HttpVersion parameter to Invoke-WebRequest
hayhay27 Aug 9, 2021
5078cdb
Add HttpVersion parameter to Invoke-WebRequest
hayhay27 Aug 21, 2021
40a5e8f
Add HttpVersion parameter to Invoke-WebRequest
hayhay27 Aug 21, 2021
0452c71
Add HttpVersion parameter to Invoke-WebRequest
hayhay27 Aug 25, 2021
fa4c937
Update src/Microsoft.PowerShell.Commands.Utility/commands/utility/Web…
iSazonov Aug 25, 2021
1662308
Add HttpVersion parameter to Invoke-WebRequest
hayhay27 Aug 25, 2021
546ca85
Add HttpVersion parameter to Invoke-WebRequest
hayhay27 Sep 7, 2021
2bca829
Add HttpVersion parameter to Invoke-WebRequest
hayhay27 Sep 9, 2021
83ce173
Add HttpVersion parameter to Invoke-WebRequest
hayhay27 Sep 9, 2021
27b46b3
Add HttpVersion parameter to Invoke-WebRequest
hayhay27 Oct 8, 2021
2fb1df8
Add HttpVersion parameter to Invoke-WebRequest
hayhay27 Nov 2, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#nullable enable

using System;
using System.Collections.Generic;
using System.Management.Automation;
using System.Net;
using System.Reflection;

namespace Microsoft.PowerShell.Commands
{
/// <summary>
/// A completer for HTTP version names.
/// </summary>
internal sealed class HttpVersionCompletionsAttribute : ArgumentCompletionsAttribute
{
public static readonly string[] AllowedVersions;

static HttpVersionCompletionsAttribute()
{
FieldInfo[] fields = typeof(HttpVersion).GetFields(BindingFlags.Static | BindingFlags.Public);

var versions = new List<string>(fields.Length - 1);

for (int i = 0; i < fields.Length; i++)
{
// skip field Unknown and not Version type
if (fields[i].Name == nameof(HttpVersion.Unknown) || fields[i].FieldType != typeof(Version))
{
continue;
}

var version = (Version?)fields[i].GetValue(null);

if (version is not null)
{
versions.Add(version.ToString());
}
}

AllowedVersions = versions.ToArray();
}

/// <inheritdoc/>
public HttpVersionCompletionsAttribute() : base(AllowedVersions)
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,18 @@ public abstract partial class WebRequestPSCmdlet : PSCmdlet

#endregion

#region HTTP Version

/// <summary>
/// Gets or sets the HTTP Version property.
/// </summary>
[Parameter]
[ArgumentToVersionTransformation]
[HttpVersionCompletions]
public virtual Version HttpVersion { get; set; }

#endregion

#region Session
/// <summary>
/// Gets or sets the Session property.
Expand Down Expand Up @@ -1081,6 +1093,11 @@ internal virtual HttpRequestMessage GetRequest(Uri uri)
// create the base WebRequest object
var request = new HttpRequestMessage(httpMethod, requestUri);

if (HttpVersion is not null)
{
request.Version = HttpVersion;
}

// pull in session data
if (WebSession.Headers.Count > 0)
{
Expand Down Expand Up @@ -1413,6 +1430,7 @@ internal virtual HttpResponseMessage GetResponse(HttpClient client, HttpRequestM
string reqVerboseMsg = string.Format(
CultureInfo.CurrentCulture,
WebCmdletStrings.WebMethodInvocationVerboseMsg,
requestWithoutRange.Version,
requestWithoutRange.Method,
requestWithoutRange.RequestUri,
requestContentLength);
Expand Down Expand Up @@ -1505,10 +1523,14 @@ protected override void ProcessRecord()
if (request.Content != null)
requestContentLength = request.Content.Headers.ContentLength.Value;

string reqVerboseMsg = string.Format(CultureInfo.CurrentCulture,
string reqVerboseMsg = string.Format(
CultureInfo.CurrentCulture,
WebCmdletStrings.WebMethodInvocationVerboseMsg,
request.Version,
request.Method,
request.RequestUri,
requestContentLength);

WriteVerbose(reqVerboseMsg);

HttpResponseMessage response = GetResponse(client, request, keepAuthorization);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@
<data name="AuthenticationCredentialNotSupplied" xml:space="preserve">
<value>The cmdlet cannot run because the following parameter is not specified: Credential. The supplied Authentication type requires a Credential. Specify Credential, then retry.</value>
</data>
<data name="AuthenticationTokenNotSupplied" xml:space="preserve">
<data name="AuthenticationTokenNotSupplied" xml:space="preserve">
<value>The cmdlet cannot run because the following parameter is not specified: Token. The supplied Authentication type requires a Token. Specify Token, then retry.</value>
</data>
<data name="AuthenticationTokenConflict" xml:space="preserve">
Expand Down Expand Up @@ -250,7 +250,7 @@
<value>Following rel link {0}</value>
</data>
<data name="WebMethodInvocationVerboseMsg" xml:space="preserve">
<value>{0} with {1}-byte payload</value>
<value>HTTP/{0} {1} {2} with {3}-byte payload</value>
</data>
<data name="WebMethodResumeFailedVerboseMsg" xml:space="preserve">
<value>The remote server indicated it could not resume downloading. The local file will be overwritten.</value>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#nullable enable

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace System.Management.Automation
{
/// <summary>
/// To make it easier to specify a version, we add some conversions that wouldn't happen otherwise:
/// * A simple integer, i.e. 2;
/// * A string without a dot, i.e. "2".
/// </summary>
internal class ArgumentToVersionTransformationAttribute : ArgumentTransformationAttribute
{
/// <inheritdoc/>
public override object Transform(EngineIntrinsics engineIntrinsics, object inputData)
{
object version = PSObject.Base(inputData);

if (version is string versionStr)
{
if (TryConvertFromString(versionStr, out var convertedVersion))
{
return convertedVersion;
}

if (versionStr.Contains('.'))
{
// If the string contains a '.', let the Version constructor handle the conversion.
return inputData;
}
}

if (version is double)
{
// The conversion to int below is wrong, but the usual conversions will turn
// the double into a string, so just return the original object.
return inputData;
}

if (LanguagePrimitives.TryConvertTo<int>(version, out var majorVersion))
{
return new Version(majorVersion, 0);
}

return inputData;
}

protected virtual bool TryConvertFromString(string versionString, [NotNullWhen(true)] out Version? version)
{
version = null;
return false;
}
}
}
46 changes: 10 additions & 36 deletions src/System.Management.Automation/engine/InternalCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Collections.Generic;
using System.Dynamic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Management.Automation;
using System.Management.Automation.Internal;
Expand All @@ -15,8 +14,10 @@
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;

using CommonParamSet = System.Management.Automation.Internal.CommonParameters;
using Dbg = System.Management.Automation.Diagnostics;
using NotNullWhen = System.Diagnostics.CodeAnalysis.NotNullWhenAttribute;

namespace Microsoft.PowerShell.Commands
{
Expand Down Expand Up @@ -2647,46 +2648,19 @@ public SwitchParameter Off
private SwitchParameter _off;

/// <summary>
/// To make it easier to specify a version, we add some conversions that wouldn't happen otherwise:
/// * A simple integer, i.e. 2
/// * A string without a dot, i.e. "2"
/// * The string 'latest', which we interpret to be the current version of PowerShell.
/// Handle 'latest', which we interpret to be the current version of PowerShell.
/// </summary>
private sealed class ArgumentToVersionTransformationAttribute : ArgumentTransformationAttribute
private sealed class ArgumentToPSVersionTransformationAttribute : ArgumentToVersionTransformationAttribute
{
public override object Transform(EngineIntrinsics engineIntrinsics, object inputData)
protected override bool TryConvertFromString(string versionString, [NotNullWhen(true)] out Version version)
{
object version = PSObject.Base(inputData);

string versionStr = version as string;
if (versionStr != null)
{
if (versionStr.Equals("latest", StringComparison.OrdinalIgnoreCase))
{
return PSVersionInfo.PSVersion;
}

if (versionStr.Contains('.'))
{
// If the string contains a '.', let the Version constructor handle the conversion.
return inputData;
}
}

if (version is double)
if (string.Equals("latest", versionString, StringComparison.OrdinalIgnoreCase))
{
// The conversion to int below is wrong, but the usual conversions will turn
// the double into a string, so just return the original object.
return inputData;
}

int majorVersion;
if (LanguagePrimitives.TryConvertTo<int>(version, out majorVersion))
{
return new Version(majorVersion, 0);
version = PSVersionInfo.PSVersion;
return true;
}

return inputData;
return base.TryConvertFromString(versionString, out version);
}
}

Expand All @@ -2711,7 +2685,7 @@ protected override void Validate(object arguments, EngineIntrinsics engineIntrin
/// Gets or sets strict mode in the current scope.
/// </summary>
[Parameter(ParameterSetName = "Version", Mandatory = true)]
[ArgumentToVersionTransformation]
[ArgumentToPSVersionTransformation]
[ValidateVersion]
[Alias("v")]
public Version Version
Expand Down
21 changes: 21 additions & 0 deletions test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -1134,6 +1134,27 @@ dir -Recurse `
$res.CompletionMatches | Should -HaveCount 4
[string]::Join(',', ($res.CompletionMatches.completiontext | Sort-Object)) | Should -BeExactly "-Path,-PipelineVariable,-PSPath,-pv"
}

It "Test completion for HttpVersion parameter name" {
$inputStr = 'Invoke-WebRequest -HttpV'
$res = TabExpansion2 -inputScript $inputStr -cursorColumn $inputStr.Length
$res.CompletionMatches | Should -HaveCount 1
$res.CompletionMatches[0].CompletionText | Should -BeExactly "-HttpVersion"
}

It "Test completion for HttpVersion parameter" {
$inputStr = 'Invoke-WebRequest -HttpVersion '
$res = TabExpansion2 -inputScript $inputStr -cursorColumn $inputStr.Length
$res.CompletionMatches | Should -HaveCount 4
[string]::Join(',', ($res.CompletionMatches.completiontext | Sort-Object)) | Should -BeExactly "1.0,1.1,2.0,3.0"
}

It "Test completion for HttpVersion parameter with input" {
$inputStr = 'Invoke-WebRequest -HttpVersion 1'
$res = TabExpansion2 -inputScript $inputStr -cursorColumn $inputStr.Length
$res.CompletionMatches | Should -HaveCount 2
[string]::Join(',', ($res.CompletionMatches.completiontext | Sort-Object)) | Should -BeExactly "1.0,1.1"
}
}

Context "Module completion for 'using module'" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,23 @@ Describe "Invoke-WebRequest tests" -Tags "Feature", "RequireAdminOnWindows" {
$result.Output.Headers.Connection | Should -Be "Close"
}

It "Validate Invoke-WebRequest -HttpVersion '<httpVersion>'" -Skip:(!$IsWindows) -TestCases @(
@{ httpVersion = '1.1'},
@{ httpVersion = '2'}
) {
param($httpVersion)
# Operation options
$uri = Get-WebListenerUrl -Test 'Get' -Https
$command = "Invoke-WebRequest -Uri $uri -HttpVersion $httpVersion -SkipCertificateCheck"

$result = ExecuteWebCommand -command $command
ValidateResponse -response $result

# Validate response content
$jsonContent = $result.Output.Content | ConvertFrom-Json
$jsonContent.protocol | Should -Be "HTTP/$httpVersion"
}

It "Validate Invoke-WebRequest -MaximumRedirection" {
$uri = Get-WebListenerUrl -Test 'Redirect' -TestValue '3'
$command = "Invoke-WebRequest -Uri '$uri' -MaximumRedirection 4"
Expand Down Expand Up @@ -2076,6 +2093,21 @@ Describe "Invoke-RestMethod tests" -Tags "Feature", "RequireAdminOnWindows" {
$result.Output.Headers.Connection | Should -Be "Close"
}

It "Validate Invoke-RestMethod -HttpVersion '<httpVersion>'" -Skip:(!$IsWindows) -TestCases @(
@{ httpVersion = '1.1'},
@{ httpVersion = '2'}
) {
param($httpVersion)
# Operation options
$uri = Get-WebListenerUrl -Test 'Get' -Https
$command = "Invoke-RestMethod -Uri $uri -HttpVersion $httpVersion -SkipCertificateCheck"

$result = ExecuteWebCommand -command $command

# Validate response
$result.Output.protocol | Should -Be "HTTP/$httpVersion"
}

It "Validate Invoke-RestMethod -MaximumRedirection" {
$uri = Get-WebListenerUrl -Test 'Redirect' -TestValue '3'
$command = "Invoke-RestMethod -Uri '$uri' -MaximumRedirection 4"
Expand Down
17 changes: 7 additions & 10 deletions test/tools/WebListener/Controllers/GetController.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http.Extensions;
using mvc.Models;
Expand All @@ -32,12 +28,13 @@ public JsonResult Index()

Hashtable output = new Hashtable
{
{"args", args},
{"headers", headers},
{"origin", Request.HttpContext.Connection.RemoteIpAddress.ToString()},
{"url", UriHelper.GetDisplayUrl(Request)},
{"query", Request.QueryString.ToUriComponent()},
{"method", Request.Method}
{ "args", args },
{ "headers", headers },
{ "origin", Request.HttpContext.Connection.RemoteIpAddress.ToString() },
{ "url", UriHelper.GetDisplayUrl(Request) },
{ "query", Request.QueryString.ToUriComponent() },
{ "method", Request.Method },
{ "protocol", Request.Protocol }
};

if (Request.HasFormContentType)
Expand Down
Loading