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 @@ -1789,7 +1789,7 @@ internal void ParseLinkHeader(HttpResponseMessage response, System.Uri requestUr

// 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>.*?)\"";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not introduce any overlapping terms

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved, I misread the Regex

IEnumerable<string> links;
if (response.Headers.TryGetValues("Link", out links))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion test/tools/WebListener/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ internal static class Constants
{
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\"";
Expand Down
24 changes: 17 additions & 7 deletions test/tools/WebListener/Controllers/LinkController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,24 @@ public JsonResult Index()

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;
Expand All @@ -65,7 +75,7 @@ public JsonResult Index()

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;
Expand Down Expand Up @@ -96,9 +106,9 @@ public IActionResult Error()
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 changes: 2 additions & 0 deletions test/tools/WebListener/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down