Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Expand Up @@ -993,7 +993,7 @@ internal virtual HttpClient GetHttpClient(bool handleRedirect)
return httpClient;
}

internal virtual HttpRequestMessage GetRequest(Uri uri, bool stripAuthorization)
internal virtual HttpRequestMessage GetRequest(Uri uri)
{
Uri requestUri = PrepareUri(uri);
HttpMethod httpMethod = null;
Expand Down Expand Up @@ -1032,14 +1032,6 @@ internal virtual HttpRequestMessage GetRequest(Uri uri, bool stripAuthorization)
}
else
{
if (stripAuthorization
&&
String.Equals(entry.Key, HttpKnownHeaderNames.Authorization.ToString(), StringComparison.OrdinalIgnoreCase)
)
{
continue;
}

if (SkipHeaderValidation)
{
request.Headers.TryAddWithoutValidation(entry.Key, entry.Value);
Expand Down Expand Up @@ -1268,15 +1260,15 @@ static bool IsRedirectToGet(HttpStatusCode code)
||
code == HttpStatusCode.RedirectMethod
||
code == HttpStatusCode.TemporaryRedirect
code == HttpStatusCode.SeeOther
||
code == HttpStatusCode.RedirectKeepVerb
code == HttpStatusCode.Ambiguous
||
code == HttpStatusCode.SeeOther
code == HttpStatusCode.MultipleChoices
);
}

