Add POST support for RPC API

Message ID 20200703175303.4366-1-kevr.gtalk@gmail.com
State New
Headers show
Series Add POST support for RPC API | expand

Commit Message

Kevin Morris July 3, 2020, 5:53 p.m. UTC
When we received a POST method from a client, deduce our `$data`
by determining which `Content-Type` is used.

Supported `Content-Type`s
=========================

* `application/json`
* `multipart/form-data`

`application/json` POST Example, searching for 'test':
```
curl -X POST -H 'Content-Type: application/data' \
    --data '{"v": "5", "type": "search", "arg": "test"}' \
    https://aur.archlinux.org/rpc/
```

`multipart/form-data` POST Example, search for 'test':
```
curl -X POST -H 'Content-Type: multipart/form-data' \
    -F v=5 -F type=search -F arg=test \
    https://aur.archlinux.org/rpc/
```

This change was written as a solution to
https://bugs.archlinux.org/task/49089.

NOTE: This commit is not final; would like a review and a bit more
consideration on `multipart/form-data` and ensuring that we're
handling everything we need to.

Signed-off-by: Kevin Morris <kevr.gtalk@gmail.com>
---
 web/html/rpc.php | 37 ++++++++++++++++++++++++++++++++++---
 1 file changed, 34 insertions(+), 3 deletions(-)

Comments

Kevin Morris July 3, 2020, 5:55 p.m. UTC | #1
Apologies, `application/data` in the commit message's example should be
`application/json`, and will need to be updated after review(s) in the
final commit.

On Fri, Jul 3, 2020 at 10:53 AM Kevin Morris <kevr.gtalk@gmail.com> wrote:

> When we received a POST method from a client, deduce our `$data`
> by determining which `Content-Type` is used.
>
> Supported `Content-Type`s
> =========================
>
> * `application/json`
> * `multipart/form-data`
>
> `application/json` POST Example, searching for 'test':
> ```
> curl -X POST -H 'Content-Type: application/data' \
>     --data '{"v": "5", "type": "search", "arg": "test"}' \
>     https://aur.archlinux.org/rpc/
> ```
>
> `multipart/form-data` POST Example, search for 'test':
> ```
> curl -X POST -H 'Content-Type: multipart/form-data' \
>     -F v=5 -F type=search -F arg=test \
>     https://aur.archlinux.org/rpc/
> ```
>
> This change was written as a solution to
> https://bugs.archlinux.org/task/49089.
>
> NOTE: This commit is not final; would like a review and a bit more
> consideration on `multipart/form-data` and ensuring that we're
> handling everything we need to.
>
> Signed-off-by: Kevin Morris <kevr.gtalk@gmail.com>
> ---
>  web/html/rpc.php | 37 ++++++++++++++++++++++++++++++++++---
>  1 file changed, 34 insertions(+), 3 deletions(-)
>
> diff --git a/web/html/rpc.php b/web/html/rpc.php
> index 64c95622..41762aa3 100644
> --- a/web/html/rpc.php
> +++ b/web/html/rpc.php
> @@ -2,16 +2,47 @@
>  set_include_path(get_include_path() . PATH_SEPARATOR . '../lib');
>  include_once("aurjson.class.php");
>
> -if ( $_SERVER['REQUEST_METHOD'] != 'GET' ) {
> +$exposed_methods = array('GET', 'POST');
> +$request_method = strtoupper($_SERVER['REQUEST_METHOD']);
> +
> +if ( !in_array($request_method, $exposed_methods) ) {
>         header('HTTP/1.1 405 Method Not Allowed');
>         exit();
>  }
>
> -if ( isset($_GET['type']) ) {
> +if ( $request_method === 'GET' ) {
> +       $data = $_GET;
> +} else {
> +       // Otherwise, we were given a POST method, and we'll do some more
> +       // work to deduce our data input.
> +
> +       // Extract Content-Type; remove any trailing arguments from the
> string.
> +       $content_type = $_SERVER['CONTENT_TYPE'];
> +       if ( strpos($content_type, ';') ) {
> +               // Example: `multipart/form-data; boundary blahblah` is
> extracted as
> +               // `multipart/form-data`.
> +               $content_type = explode(';', $content_type)[0];
> +       }
> +       $content_type = rtrim(trim($content_type));
> +
> +       if ($content_type === 'application/json') {
> +               $json = file_get_contents('php://input');
> +               $data = json_decode($json, true);
> +       } elseif ($content_type === 'multipart/form-data') {
> +               $data = $_POST;
> +       } else {
> +               header('HTTP/1.1 400 Bad Request');
> +               echo "Error: Unsupported Content-Type header.\n";
> +               exit();
> +       }
> +}
> +
> +if ( isset($data['type']) ) {
>         $rpc_o = new AurJSON();
> -       echo $rpc_o->handle($_GET);
> +       echo $rpc_o->handle($data);
>  }
>  else {
>         echo file_get_contents('../../doc/rpc.html');
>  }
> +
>  ?>
> --
> 2.20.1
>
>

Patch

diff --git a/web/html/rpc.php b/web/html/rpc.php
index 64c95622..41762aa3 100644
--- a/web/html/rpc.php
+++ b/web/html/rpc.php
@@ -2,16 +2,47 @@ 
 set_include_path(get_include_path() . PATH_SEPARATOR . '../lib');
 include_once("aurjson.class.php");
 
-if ( $_SERVER['REQUEST_METHOD'] != 'GET' ) {
+$exposed_methods = array('GET', 'POST');
+$request_method = strtoupper($_SERVER['REQUEST_METHOD']);
+
+if ( !in_array($request_method, $exposed_methods) ) {
 	header('HTTP/1.1 405 Method Not Allowed');
 	exit();
 }
 
-if ( isset($_GET['type']) ) {
+if ( $request_method === 'GET' ) {
+	$data = $_GET;
+} else {
+	// Otherwise, we were given a POST method, and we'll do some more
+	// work to deduce our data input.
+
+	// Extract Content-Type; remove any trailing arguments from the string.
+	$content_type = $_SERVER['CONTENT_TYPE'];
+	if ( strpos($content_type, ';') ) {
+		// Example: `multipart/form-data; boundary blahblah` is extracted as
+		// `multipart/form-data`.
+		$content_type = explode(';', $content_type)[0];
+	}
+	$content_type = rtrim(trim($content_type));
+
+	if ($content_type === 'application/json') {
+		$json = file_get_contents('php://input');
+		$data = json_decode($json, true);
+	} elseif ($content_type === 'multipart/form-data') {
+		$data = $_POST;
+	} else {
+		header('HTTP/1.1 400 Bad Request');
+		echo "Error: Unsupported Content-Type header.\n";
+		exit();
+	}
+}
+
+if ( isset($data['type']) ) {
 	$rpc_o = new AurJSON();
-	echo $rpc_o->handle($_GET);
+	echo $rpc_o->handle($data);
 }
 else {
 	echo file_get_contents('../../doc/rpc.html');
 }
+
 ?>