mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Add support for parsing Link Header with variable whitespace (#7322)
Fix is to change the regex to allow for variable or no whitespace and associated tests to handle these two cases.
This commit is contained in:
+1
-1
@@ -1787,7 +1787,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
|
||||
// we only support the URL in angle brackets and `rel`, other attributes are ignored
|
||||
// user can still parse it themselves via the Headers property
|
||||
string pattern = "<(?<url>.*?)>;\\srel=\"(?<rel>.*?)\"";
|
||||
string pattern = "<(?<url>.*?)>;\\s*rel=\"(?<rel>.*?)\"";
|
||||
IEnumerable<string> links;
|
||||
if (response.Headers.TryGetValues("Link", out links))
|
||||
{
|
||||
|
||||
@@ -739,6 +739,23 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" {
|
||||
$result.Output.RelationLink["self"] | Should -BeExactly "${baseUri}?maxlinks=3&linknumber=1&type=${type}"
|
||||
}
|
||||
|
||||
It "Validate Invoke-WebRequest handles different whitespace for Link Headers: <type>" -TestCases @(
|
||||
@{ type = "noWhitespace" }
|
||||
@{ type = "extraWhitespace" }
|
||||
) {
|
||||
param($type)
|
||||
$uri = Get-WebListenerUrl -Test 'Link' -Query @{type = $type}
|
||||
$command = "Invoke-WebRequest -Uri '$uri'"
|
||||
$result = ExecuteWebCommand -command $command
|
||||
|
||||
$result.Output.RelationLink.Count | Should -BeExactly 4
|
||||
$baseUri = Get-WebListenerUrl -Test 'Link'
|
||||
$result.Output.RelationLink["last"] | Should -BeExactly "${baseUri}?maxlinks=3&linknumber=3&type=${type}"
|
||||
$result.Output.RelationLink["first"] | Should -BeExactly "${baseUri}?maxlinks=3&linknumber=1&type=${type}"
|
||||
$result.Output.RelationLink["self"] | Should -BeExactly "${baseUri}?maxlinks=3&linknumber=1&type=${type}"
|
||||
$result.Output.RelationLink["next"] | Should -BeExactly "${baseUri}?maxlinks=3&linknumber=2&type=${type}"
|
||||
}
|
||||
|
||||
#region Redirect tests
|
||||
|
||||
It "Validates Invoke-WebRequest with -PreserveAuthorizationOnRedirect preserves the authorization header on redirect: <redirectType> <redirectedMethod>" -TestCases $redirectTests {
|
||||
@@ -2072,6 +2089,17 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" {
|
||||
$result.Output.linknumber | Should -BeExactly 1
|
||||
}
|
||||
|
||||
It "Validate Invoke-RestMethod handles whitespace for Link Headers if -FollowRelLink is specified: <type>" -TestCases @(
|
||||
@{ type = "noWhitespace" }
|
||||
@{ type = "extraWhitespace" }
|
||||
) {
|
||||
param($type)
|
||||
$uri = Get-WebListenerUrl -Test 'Link' -Query @{type = $type}
|
||||
$command = "Invoke-RestMethod -Uri '$uri' -FollowRelLink"
|
||||
$result = ExecuteWebCommand -command $command
|
||||
1..3 | ForEach-Object { $result.Output[$_ - 1].linknumber | Should -BeExactly $_ }
|
||||
}
|
||||
|
||||
#region Redirect tests
|
||||
|
||||
It "Validates Invoke-RestMethod with -PreserveAuthorizationOnRedirect preserves the authorization header on redirect: <redirectType> <redirectedMethod>" -TestCases $redirectTests {
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace mvc.Controllers
|
||||
{
|
||||
public const string HeaderSeparator = ", ";
|
||||
public const string ApplicationJson = "application/json";
|
||||
public const string LinkUriTemplate = "<{0}?maxlinks={1}&linknumber={2}&type={3}>; rel=\"{4}\"";
|
||||
public const string LinkUriTemplate = "<{0}?maxlinks={1}&linknumber={2}&type={3}>;{4}rel=\"{5}\"";
|
||||
public const string MalformedUrlLinkHeader = "{url}; foo";
|
||||
public const string NoRelLinkHeader = "<url>; foo=\"bar\"";
|
||||
public const string NoUrlLinkHeader = "<>; rel=\"next\"";
|
||||
|
||||
@@ -31,14 +31,24 @@ namespace mvc.Controllers
|
||||
|
||||
string type = Request.Query.TryGetValue("type", out StringValues typeSV) ? typeSV.FirstOrDefault() : "default";
|
||||
|
||||
string whitespace = " ";
|
||||
if (type.ToUpper() == "EXTRAWHITESPACE")
|
||||
{
|
||||
whitespace = " ";
|
||||
}
|
||||
else if (type.ToUpper() == "NOWHITESPACE")
|
||||
{
|
||||
whitespace = string.Empty;
|
||||
}
|
||||
|
||||
var linkList = new List<String>();
|
||||
if (maxLinks > 1 && linkNumber > 1)
|
||||
{
|
||||
linkList.Add(GetLink(baseUri: baseUri, maxLinks: maxLinks, linkNumber: linkNumber - 1, type: type, rel: "prev"));
|
||||
linkList.Add(GetLink(baseUri: baseUri, maxLinks: maxLinks, linkNumber: linkNumber - 1, type: type, whitespace: whitespace, rel: "prev"));
|
||||
}
|
||||
linkList.Add(GetLink(baseUri: baseUri, maxLinks: maxLinks, linkNumber: maxLinks, type: type, rel: "last"));
|
||||
linkList.Add(GetLink(baseUri: baseUri, maxLinks: maxLinks, linkNumber: 1, type: type, rel: "first"));
|
||||
linkList.Add(GetLink(baseUri: baseUri, maxLinks: maxLinks, linkNumber: linkNumber, type: type, rel: "self"));
|
||||
linkList.Add(GetLink(baseUri: baseUri, maxLinks: maxLinks, linkNumber: maxLinks, type: type, whitespace: whitespace, rel: "last"));
|
||||
linkList.Add(GetLink(baseUri: baseUri, maxLinks: maxLinks, linkNumber: 1, type: type, whitespace: whitespace, rel: "first"));
|
||||
linkList.Add(GetLink(baseUri: baseUri, maxLinks: maxLinks, linkNumber: linkNumber, type: type, whitespace: whitespace, rel: "self"));
|
||||
|
||||
bool sendMultipleHeaders = false;
|
||||
bool skipNextLink = false;
|
||||
@@ -65,7 +75,7 @@ namespace mvc.Controllers
|
||||
|
||||
if (!skipNextLink && maxLinks > 1 && linkNumber < maxLinks)
|
||||
{
|
||||
linkList.Add(GetLink(baseUri: baseUri, maxLinks: maxLinks, linkNumber: linkNumber + 1, type: type, rel: "next"));
|
||||
linkList.Add(GetLink(baseUri: baseUri, maxLinks: maxLinks, linkNumber: linkNumber + 1, type: type, whitespace: whitespace, rel: "next"));
|
||||
}
|
||||
|
||||
StringValues linkHeader;
|
||||
@@ -96,9 +106,9 @@ namespace mvc.Controllers
|
||||
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
||||
}
|
||||
|
||||
private string GetLink(string baseUri, int maxLinks, int linkNumber, string type, string rel)
|
||||
private string GetLink(string baseUri, int maxLinks, int linkNumber, string whitespace, string type, string rel)
|
||||
{
|
||||
return String.Format(Constants.LinkUriTemplate, baseUri, maxLinks, linkNumber, type, rel);
|
||||
return string.Format(Constants.LinkUriTemplate, baseUri, maxLinks, linkNumber, type, whitespace, rel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,6 +277,8 @@ Returns Link response headers to test paginated results. The endpoint accepts 3
|
||||
* `nourl` - Returns a Link header that does not include the URI portion. Suppresses `next` link.
|
||||
* `malformed` - Returns a malformed Link header. Suppresses `next` link.
|
||||
* `multiple` - Returns multiple Link headers instead of a single Link header and returns `next` link if one is available.
|
||||
* `nowhitespace` - Returns `default` links without any whitespace between the semicolon and `rel`
|
||||
* `extrawhitespace` - Returns `default` links with double whitespace between the semicolon and `rel`
|
||||
|
||||
The body will contain the same results as `/Get/` with the addition of the `type`, `linknumber`, and `maxlinks` for the current page.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user