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:
Steve Lee
2018-07-24 10:56:00 +05:00
committed by Ilya
parent 80d2af8934
commit f5a7d79175
5 changed files with 49 additions and 9 deletions
@@ -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 {
+1 -1
View File
@@ -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);
}
}
}
+2
View File
@@ -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.