1
1
Fork 0
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:
J. King 2019-09-05 10:19:05 -04:00
parent 6235cb0be6
commit d9c769d40e
5 changed files with 13 additions and 217 deletions

View file

@ -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

View file

@ -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 {

View file

@ -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 [];
}
}
}

View file

@ -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"],
];
}
}

View file

@ -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">