mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Replace HttpListener Link Header Tests with WebListener (#5806)
- Add Link controller to WebListener - Replace HttpListener Link tests with WebListener - Update WebListener Documentation - Enable cross-platform multiple Link header tests
This commit is contained in:
@@ -712,33 +712,31 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" {
|
||||
}
|
||||
|
||||
It "Validate Invoke-WebRequest returns valid RelationLink property with absolute uris if Link Header is present" {
|
||||
$command = "Invoke-WebRequest -Uri 'http://localhost:8080/PowerShell?test=linkheader&maxlinks=5'"
|
||||
$result = ExecuteWebCommand -command $command
|
||||
$result.Output.RelationLink.Count | Should BeExactly 2
|
||||
$result.Output.RelationLink["next"] | Should BeExactly "http://localhost:8080/PowerShell?test=linkheader&maxlinks=5&linknumber=2"
|
||||
$result.Output.RelationLink["last"] | Should BeExactly "http://localhost:8080/PowerShell?test=linkheader&maxlinks=5&linknumber=5"
|
||||
}
|
||||
|
||||
# Test pending support for multiple header capable server on Linux/macOS see issue #4639
|
||||
It "Validate Invoke-WebRequest returns valid RelationLink property with absolute uris if Multiple Link Headers are present" -Pending:$(!$IsWindows) {
|
||||
$Query = @{
|
||||
body = "ok"
|
||||
contenttype = 'text/plain'
|
||||
headers = @{
|
||||
Link = @(
|
||||
'<http://localhost:8080/PowerShell?test=linkheader&maxlinks=5&linknumber=1>; rel="self"'
|
||||
'<http://localhost:8080/PowerShell?test=linkheader&maxlinks=5&linknumber=2>; rel="next"'
|
||||
'<http://localhost:8080/PowerShell?test=linkheader&maxlinks=5&linknumber=5>; rel="last"'
|
||||
)
|
||||
} | ConvertTo-Json -Compress
|
||||
}
|
||||
$Uri = Get-WebListenerUrl -Test 'Response' -Query $Query
|
||||
$uri = Get-WebListenerUrl -Test 'Link' -Query @{maxlinks = 5; linknumber = 2}
|
||||
$command = "Invoke-WebRequest -Uri '$uri'"
|
||||
$result = ExecuteWebCommand -command $command
|
||||
$result.Output.RelationLink.Count | Should BeExactly 3
|
||||
$result.Output.RelationLink["self"] | Should BeExactly "http://localhost:8080/PowerShell?test=linkheader&maxlinks=5&linknumber=1"
|
||||
$result.Output.RelationLink["next"] | Should BeExactly "http://localhost:8080/PowerShell?test=linkheader&maxlinks=5&linknumber=2"
|
||||
$result.Output.RelationLink["last"] | Should BeExactly "http://localhost:8080/PowerShell?test=linkheader&maxlinks=5&linknumber=5"
|
||||
|
||||
$result.Output.RelationLink.Count | Should BeExactly 5
|
||||
$baseUri = Get-WebListenerUrl -Test 'Link'
|
||||
$result.Output.RelationLink["next"] | Should BeExactly "${baseUri}?maxlinks=5&linknumber=3&type=default"
|
||||
$result.Output.RelationLink["last"] | Should BeExactly "${baseUri}?maxlinks=5&linknumber=5&type=default"
|
||||
$result.Output.RelationLink["prev"] | Should BeExactly "${baseUri}?maxlinks=5&linknumber=1&type=default"
|
||||
$result.Output.RelationLink["first"] | Should BeExactly "${baseUri}?maxlinks=5&linknumber=1&type=default"
|
||||
$result.Output.RelationLink["self"] | Should BeExactly "${baseUri}?maxlinks=5&linknumber=2&type=default"
|
||||
}
|
||||
|
||||
It "Validate Invoke-WebRequest returns valid RelationLink property with absolute uris if Multiple Link Headers are present" {
|
||||
$uri = Get-WebListenerUrl -Test 'Link' -Query @{maxlinks = 5; linknumber = 2; type = 'multiple'}
|
||||
$command = "Invoke-WebRequest -Uri '$uri'"
|
||||
$result = ExecuteWebCommand -command $command
|
||||
|
||||
$result.Output.RelationLink.Count | Should BeExactly 5
|
||||
$baseUri = Get-WebListenerUrl -Test 'Link'
|
||||
$result.Output.RelationLink["next"] | Should BeExactly "${baseUri}?maxlinks=5&linknumber=3&type=multiple"
|
||||
$result.Output.RelationLink["last"] | Should BeExactly "${baseUri}?maxlinks=5&linknumber=5&type=multiple"
|
||||
$result.Output.RelationLink["prev"] | Should BeExactly "${baseUri}?maxlinks=5&linknumber=1&type=multiple"
|
||||
$result.Output.RelationLink["first"] | Should BeExactly "${baseUri}?maxlinks=5&linknumber=1&type=multiple"
|
||||
$result.Output.RelationLink["self"] | Should BeExactly "${baseUri}?maxlinks=5&linknumber=2&type=multiple"
|
||||
}
|
||||
|
||||
It "Validate Invoke-WebRequest quietly ignores invalid Link Headers in RelationLink property: <type>" -TestCases @(
|
||||
@@ -747,10 +745,15 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" {
|
||||
@{ type = "noRel" }
|
||||
) {
|
||||
param($type)
|
||||
$command = "Invoke-WebRequest -Uri 'http://localhost:8080/PowerShell?test=linkheader&type=$type'"
|
||||
$uri = Get-WebListenerUrl -Test 'Link' -Query @{type = $type}
|
||||
$command = "Invoke-WebRequest -Uri '$uri'"
|
||||
$result = ExecuteWebCommand -command $command
|
||||
$result.Output.RelationLink.Count | Should BeExactly 1
|
||||
$result.Output.RelationLink["last"] | Should BeExactly "http://localhost:8080/PowerShell?test=linkheader&maxlinks=3&linknumber=3"
|
||||
|
||||
$result.Output.RelationLink.Count | Should BeExactly 3
|
||||
$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}"
|
||||
}
|
||||
|
||||
#region Redirect tests
|
||||
@@ -1030,9 +1033,7 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" {
|
||||
$result.Output.RawContent | Should Match ([regex]::Escape('Content-Length: 2'))
|
||||
}
|
||||
|
||||
# Test pending due to limitation on Linux/macOS
|
||||
# https://github.com/PowerShell/PowerShell/pull/4640
|
||||
It "Verifies Invoke-WebRequest Supports Multiple response headers with same name" -Pending:$(!$IsWindows) {
|
||||
It "Verifies Invoke-WebRequest Supports Multiple response headers with same name" {
|
||||
$query = @{
|
||||
contenttype = 'text/plain'
|
||||
body = 'OK'
|
||||
@@ -1723,23 +1724,23 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" {
|
||||
|
||||
It "Validate Invoke-RestMethod -FollowRelLink correctly follows all the available relation links" {
|
||||
$maxLinks = 5
|
||||
|
||||
$command = "Invoke-RestMethod -Uri 'http://localhost:8081/PowerShell?test=linkheader&maxlinks=$maxlinks' -FollowRelLink"
|
||||
$uri = Get-WebListenerUrl -Test 'Link' -Query @{maxlinks = $maxLinks}
|
||||
$command = "Invoke-RestMethod -Uri '$uri' -FollowRelLink"
|
||||
$result = ExecuteWebCommand -command $command
|
||||
|
||||
$result.Output.output.Count | Should BeExactly $maxLinks
|
||||
1..$maxLinks | ForEach-Object { $result.Output.output[$_ - 1] | Should BeExactly $_ }
|
||||
$result.Output.Count | Should BeExactly $maxLinks
|
||||
1..$maxLinks | ForEach-Object { $result.Output[$_ - 1].linknumber | Should BeExactly $_ }
|
||||
}
|
||||
|
||||
It "Validate Invoke-RestMethod -FollowRelLink correctly limits to -MaximumRelLink" {
|
||||
$maxLinks = 10
|
||||
$maxLinksToFollow = 6
|
||||
|
||||
$command = "Invoke-RestMethod -Uri 'http://localhost:8081/PowerShell?test=linkheader&maxlinks=$maxlinks' -FollowRelLink -MaximumFollowRelLink $maxLinksToFollow"
|
||||
$uri = Get-WebListenerUrl -Test 'Link' -Query @{maxlinks = $maxLinks}
|
||||
$command = "Invoke-RestMethod -Uri '$uri' -FollowRelLink -MaximumFollowRelLink $maxLinksToFollow"
|
||||
$result = ExecuteWebCommand -command $command
|
||||
|
||||
$result.Output.output.Count | Should BeExactly $maxLinksToFollow
|
||||
1..$maxLinksToFollow | ForEach-Object { $result.Output.output[$_ - 1] | Should BeExactly $_ }
|
||||
$result.Output.Count | Should BeExactly $maxLinksToFollow
|
||||
1..$maxLinksToFollow | ForEach-Object { $result.Output[$_ - 1].linknumber | Should BeExactly $_ }
|
||||
}
|
||||
|
||||
It "Validate Invoke-RestMethod quietly ignores invalid Link Headers if -FollowRelLink is specified: <type>" -TestCases @(
|
||||
@@ -1748,9 +1749,10 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" {
|
||||
@{ type = "noRel" }
|
||||
) {
|
||||
param($type)
|
||||
$command = "Invoke-RestMethod -Uri 'http://localhost:8081/PowerShell?test=linkheader&type=$type' -FollowRelLink"
|
||||
$uri = Get-WebListenerUrl -Test 'Link' -Query @{type = $type}
|
||||
$command = "Invoke-RestMethod -Uri '$uri' -FollowRelLink"
|
||||
$result = ExecuteWebCommand -command $command
|
||||
$result.Output.output | Should BeExactly 1
|
||||
$result.Output.linknumber | Should BeExactly 1
|
||||
}
|
||||
|
||||
#region Redirect tests
|
||||
|
||||
@@ -135,6 +135,7 @@ function Get-WebListenerUrl {
|
||||
'Encoding',
|
||||
'Get',
|
||||
'Home',
|
||||
'Link',
|
||||
'Multipart',
|
||||
'Patch',
|
||||
'Post',
|
||||
|
||||
@@ -6,6 +6,12 @@ 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 MalformedUrlLinkHeader = "{url}; foo";
|
||||
public const string NoRelLinkHeader = "<url>; foo=\"bar\"";
|
||||
public const string NoUrlLinkHeader = "<>; rel=\"next\"";
|
||||
|
||||
|
||||
}
|
||||
|
||||
internal static class StatusCodes
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using mvc.Models;
|
||||
|
||||
namespace mvc.Controllers
|
||||
{
|
||||
public class LinkController : Controller
|
||||
{
|
||||
public JsonResult Index()
|
||||
{
|
||||
if (!Request.Query.TryGetValue("maxlinks", out StringValues maxLinksSV) || !Int32.TryParse(maxLinksSV.FirstOrDefault(), out int maxLinks) || maxLinks < 1)
|
||||
{
|
||||
maxLinks = 3;
|
||||
}
|
||||
|
||||
if (!Request.Query.TryGetValue("linknumber", out StringValues linkNumberSV) || !Int32.TryParse(linkNumberSV.FirstOrDefault(), out int linkNumber) || linkNumber < 1)
|
||||
{
|
||||
linkNumber = 1;
|
||||
}
|
||||
|
||||
string baseUri = Regex.Replace(UriHelper.GetDisplayUrl(Request), "\\?.*", String.Empty);
|
||||
|
||||
string type = Request.Query.TryGetValue("type", out StringValues typeSV) ? typeSV.FirstOrDefault() : "default";
|
||||
|
||||
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: 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"));
|
||||
|
||||
bool sendMultipleHeaders = false;
|
||||
bool skipNextLink = false;
|
||||
switch (type.ToUpper())
|
||||
{
|
||||
case "NOURL":
|
||||
linkList.Add(Constants.NoUrlLinkHeader);
|
||||
skipNextLink = true;
|
||||
break;
|
||||
case "MALFORMED":
|
||||
linkList.Add(Constants.MalformedUrlLinkHeader);
|
||||
skipNextLink = true;
|
||||
break;
|
||||
case "NOREL":
|
||||
linkList.Add(Constants.NoRelLinkHeader);
|
||||
skipNextLink = true;
|
||||
break;
|
||||
case "MULTIPLE":
|
||||
sendMultipleHeaders = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!skipNextLink && maxLinks > 1 && linkNumber < maxLinks)
|
||||
{
|
||||
linkList.Add(GetLink(baseUri: baseUri, maxLinks: maxLinks, linkNumber: linkNumber + 1, type: type, rel: "next"));
|
||||
}
|
||||
|
||||
StringValues linkHeader;
|
||||
if (sendMultipleHeaders)
|
||||
{
|
||||
linkHeader = linkList.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
linkHeader = String.Join(",", linkList);
|
||||
}
|
||||
Response.Headers.Add("Link", linkHeader);
|
||||
|
||||
// Generate /Get/ result and append linknumber, maxlinks, and type
|
||||
var getController = new GetController();
|
||||
getController.ControllerContext = this.ControllerContext;
|
||||
var result = getController.Index();
|
||||
var output = result.Value as Hashtable;
|
||||
output.Add("linknumber", linkNumber);
|
||||
output.Add("maxlinks", maxLinks);
|
||||
output.Add("type", type.FirstOrDefault());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return String.Format(Constants.LinkUriTemplate, baseUri, maxLinks, linkNumber, type, rel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -265,6 +265,67 @@ Invoke-RestMethod -Uri $uri -Body @{TestField = 'TestValue'}
|
||||
}
|
||||
```
|
||||
|
||||
### /Link/
|
||||
|
||||
Returns Link response headers to test paginated results. The endpoint accepts 3 query items:
|
||||
|
||||
* `linknumber` - The current link number. This determines the current page. If not supplied or less than 1, this will be set to 1.
|
||||
* `maxlinks` - The maximum number of links. This determines the last page. If not supplied or less than 1, this will be set to 3.
|
||||
* `type` - The type of link to return. When not supplied or not in the list below, `default` will be used.
|
||||
* `default` - Does not return any special test links and returns `next` link if one is available.
|
||||
* `norel` - Returns a Link header that does not include the `rel=` portion. Suppresses `next` link.
|
||||
* `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.
|
||||
|
||||
The body will contain the same results as `/Get/` with the addition of the `type`, `linknumber`, and `maxlinks` for the current page.
|
||||
|
||||
```powershell
|
||||
$Query = @{
|
||||
linknumber = 1
|
||||
maxlinks = 3
|
||||
type = 'default'
|
||||
}
|
||||
$Uri = Get-WebListenerUrl -Test 'Link' -Query $Query
|
||||
Invoke-RestMethod -Uri $uri -FollowRelLink -MaximumFollowRelLink 1
|
||||
```
|
||||
|
||||
Headers:
|
||||
|
||||
```none
|
||||
HTTP/1.1 200 OK
|
||||
Date: Sat, 06 Jan 2018 14:27:36 GMT
|
||||
Content-Type: application/json; charset=utf-8
|
||||
Server: Kestrel
|
||||
Transfer-Encoding: chunked
|
||||
Link: <http://localhost:8083/Link/?maxlinks=3&linknumber=3>; rel="last",<http://localhost:8083/Link/?maxlinks=3&linknumber=1>; rel="first",<http://localhost:8083/Link/?maxlinks=3&linknumber=1>; rel="self",<http://localhost:8083/Link/?maxlinks=3&linknumber=2>; rel="next"
|
||||
```
|
||||
|
||||
Body:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "default",
|
||||
"url": "http://localhost:8083/Link/?maxlinks=3&linknumber=1&type=default",
|
||||
"maxlinks": 3,
|
||||
"linknumber": 1,
|
||||
"headers": {
|
||||
"User-Agent": "insomnia/5.12.4",
|
||||
"Accept": "*/*",
|
||||
"Content-Length": "0",
|
||||
"Host": "localhost:8083",
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
"args": {
|
||||
"linknumber": "1",
|
||||
"maxlinks": "3",
|
||||
"type": "default"
|
||||
},
|
||||
"origin": "127.0.0.1",
|
||||
"method": "GET"
|
||||
}
|
||||
```
|
||||
|
||||
### /Multipart/
|
||||
|
||||
#### GET
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
<li>/Delete/ - returns data from a DELETE request</li>
|
||||
<li><a href="/Encoding/Utf8/">/Encoding/Utf8/</a> - Returns page containing UTF-8 data.</li>
|
||||
<li><a href="/Get/">/Get/</a> - Emulates functionality of https://httpbin.org/get by returning GET headers, Arguments, and Request URL</li>
|
||||
<li><a href="/Multipart/">/Multipart/</a> - Multipart/form-data submission testing</li>
|
||||
<li><a href="/Get/">/Get/</a> - Emulates functionality of https://httpbin.org/get by returning GET headers, Arguments, and Request URL</li>
|
||||
<li><a href="/Link/?linknumber=1&maxlinks=3&type=default">/Link/</a> - Link Header (pagination) testing</li>
|
||||
<li>/Patch/ - returns data from a PATCH request</li>
|
||||
<li>/Post/ - returns data from a POST request</li>
|
||||
<li>/Put/ - returns data from a PUT request</li>
|
||||
|
||||
Reference in New Issue
Block a user