internal virtual HttpResponseMessage GetResponse(HttpClient client, HttpRequestMessage request, bool stripAuthorization)
internal virtual HttpResponseMessage GetResponse(HttpClient client, HttpRequestMessage request, bool keepAuthorization)
{
if (client == null) { throw new ArgumentNullException("client"); }
if (request == null) { throw new ArgumentNullException("request"); }
Expand All @@ -1287,7 +1279,7 @@ internal virtual HttpResponseMessage GetResponse(HttpClient client, HttpRequestM
_cancelToken = new CancellationTokenSource();
HttpResponseMessage response = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, _cancelToken.Token).GetAwaiter().GetResult();

if (stripAuthorization && IsRedirectCode(response.StatusCode) && response.Headers.Location != null)
if (keepAuthorization && IsRedirectCode(response.StatusCode) && response.Headers.Location != null)
{
_cancelToken.Cancel();
_cancelToken = null;
Expand All @@ -1306,14 +1298,12 @@ internal virtual HttpResponseMessage GetResponse(HttpClient client, HttpRequestM
Method = WebRequestMethod.Get;
}

// recreate the HttpClient with redirection enabled since the first call suppressed redirection
currentUri = new Uri(request.RequestUri, response.Headers.Location);
using (client = GetHttpClient(false))
using (HttpRequestMessage redirectRequest = GetRequest(currentUri, stripAuthorization:true))
// Continue to handle redirection
using (client = GetHttpClient(handleRedirect: true))
using (HttpRequestMessage redirectRequest = GetRequest(currentUri))
{
FillRequestStream(redirectRequest);
_cancelToken = new CancellationTokenSource();
response = client.SendAsync(redirectRequest, HttpCompletionOption.ResponseHeadersRead, _cancelToken.Token).GetAwaiter().GetResult();
response = GetResponse(client, redirectRequest, keepAuthorization);
}
}

Expand All @@ -1333,7 +1323,7 @@ internal virtual HttpResponseMessage GetResponse(HttpClient client, HttpRequestM
// are treated as a standard -OutFile request. This also disables appending local file.
Resume = new SwitchParameter(false);

using (HttpRequestMessage requestWithoutRange = GetRequest(currentUri, stripAuthorization:false))
using (HttpRequestMessage requestWithoutRange = GetRequest(currentUri))
{
FillRequestStream(requestWithoutRange);
long requestContentLength = 0;
Expand All @@ -1347,7 +1337,7 @@ internal virtual HttpResponseMessage GetResponse(HttpClient client, HttpRequestM
requestContentLength);
WriteVerbose(reqVerboseMsg);

return GetResponse(client, requestWithoutRange, stripAuthorization);
return GetResponse(client, requestWithoutRange, keepAuthorization);
}
}

Expand Down Expand Up @@ -1377,15 +1367,15 @@ protected override void ProcessRecord()

// if the request contains an authorization header and PreserveAuthorizationOnRedirect is not set,
// it needs to be stripped on the first redirect.
bool stripAuthorization = null != WebSession
bool keepAuthorization = null != WebSession
&&
null != WebSession.Headers
&&
!PreserveAuthorizationOnRedirect.IsPresent
PreserveAuthorizationOnRedirect.IsPresent
&&
WebSession.Headers.ContainsKey(HttpKnownHeaderNames.Authorization.ToString());

using (HttpClient client = GetHttpClient(stripAuthorization))
using (HttpClient client = GetHttpClient(keepAuthorization))
{
int followedRelLink = 0;
Uri uri = Uri;
Expand All @@ -1399,7 +1389,7 @@ protected override void ProcessRecord()
WriteVerbose(linkVerboseMsg);
}

using (HttpRequestMessage request = GetRequest(uri, stripAuthorization:false))
using (HttpRequestMessage request = GetRequest(uri))
{
FillRequestStream(request);
try
Expand All @@ -1415,7 +1405,7 @@ protected override void ProcessRecord()
requestContentLength);
WriteVerbose(reqVerboseMsg);

HttpResponseMessage response = GetResponse(client, request, stripAuthorization);
HttpResponseMessage response = GetResponse(client, request, keepAuthorization);

string contentType = ContentHelper.GetContentType(response);
string respVerboseMsg = string.Format(CultureInfo.CurrentCulture,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,8 +349,8 @@ function GetMultipartBody {
for additonal details.
#>
$redirectTests = @(
@{redirectType = 'MultipleChoices'; redirectedMethod = 'POST'}
@{redirectType = 'Ambiguous'; redirectedMethod = 'POST'} # Synonym for MultipleChoices
@{redirectType = 'MultipleChoices'; redirectedMethod = 'GET'}
@{redirectType = 'Ambiguous'; redirectedMethod = 'GET'} # Synonym for MultipleChoices

@{redirectType = 'Moved'; redirectedMethod = 'GET'}
@{redirectType = 'MovedPermanently'; redirectedMethod = 'GET'} # Synonym for Moved
Expand All @@ -361,8 +361,8 @@ $redirectTests = @(
@{redirectType = 'redirectMethod'; redirectedMethod = 'GET'}
@{redirectType = 'SeeOther'; redirectedMethod = 'GET'} # Synonym for RedirectMethod

@{redirectType = 'TemporaryRedirect'; redirectedMethod = 'GET'}
@{redirectType = 'RedirectKeepVerb'; redirectedMethod = 'GET'} # Synonym for TemporaryRedirect
@{redirectType = 'TemporaryRedirect'; redirectedMethod = 'POST'}
@{redirectType = 'RedirectKeepVerb'; redirectedMethod = 'POST'} # Synonym for TemporaryRedirect

@{redirectType = 'relative'; redirectedMethod = 'GET'}
)
Expand Down Expand Up @@ -778,7 +778,7 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" {

#region Redirect tests

It "Validates Invoke-WebRequest with -PreserveAuthorizationOnRedirect preserves the authorization header on redirect: <redirectType> <redirectedMethod>" -Pending -TestCases $redirectTests {
It "Validates Invoke-WebRequest with -PreserveAuthorizationOnRedirect preserves the authorization header on redirect: <redirectType> <redirectedMethod>" -TestCases $redirectTests {
param($redirectType, $redirectedMethod)
$uri = Get-WebListenerUrl -Test 'Redirect' -Query @{type = $redirectType}
$response = ExecuteRedirectRequest -Uri $uri -PreserveAuthorizationOnRedirect
Expand All @@ -787,7 +787,7 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" {
$response.Content.Headers."Authorization" | Should -BeExactly "test"
}

It "Validates Invoke-WebRequest preserves the authorization header on multiple redirects: <redirectType>" -Pending -TestCases $redirectTests {
It "Validates Invoke-WebRequest preserves the authorization header on multiple redirects: <redirectType>" -TestCases $redirectTests {
param($redirectType)
$uri = Get-WebListenerUrl -Test 'Redirect' -TestValue 3 -Query @{type = $redirectType}
$response = ExecuteRedirectRequest -Uri $uri -PreserveAuthorizationOnRedirect
Expand All @@ -796,7 +796,7 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" {
$response.Content.Headers."Authorization" | Should -BeExactly "test"
}

It "Validates Invoke-WebRequest strips the authorization header on various redirects: <redirectType>" -Pending -TestCases $redirectTests {
It "Validates Invoke-WebRequest strips the authorization header on various redirects: <redirectType>" -TestCases $redirectTests {
param($redirectType)
$uri = Get-WebListenerUrl -Test 'Redirect' -Query @{type = $redirectType}
$response = ExecuteRedirectRequest -Uri $uri
Expand All @@ -810,7 +810,7 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" {

# NOTE: Only testing redirection of POST -> GET for unique underlying values of HttpStatusCode.
# Some names overlap in underlying value.
It "Validates Invoke-WebRequest strips the authorization header redirects and switches from POST to GET when it handles the redirect: <redirectType> <redirectedMethod>" -Pending -TestCases $redirectTests {
It "Validates Invoke-WebRequest strips the authorization header redirects and switches from POST to GET when it handles the redirect: <redirectType> <redirectedMethod>" -TestCases $redirectTests {
param($redirectType, $redirectedMethod)
$uri = Get-WebListenerUrl -Test 'Redirect' -Query @{type = $redirectType}
$response = ExecuteRedirectRequest -Uri $uri -Method 'POST'
Expand All @@ -824,6 +824,20 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" {
$response.Content.Method | Should -Be $redirectedMethod
}

It "Validates Invoke-WebRequest -PreserveAuthorizationOnRedirect keeps the authorization header redirects and switches from POST to GET when it handles the redirect: <redirectType> <redirectedMethod>" -TestCases $redirectTests {
param($redirectType, $redirectedMethod)
$uri = Get-WebListenerUrl -Test 'Redirect' -Query @{type = $redirectType}
$response = ExecuteRedirectRequest -PreserveAuthorizationOnRedirect -Uri $uri -Method 'POST'

$response.Error | Should -BeNullOrEmpty
# ensure user-agent is present (i.e., no false positives )
$response.Content.Headers."User-Agent" | Should -Not -BeNullOrEmpty
# ensure Authorization header has been removed.
$response.Content.Headers."Authorization" | Should -BeExactly 'test'
# ensure POST was changed to GET for selected redirections and remains as POST for others.
$response.Content.Method | Should -Be $redirectedMethod
}

It "Validates Invoke-WebRequest handles responses without Location header for requests with Authorization header and redirect: <redirectType>" -TestCases $redirectTests {
param($redirectType, $redirectedMethod)
# Skip relative test as it is not a valid response type.
Expand Down Expand Up @@ -2113,7 +2127,7 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" {

#region Redirect tests

It "Validates Invoke-RestMethod with -PreserveAuthorizationOnRedirect preserves the authorization header on redirect: <redirectType> <redirectedMethod>" -Pending -TestCases $redirectTests {
It "Validates Invoke-RestMethod with -PreserveAuthorizationOnRedirect preserves the authorization header on redirect: <redirectType> <redirectedMethod>" -TestCases $redirectTests {
param($redirectType, $redirectedMethod)
$uri = Get-WebListenerUrl -Test 'Redirect' -Query @{type = $redirectType}
$response = ExecuteRedirectRequest -Cmdlet 'Invoke-RestMethod' -Uri $uri -PreserveAuthorizationOnRedirect
Expand All @@ -2123,7 +2137,7 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" {
$response.Content.Headers."Authorization" | Should -BeExactly "test"
}

It "Validates Invoke-RestMethod preserves the authorization header on multiple redirects: <redirectType>" -Pending -TestCases $redirectTests {
It "Validates Invoke-RestMethod preserves the authorization header on multiple redirects: <redirectType>" -TestCases $redirectTests {
param($redirectType)
$uri = Get-WebListenerUrl -Test 'Redirect' -TestValue 3 -Query @{type = $redirectType}
$response = ExecuteRedirectRequest -Cmdlet 'Invoke-RestMethod' -Uri $uri -PreserveAuthorizationOnRedirect
Expand All @@ -2133,7 +2147,7 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" {
$response.Content.Headers."Authorization" | Should -BeExactly "test"
}

It "Validates Invoke-RestMethod strips the authorization header on various redirects: <redirectType>" -Pending -TestCases $redirectTests {
It "Validates Invoke-RestMethod strips the authorization header on various redirects: <redirectType>" -TestCases $redirectTests {
param($redirectType)
$uri = Get-WebListenerUrl -Test 'Redirect' -Query @{type = $redirectType}
$response = ExecuteRedirectRequest -Cmdlet 'Invoke-RestMethod' -Uri $uri
Expand All @@ -2147,7 +2161,7 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" {

# NOTE: Only testing redirection of POST -> GET for unique underlying values of HttpStatusCode.
# Some names overlap in underlying value.
It "Validates Invoke-RestMethod strips the authorization header redirects and switches from POST to GET when it handles the redirect: <redirectType> <redirectedMethod>" -Pending -TestCases $redirectTests {
It "Validates Invoke-RestMethod strips the authorization header redirects and switches from POST to GET when it handles the redirect: <redirectType> <redirectedMethod>" -TestCases $redirectTests {
param($redirectType, $redirectedMethod)
$uri = Get-WebListenerUrl -Test 'Redirect' -Query @{type = $redirectType}
$response = ExecuteRedirectRequest -Cmdlet 'Invoke-RestMethod' -Uri $uri -Method 'POST'
Expand All @@ -2156,7 +2170,21 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" {
# ensure user-agent is present (i.e., no false positives )
$response.Content.Headers."User-Agent" | Should -Not -BeNullOrEmpty
# ensure Authorization header has been removed.
$response.Content."Authorization" | Should -BeNullOrEmpty
$response.Content.Headers."Authorization" | Should -BeNullOrEmpty
# ensure POST was changed to GET for selected redirections and remains as POST for others.
$response.Content.Method | Should -Be $redirectedMethod
}

It "Validates Invoke-RestMethod -PreserveAuthorizationOnRedirect keeps the authorization header redirects and switches from POST to GET when it handles the redirect: <redirectType> <redirectedMethod>" -TestCases $redirectTests {
param($redirectType, $redirectedMethod)
$uri = Get-WebListenerUrl -Test 'Redirect' -Query @{type = $redirectType}
$response = ExecuteRedirectRequest -PreserveAuthorizationOnRedirect -Cmdlet 'Invoke-RestMethod' -Uri $uri -Method 'POST'

$response.Error | Should -BeNullOrEmpty
# ensure user-agent is present (i.e., no false positives )
$response.Content.Headers."User-Agent" | Should -Not -BeNullOrEmpty
# ensure Authorization header has been removed.
$response.Content.Headers."Authorization" | Should -BeExactly 'test'
# ensure POST was changed to GET for selected redirections and remains as POST for others.
$response.Content.Method | Should -Be $redirectedMethod
}
Expand Down