mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-23 09:02:41 +00:00
Merge branch 'master' into microsub
This commit is contained in:
commit
cb49d810ee
7 changed files with 68 additions and 12 deletions
|
@ -6,6 +6,7 @@ Bug fixes:
|
||||||
- Don't fail adding a feed which collides with another via redirection
|
- Don't fail adding a feed which collides with another via redirection
|
||||||
- Don't fail on very long text-search queries containing question marks
|
- Don't fail on very long text-search queries containing question marks
|
||||||
when using PostgreSQL or MySQL
|
when using PostgreSQL or MySQL
|
||||||
|
- Specify HTTP authentication encoding as UTF-8
|
||||||
|
|
||||||
Changes:
|
Changes:
|
||||||
- Include a user manual
|
- Include a user manual
|
||||||
|
|
22
lib/Misc/HTTP.php
Normal file
22
lib/Misc/HTTP.php
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?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\Misc;
|
||||||
|
|
||||||
|
use Psr\Http\Message\MessageInterface;
|
||||||
|
|
||||||
|
class HTTP {
|
||||||
|
public static function matchType(MessageInterface $msg, string ...$type): bool {
|
||||||
|
$header = $msg->getHeaderLine("Content-Type") ?? "";
|
||||||
|
foreach ($type as $t) {
|
||||||
|
$pattern = "/^".preg_quote(trim($t), "/")."($|;|,)/i";
|
||||||
|
if (preg_match($pattern, $header)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ use JKingWeb\Arsse\Context\Context;
|
||||||
use JKingWeb\Arsse\Misc\ValueInfo as V;
|
use JKingWeb\Arsse\Misc\ValueInfo as V;
|
||||||
use JKingWeb\Arsse\Misc\Date;
|
use JKingWeb\Arsse\Misc\Date;
|
||||||
use JKingWeb\Arsse\Db\ExceptionInput;
|
use JKingWeb\Arsse\Db\ExceptionInput;
|
||||||
|
use JKingWeb\Arsse\Misc\HTTP;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Zend\Diactoros\Response\JsonResponse;
|
use Zend\Diactoros\Response\JsonResponse;
|
||||||
|
@ -21,6 +22,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
const LEVEL = 3;
|
const LEVEL = 3;
|
||||||
const GENERIC_ICON_TYPE = "image/png;base64";
|
const GENERIC_ICON_TYPE = "image/png;base64";
|
||||||
const GENERIC_ICON_DATA = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMjHxIGmVAAAADUlEQVQYV2NgYGBgAAAABQABijPjAAAAAABJRU5ErkJggg==";
|
const GENERIC_ICON_DATA = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMjHxIGmVAAAADUlEQVQYV2NgYGBgAAAABQABijPjAAAAAABJRU5ErkJggg==";
|
||||||
|
const ACCEPTED_TYPE = "application/x-www-form-urlencoded";
|
||||||
|
|
||||||
// GET parameters for which we only check presence: these will be converted to booleans
|
// GET parameters for which we only check presence: these will be converted to booleans
|
||||||
const PARAM_BOOL = ["groups", "feeds", "items", "favicons", "links", "unread_item_ids", "saved_item_ids"];
|
const PARAM_BOOL = ["groups", "feeds", "items", "favicons", "links", "unread_item_ids", "saved_item_ids"];
|
||||||
|
@ -66,11 +68,11 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
case "OPTIONS":
|
case "OPTIONS":
|
||||||
return new EmptyResponse(204, [
|
return new EmptyResponse(204, [
|
||||||
'Allow' => "POST",
|
'Allow' => "POST",
|
||||||
'Accept' => "application/x-www-form-urlencoded",
|
'Accept' => self::ACCEPTED_TYPE,
|
||||||
]);
|
]);
|
||||||
case "POST":
|
case "POST":
|
||||||
if (strlen($req->getHeaderLine("Content-Type")) && $req->getHeaderLine("Content-Type") !== "application/x-www-form-urlencoded") {
|
if (!HTTP::matchType($req, self::ACCEPTED_TYPE, "")) {
|
||||||
return new EmptyResponse(415, ['Accept' => "application/x-www-form-urlencoded"]);
|
return new EmptyResponse(415, ['Accept' => self::ACCEPTED_TYPE]);
|
||||||
}
|
}
|
||||||
$out = [
|
$out = [
|
||||||
'api_version' => self::LEVEL,
|
'api_version' => self::LEVEL,
|
||||||
|
|
|
@ -13,6 +13,7 @@ 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\Misc\HTTP;
|
||||||
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;
|
||||||
|
@ -23,6 +24,7 @@ use Zend\Diactoros\Response\EmptyResponse;
|
||||||
class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
const REALM = "NextCloud News API v1-2";
|
const REALM = "NextCloud News API v1-2";
|
||||||
const VERSION = "11.0.5";
|
const VERSION = "11.0.5";
|
||||||
|
const ACCEPTED_TYPE = "application/json";
|
||||||
|
|
||||||
protected $dateFormat = "unix";
|
protected $dateFormat = "unix";
|
||||||
|
|
||||||
|
@ -90,15 +92,10 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
}
|
}
|
||||||
// normalize the input
|
// normalize the input
|
||||||
$data = (string) $req->getBody();
|
$data = (string) $req->getBody();
|
||||||
$type = "";
|
|
||||||
if ($req->hasHeader("Content-Type")) {
|
|
||||||
$type = $req->getHeader("Content-Type");
|
|
||||||
$type = array_pop($type);
|
|
||||||
}
|
|
||||||
if ($data) {
|
if ($data) {
|
||||||
// if the entity body is not JSON according to content type, return "415 Unsupported Media Type"
|
// if the entity body is not JSON according to content type, return "415 Unsupported Media Type"
|
||||||
if (!preg_match("<^application/json\b|^$>", $type)) {
|
if (!HTTP::matchType($req, "", self::ACCEPTED_TYPE)) {
|
||||||
return new EmptyResponse(415, ['Accept' => "application/json"]);
|
return new EmptyResponse(415, ['Accept' => self::ACCEPTED_TYPE]);
|
||||||
}
|
}
|
||||||
$data = @json_decode($data, true);
|
$data = @json_decode($data, true);
|
||||||
if (json_last_error() !== \JSON_ERROR_NONE) {
|
if (json_last_error() !== \JSON_ERROR_NONE) {
|
||||||
|
@ -269,7 +266,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
}
|
}
|
||||||
return new EmptyResponse(204, [
|
return new EmptyResponse(204, [
|
||||||
'Allow' => implode(",", $allowed),
|
'Allow' => implode(",", $allowed),
|
||||||
'Accept' => "application/json",
|
'Accept' => self::ACCEPTED_TYPE,
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
// if the path is not supported, return 404
|
// if the path is not supported, return 404
|
||||||
|
|
|
@ -43,6 +43,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
const CAT_NOT_SPECIAL = -3;
|
const CAT_NOT_SPECIAL = -3;
|
||||||
const CAT_ALL = -4;
|
const CAT_ALL = -4;
|
||||||
// valid input
|
// valid input
|
||||||
|
const ACCEPTED_TYPES = ["application/json", "text/json"];
|
||||||
const VALID_INPUT = [
|
const VALID_INPUT = [
|
||||||
'op' => ValueInfo::T_STRING, // the function ("operation") to perform
|
'op' => ValueInfo::T_STRING, // the function ("operation") to perform
|
||||||
'sid' => ValueInfo::T_STRING, // session ID
|
'sid' => ValueInfo::T_STRING, // session ID
|
||||||
|
@ -99,7 +100,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
// respond to OPTIONS rquests; the response is a fib, as we technically accept any type or method
|
// respond to OPTIONS rquests; the response is a fib, as we technically accept any type or method
|
||||||
return new EmptyResponse(204, [
|
return new EmptyResponse(204, [
|
||||||
'Allow' => "POST",
|
'Allow' => "POST",
|
||||||
'Accept' => "application/json, text/json",
|
'Accept' => implode(", ", self::ACCEPTED_TYPES),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
$data = (string) $req->getBody();
|
$data = (string) $req->getBody();
|
||||||
|
|
32
tests/cases/Misc/TestHTTP.php
Normal file
32
tests/cases/Misc/TestHTTP.php
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<?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\Misc;
|
||||||
|
|
||||||
|
use JKingWeb\Arsse\Misc\HTTP;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
/** @covers \JKingWeb\Arsse\Misc\HTTP */
|
||||||
|
class TestHTTP extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
|
/** @dataProvider provideMediaTypes */
|
||||||
|
public function testMatchMediaType(string $header, array $types, bool $exp) {
|
||||||
|
$msg = (new \Zend\Diactoros\Request)->withHeader("Content-Type", $header);
|
||||||
|
$this->assertSame($exp, HTTP::matchType($msg, ...$types));
|
||||||
|
$msg = (new \Zend\Diactoros\Response)->withHeader("Content-Type", $header);
|
||||||
|
$this->assertSame($exp, HTTP::matchType($msg, ...$types));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideMediaTypes() {
|
||||||
|
return [
|
||||||
|
["application/json", ["application/json"], true],
|
||||||
|
["APPLICATION/JSON", ["application/json"], true],
|
||||||
|
["text/JSON", ["application/json", "text/json"], true],
|
||||||
|
["text/json; charset=utf-8", ["application/json", "text/json"], true],
|
||||||
|
["", ["application/json"], false],
|
||||||
|
["", ["application/json", ""], true],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,6 +47,7 @@
|
||||||
<file>cases/Misc/TestDate.php</file>
|
<file>cases/Misc/TestDate.php</file>
|
||||||
<file>cases/Misc/TestContext.php</file>
|
<file>cases/Misc/TestContext.php</file>
|
||||||
<file>cases/Misc/TestURL.php</file>
|
<file>cases/Misc/TestURL.php</file>
|
||||||
|
<file>cases/Misc/TestHTTP.php</file>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
<testsuite name="User management">
|
<testsuite name="User management">
|
||||||
<file>cases/User/TestInternal.php</file>
|
<file>cases/User/TestInternal.php</file>
|
||||||
|
|
Loading…
Reference in a new issue