mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
Remove Target class
This commit is contained in:
parent
6235cb0be6
commit
d9c769d40e
5 changed files with 13 additions and 217 deletions
|
@ -7,10 +7,10 @@ declare(strict_types=1);
|
||||||
namespace JKingWeb\Arsse;
|
namespace JKingWeb\Arsse;
|
||||||
|
|
||||||
use JKingWeb\Arsse\Arsse;
|
use JKingWeb\Arsse\Arsse;
|
||||||
|
use JKingWeb\Arsse\Misc\URL;
|
||||||
use Psr\Http\Message\RequestInterface;
|
use Psr\Http\Message\RequestInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Zend\Diactoros\ServerRequest;
|
|
||||||
use Zend\Diactoros\ServerRequestFactory;
|
use Zend\Diactoros\ServerRequestFactory;
|
||||||
use Zend\Diactoros\Response\EmptyResponse;
|
use Zend\Diactoros\Response\EmptyResponse;
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ class REST {
|
||||||
return (strlen($a['match']) <=> strlen($b['match'])) * -1;
|
return (strlen($a['match']) <=> strlen($b['match'])) * -1;
|
||||||
});
|
});
|
||||||
// normalize the target URL
|
// normalize the target URL
|
||||||
$url = REST\Target::normalize($url);
|
$url = URL::normalize($url);
|
||||||
// find a match
|
// find a match
|
||||||
foreach ($map as $id => $api) {
|
foreach ($map as $id => $api) {
|
||||||
// first try a simple substring match
|
// first try a simple substring match
|
||||||
|
|
|
@ -7,15 +7,12 @@ declare(strict_types=1);
|
||||||
namespace JKingWeb\Arsse\REST\NextCloudNews;
|
namespace JKingWeb\Arsse\REST\NextCloudNews;
|
||||||
|
|
||||||
use JKingWeb\Arsse\Arsse;
|
use JKingWeb\Arsse\Arsse;
|
||||||
use JKingWeb\Arsse\Database;
|
|
||||||
use JKingWeb\Arsse\User;
|
|
||||||
use JKingWeb\Arsse\Service;
|
use JKingWeb\Arsse\Service;
|
||||||
use JKingWeb\Arsse\Context\Context;
|
use JKingWeb\Arsse\Context\Context;
|
||||||
use JKingWeb\Arsse\Misc\ValueInfo;
|
use JKingWeb\Arsse\Misc\ValueInfo;
|
||||||
use JKingWeb\Arsse\AbstractException;
|
use JKingWeb\Arsse\AbstractException;
|
||||||
use JKingWeb\Arsse\Db\ExceptionInput;
|
use JKingWeb\Arsse\Db\ExceptionInput;
|
||||||
use JKingWeb\Arsse\Feed\Exception as FeedException;
|
use JKingWeb\Arsse\Feed\Exception as FeedException;
|
||||||
use JKingWeb\Arsse\REST\Target;
|
|
||||||
use JKingWeb\Arsse\REST\Exception404;
|
use JKingWeb\Arsse\REST\Exception404;
|
||||||
use JKingWeb\Arsse\REST\Exception405;
|
use JKingWeb\Arsse\REST\Exception405;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
@ -85,11 +82,11 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
} else {
|
} else {
|
||||||
return new EmptyResponse(401);
|
return new EmptyResponse(401);
|
||||||
}
|
}
|
||||||
// explode and normalize the URL path
|
// get the request path only; this is assumed to already be normalized
|
||||||
$target = new Target($req->getRequestTarget());
|
$target = parse_url($req->getRequestTarget())['path'] ?? "";
|
||||||
// handle HTTP OPTIONS requests
|
// handle HTTP OPTIONS requests
|
||||||
if ($req->getMethod() === "OPTIONS") {
|
if ($req->getMethod() === "OPTIONS") {
|
||||||
return $this->handleHTTPOptions((string) $target);
|
return $this->handleHTTPOptions($target);
|
||||||
}
|
}
|
||||||
// normalize the input
|
// normalize the input
|
||||||
$data = (string) $req->getBody();
|
$data = (string) $req->getBody();
|
||||||
|
@ -115,7 +112,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
$data = $this->normalizeInput(array_merge($req->getQueryParams(), $data), $this->validInput, "unix");
|
$data = $this->normalizeInput(array_merge($req->getQueryParams(), $data), $this->validInput, "unix");
|
||||||
// check to make sure the requested function is implemented
|
// check to make sure the requested function is implemented
|
||||||
try {
|
try {
|
||||||
$func = $this->chooseCall((string) $target, $req->getMethod());
|
$func = $this->chooseCall($target, $req->getMethod());
|
||||||
} catch (Exception404 $e) {
|
} catch (Exception404 $e) {
|
||||||
return new EmptyResponse(404);
|
return new EmptyResponse(404);
|
||||||
} catch (Exception405 $e) {
|
} catch (Exception405 $e) {
|
||||||
|
@ -126,7 +123,8 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
}
|
}
|
||||||
// dispatch
|
// dispatch
|
||||||
try {
|
try {
|
||||||
return $this->$func($target->path, $data);
|
$path = explode("/", ltrim($target, "/"));
|
||||||
|
return $this->$func($path, $data);
|
||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
// if there was a REST exception return 400
|
// if there was a REST exception return 400
|
||||||
|
@ -139,18 +137,14 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function normalizePathIds(string $url): string {
|
protected function normalizePathIds(string $url): string {
|
||||||
// first parse the URL and perform syntactic normalization
|
$path = explode("/", $url);
|
||||||
$target = new Target($url);
|
|
||||||
// any path components which are database IDs (integers greater than zero) should be replaced with "1", for easier comparison (we don't care about the specific ID)
|
// any path components which are database IDs (integers greater than zero) should be replaced with "1", for easier comparison (we don't care about the specific ID)
|
||||||
for ($a = 0; $a < sizeof($target->path); $a++) {
|
for ($a = 0; $a < sizeof($path); $a++) {
|
||||||
if (ValueInfo::id($target->path[$a])) {
|
if (ValueInfo::id($path[$a])) {
|
||||||
$target->path[$a] = "1";
|
$path[$a] = "1";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// discard any fragment ID (there shouldn't be any) and query string (the query is available in the request itself)
|
return implode("/", $path);
|
||||||
$target->fragment = "";
|
|
||||||
$target->query = "";
|
|
||||||
return (string) $target;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function chooseCall(string $url, string $method): string {
|
protected function chooseCall(string $url, string $method): string {
|
||||||
|
|
|
@ -1,131 +0,0 @@
|
||||||
<?php
|
|
||||||
/** @license MIT
|
|
||||||
* Copyright 2017 J. King, Dustin Wilson et al.
|
|
||||||
* See LICENSE and AUTHORS files for details */
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
namespace JKingWeb\Arsse\REST;
|
|
||||||
|
|
||||||
use JKingWeb\Arsse\Misc\ValueInfo;
|
|
||||||
|
|
||||||
class Target {
|
|
||||||
public $relative = false;
|
|
||||||
public $index = false;
|
|
||||||
public $path = [];
|
|
||||||
public $query = "";
|
|
||||||
public $fragment = "";
|
|
||||||
|
|
||||||
public function __construct(string $target) {
|
|
||||||
$target = $this->parseFragment($target);
|
|
||||||
$target = $this->parseQuery($target);
|
|
||||||
$this->path = $this->parsePath($target);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __toString(): string {
|
|
||||||
$out = "";
|
|
||||||
$path = [];
|
|
||||||
foreach ($this->path as $segment) {
|
|
||||||
if (is_null($segment)) {
|
|
||||||
if (!$path) {
|
|
||||||
$path[] = "..";
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} elseif ($segment === ".") {
|
|
||||||
$path[] = "%2E";
|
|
||||||
} elseif ($segment === "..") {
|
|
||||||
$path[] = "%2E%2E";
|
|
||||||
} else {
|
|
||||||
$path[] = rawurlencode(ValueInfo::normalize($segment, ValueInfo::T_STRING));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$path = implode("/", $path);
|
|
||||||
if (!$this->relative) {
|
|
||||||
$out .= "/";
|
|
||||||
}
|
|
||||||
$out .= $path;
|
|
||||||
if ($this->index && strlen($path)) {
|
|
||||||
$out .= "/";
|
|
||||||
}
|
|
||||||
if (strlen($this->query)) {
|
|
||||||
$out .= "?".$this->query;
|
|
||||||
}
|
|
||||||
if (strlen($this->fragment)) {
|
|
||||||
$out .= "#".rawurlencode($this->fragment);
|
|
||||||
}
|
|
||||||
return $out;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function normalize(string $target): string {
|
|
||||||
return (string) new self($target);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function parseFragment(string $target): string {
|
|
||||||
// store and strip off any fragment identifier and return the target without a fragment
|
|
||||||
$pos = strpos($target, "#");
|
|
||||||
if ($pos !== false) {
|
|
||||||
$this->fragment = rawurldecode(substr($target, $pos + 1));
|
|
||||||
$target = substr($target, 0, $pos);
|
|
||||||
}
|
|
||||||
return $target;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function parseQuery(string $target): string {
|
|
||||||
// store and strip off any query string and return the target without a query
|
|
||||||
// note that the function assumes any fragment identifier has already been stripped off
|
|
||||||
// unlike the other parts the query string is currently neither parsed nor normalized
|
|
||||||
$pos = strpos($target, "?");
|
|
||||||
if ($pos !== false) {
|
|
||||||
$this->query = substr($target, $pos + 1);
|
|
||||||
$target = substr($target, 0, $pos);
|
|
||||||
}
|
|
||||||
return $target;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function parsePath(string $target): array {
|
|
||||||
// note that the function assumes any fragment identifier or query has already been stripped off
|
|
||||||
// syntax-based normalization is applied to the path segments (see RFC 3986 sec. 6.2.2)
|
|
||||||
// duplicate slashes are NOT collapsed
|
|
||||||
if (substr($target, 0, 1) === "/") {
|
|
||||||
// if the path starts with a slash, strip it off
|
|
||||||
$target = substr($target, 1);
|
|
||||||
} else {
|
|
||||||
// otherwise this is a relative target
|
|
||||||
$this->relative = true;
|
|
||||||
}
|
|
||||||
if (!strlen($target)) {
|
|
||||||
// if the target is an empty string, this is an index target
|
|
||||||
$this->index = true;
|
|
||||||
} elseif (substr($target, -1, 1) === "/") {
|
|
||||||
// if the path ends in a slash, this is an index target and the slash should be stripped off
|
|
||||||
$this->index = true;
|
|
||||||
$target = substr($target, 0, strlen($target) -1);
|
|
||||||
}
|
|
||||||
// after stripping, explode the path parts
|
|
||||||
if (strlen($target)) {
|
|
||||||
$target = explode("/", $target);
|
|
||||||
$out = [];
|
|
||||||
// resolve relative path segments and decode each retained segment
|
|
||||||
foreach ($target as $index => $segment) {
|
|
||||||
if ($segment === ".") {
|
|
||||||
// self-referential segments can be ignored
|
|
||||||
continue;
|
|
||||||
} elseif ($segment === "..") {
|
|
||||||
if ($index == 0) {
|
|
||||||
// if the first path segment refers to its parent (which we don't know about) we cannot output a correct path, so we do the best we can
|
|
||||||
$out[] = null;
|
|
||||||
} else {
|
|
||||||
// for any other segments after the first we pop off the last stored segment
|
|
||||||
array_pop($out);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// any other segment is decoded and retained
|
|
||||||
$out[] = rawurldecode($segment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $out;
|
|
||||||
} else {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
<?php
|
|
||||||
/** @license MIT
|
|
||||||
* Copyright 2017 J. King, Dustin Wilson et al.
|
|
||||||
* See LICENSE and AUTHORS files for details */
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
namespace JKingWeb\Arsse\TestCase\REST;
|
|
||||||
|
|
||||||
use JKingWeb\Arsse\REST\Target;
|
|
||||||
|
|
||||||
/** @covers \JKingWeb\Arsse\REST\Target */
|
|
||||||
class TestTarget extends \JKingWeb\Arsse\Test\AbstractTest {
|
|
||||||
|
|
||||||
/** @dataProvider provideTargetUrls */
|
|
||||||
public function testParseTargetUrl(string $target, array $path, bool $relative, bool $index, string $query, string $fragment, string $normalized) {
|
|
||||||
$test = new Target($target);
|
|
||||||
$this->assertEquals($path, $test->path, "Path does not match");
|
|
||||||
$this->assertSame($path, $test->path, "Path does not match exactly");
|
|
||||||
$this->assertSame($relative, $test->relative, "Relative flag does not match");
|
|
||||||
$this->assertSame($index, $test->index, "Index flag does not match");
|
|
||||||
$this->assertSame($query, $test->query, "Query does not match");
|
|
||||||
$this->assertSame($fragment, $test->fragment, "Fragment does not match");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @dataProvider provideTargetUrls */
|
|
||||||
public function testNormalizeTargetUrl(string $target, array $path, bool $relative, bool $index, string $query, string $fragment, string $normalized) {
|
|
||||||
$test = new Target("");
|
|
||||||
$test->path = $path;
|
|
||||||
$test->relative = $relative;
|
|
||||||
$test->index = $index;
|
|
||||||
$test->query = $query;
|
|
||||||
$test->fragment = $fragment;
|
|
||||||
$this->assertSame($normalized, (string) $test);
|
|
||||||
$this->assertSame($normalized, Target::normalize($target));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function provideTargetUrls() {
|
|
||||||
return [
|
|
||||||
["/", [], false, true, "", "", "/"],
|
|
||||||
["", [], true, true, "", "", ""],
|
|
||||||
["/index.php", ["index.php"], false, false, "", "", "/index.php"],
|
|
||||||
["index.php", ["index.php"], true, false, "", "", "index.php"],
|
|
||||||
["/ook/", ["ook"], false, true, "", "", "/ook/"],
|
|
||||||
["ook/", ["ook"], true, true, "", "", "ook/"],
|
|
||||||
["/eek/../ook/", ["ook"], false, true, "", "", "/ook/"],
|
|
||||||
["eek/../ook/", ["ook"], true, true, "", "", "ook/"],
|
|
||||||
["/./ook/", ["ook"], false, true, "", "", "/ook/"],
|
|
||||||
["./ook/", ["ook"], true, true, "", "", "ook/"],
|
|
||||||
["/../ook/", [null,"ook"], false, true, "", "", "/../ook/"],
|
|
||||||
["../ook/", [null,"ook"], true, true, "", "", "../ook/"],
|
|
||||||
["0", ["0"], true, false, "", "", "0"],
|
|
||||||
["%6f%6F%6b", ["ook"], true, false, "", "", "ook"],
|
|
||||||
["%2e%2E%2f%2E%2Fook%2f", [".././ook/"], true, false, "", "", "..%2F.%2Fook%2F"],
|
|
||||||
["%2e%2E/%2E/ook%2f", ["..",".","ook/"], true, false, "", "", "%2E%2E/%2E/ook%2F"],
|
|
||||||
["...", ["..."], true, false, "", "", "..."],
|
|
||||||
["%2e%2e%2e", ["..."], true, false, "", "", "..."],
|
|
||||||
["/?", [], false, true, "", "", "/"],
|
|
||||||
["/#", [], false, true, "", "", "/"],
|
|
||||||
["/?#", [], false, true, "", "", "/"],
|
|
||||||
["#%2e", [], true, true, "", ".", "#."],
|
|
||||||
["?%2e", [], true, true, "%2e", "", "?%2e"],
|
|
||||||
["?%2e#%2f", [], true, true, "%2e", "/", "?%2e#%2F"],
|
|
||||||
["#%2e?%2f", [], true, true, "", ".?/", "#.%3F%2F"],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -106,7 +106,6 @@
|
||||||
<file>cases/Db/MySQLPDO/TestDatabase.php</file>
|
<file>cases/Db/MySQLPDO/TestDatabase.php</file>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
<testsuite name="REST">
|
<testsuite name="REST">
|
||||||
<file>cases/REST/TestTarget.php</file>
|
|
||||||
<file>cases/REST/TestREST.php</file>
|
<file>cases/REST/TestREST.php</file>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
<testsuite name="NCNv1">
|
<testsuite name="NCNv1">
|
||||||
|
|
Loading…
Reference in a new issue