Browse Source

Implement OPTIONS requests for NCNv1; fixes #107

microsub
J. King 6 years ago
parent
commit
3d958547a5
  1. 26
      lib/REST/NextCloudNews/V1_2.php
  2. 20
      lib/REST/NextCloudNews/Versions.php
  3. 28
      tests/REST/NextCloudNews/TestNCNV1_2.php
  4. 13
      tests/REST/NextCloudNews/TestNCNVersionDiscovery.php

26
lib/REST/NextCloudNews/V1_2.php

@ -76,6 +76,10 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
if (!Arsse::$user->authHTTP()) {
return new Response(401, "", "", ['WWW-Authenticate: Basic realm="'.self::REALM.'"']);
}
// handle HTTP OPTIONS requests
if ($req->method=="OPTIONS") {
return $this->handleHTTPOptions($req->paths);
}
// normalize the input
if ($req->body) {
// if the entity body is not JSON according to content type, return "415 Unsupported Media Type"
@ -144,7 +148,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
throw new Exception405(implode(", ", array_keys($this->paths[$url])));
}
} else {
// if the path is not supported, return 501
// if the path is not supported, return 404
throw new Exception404();
}
}
@ -202,6 +206,26 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
], $this->dateFormat);
return $article;
}
protected function handleHTTPOptions(array $url): Response {
// normalize the URL path
$url = $this->normalizePath($url);
if (isset($this->paths[$url])) {
// if the path is supported, respond with the allowed methods and other metadata
$allowed = array_keys($this->paths[$url]);
// if GET is allowed, so is HEAD
if (in_array("GET", $allowed)) {
array_unshift($allowed, "HEAD");
}
return new Response(204, "", "", [
"Allow: ".implode(",", $allowed),
"Accept: application/json",
]);
} else {
// if the path is not supported, return 404
return new Response(404);
}
}
// list folders
protected function folderList(array $url, array $data): Response {

20
lib/REST/NextCloudNews/Versions.php

@ -13,21 +13,23 @@ class Versions implements \JKingWeb\Arsse\REST\Handler {
}
public function dispatch(\JKingWeb\Arsse\REST\Request $req): Response {
// if a method other than GET was used, this is an error
if ($req->method != "GET") {
return new Response(405, "", "", ["Allow: GET"]);
}
if (preg_match("<^/?$>", $req->path)) {
// if the request path is an empty string or just a slash, return the supported versions
if (!preg_match("<^/?$>", $req->path)) {
// if the request path is an empty string or just a slash, the client is probably trying a version we don't support
return new Response(404);
} elseif ($req->method=="OPTIONS") {
// if the request method is OPTIONS, respond accordingly
return new Response(204, "", "", ["Allow: HEAD,GET"]);
} elseif ($req->method != "GET") {
// if a method other than GET was used, this is an error
return new Response(405, "", "", ["Allow: HEAD,GET"]);
} else {
// otherwise return the supported versions
$out = [
'apiLevels' => [
'v1-2',
]
];
return new Response(200, $out);
} else {
// if the URL path was anything else, the client is probably trying a version we don't support
return new Response(404);
}
}
}

28
tests/REST/NextCloudNews/TestNCNV1_2.php

@ -311,6 +311,12 @@ class TestNCNV1_2 extends Test\AbstractTest {
$this->clearData();
}
public function testSendAuthenticationChallenge() {
Phake::when(Arsse::$user)->authHTTP->thenReturn(false);
$exp = new Response(401, "", "", ['WWW-Authenticate: Basic realm="'.REST\NextCloudNews\V1_2::REALM.'"']);
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/")));
}
public function testRespondToInvalidPaths() {
$errs = [
404 => [
@ -364,10 +370,24 @@ class TestNCNV1_2 extends Test\AbstractTest {
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/1", '<data/>', 'application/json')));
}
public function testSendAuthenticationChallenge() {
Phake::when(Arsse::$user)->authHTTP->thenReturn(false);
$exp = new Response(401, "", "", ['WWW-Authenticate: Basic realm="'.REST\NextCloudNews\V1_2::REALM.'"']);
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/")));
public function testRespondToOptionsRequests() {
$exp = new Response(204, "", "", [
"Allow: HEAD,GET,POST",
"Accept: application/json",
]);
$this->assertEquals($exp, $this->h->dispatch(new Request("OPTIONS", "/feeds")));
$exp = new Response(204, "", "", [
"Allow: DELETE",
"Accept: application/json",
]);
$this->assertEquals($exp, $this->h->dispatch(new Request("OPTIONS", "/feeds/2112")));
$exp = new Response(204, "", "", [
"Allow: HEAD,GET",
"Accept: application/json",
]);
$this->assertEquals($exp, $this->h->dispatch(new Request("OPTIONS", "/user")));
$exp = new Response(404);
$this->assertEquals($exp, $this->h->dispatch(new Request("OPTIONS", "/invalid/path")));
}
public function testListFolders() {

13
tests/REST/NextCloudNews/TestNCNVersionDiscovery.php

@ -29,8 +29,16 @@ class TestNCNVersionDiscovery extends Test\AbstractTest {
$this->assertEquals($exp, $res);
}
public function testRespondToOptionsRequest() {
$exp = new Response(204, "", "", ["Allow: HEAD,GET"]);
$h = new REST\NextCloudNews\Versions();
$req = new Request("OPTIONS", "/");
$res = $h->dispatch($req);
$this->assertEquals($exp, $res);
}
public function testUseIncorrectMethod() {
$exp = new Response(405, "", "", ["Allow: GET"]);
$exp = new Response(405, "", "", ["Allow: HEAD,GET"]);
$h = new REST\NextCloudNews\Versions();
$req = new Request("POST", "/");
$res = $h->dispatch($req);
@ -43,5 +51,8 @@ class TestNCNVersionDiscovery extends Test\AbstractTest {
$req = new Request("GET", "/ook");
$res = $h->dispatch($req);
$this->assertEquals($exp, $res);
$req = new Request("OPTIONS", "/ook");
$res = $h->dispatch($req);
$this->assertEquals($exp, $res);
}
}

Loading…
Cancel
Save