Browse Source

Implement OPTIONS requests for NCNv1; fixes #107

microsub
J. King 7 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()) { if (!Arsse::$user->authHTTP()) {
return new Response(401, "", "", ['WWW-Authenticate: Basic realm="'.self::REALM.'"']); 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 // normalize the input
if ($req->body) { if ($req->body) {
// 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"
@ -144,7 +148,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
throw new Exception405(implode(", ", array_keys($this->paths[$url]))); throw new Exception405(implode(", ", array_keys($this->paths[$url])));
} }
} else { } else {
// if the path is not supported, return 501 // if the path is not supported, return 404
throw new Exception404(); throw new Exception404();
} }
} }
@ -202,6 +206,26 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
], $this->dateFormat); ], $this->dateFormat);
return $article; 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 // list folders
protected function folderList(array $url, array $data): Response { 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 { public function dispatch(\JKingWeb\Arsse\REST\Request $req): Response {
// if a method other than GET was used, this is an error if (!preg_match("<^/?$>", $req->path)) {
if ($req->method != "GET") { // 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(405, "", "", ["Allow: GET"]); return new Response(404);
} } elseif ($req->method=="OPTIONS") {
if (preg_match("<^/?$>", $req->path)) { // if the request method is OPTIONS, respond accordingly
// if the request path is an empty string or just a slash, return the supported versions 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 = [ $out = [
'apiLevels' => [ 'apiLevels' => [
'v1-2', 'v1-2',
] ]
]; ];
return new Response(200, $out); 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(); $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() { public function testRespondToInvalidPaths() {
$errs = [ $errs = [
404 => [ 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'))); $this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/1", '<data/>', 'application/json')));
} }
public function testSendAuthenticationChallenge() { public function testRespondToOptionsRequests() {
Phake::when(Arsse::$user)->authHTTP->thenReturn(false); $exp = new Response(204, "", "", [
$exp = new Response(401, "", "", ['WWW-Authenticate: Basic realm="'.REST\NextCloudNews\V1_2::REALM.'"']); "Allow: HEAD,GET,POST",
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/"))); "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() { public function testListFolders() {

13
tests/REST/NextCloudNews/TestNCNVersionDiscovery.php

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

Loading…
Cancel
Save