From 05866d02f833ade20953331e9fe0d237a65b9e7b Mon Sep 17 00:00:00 2001 From: Victor Westerlund Date: Wed, 18 Jan 2023 15:52:24 +0100 Subject: [PATCH] feat: add HTTP client (#1) * feat: add HTTP client * feat(doc): update readme * fix(doc): missed a rename --- README.md | 18 ++++----- client | 4 +- composer.json | 4 +- src/Client.php | 88 ++++++++++++++++++++++++++++++++++++++++++++ src/SocketClient.php | 43 ---------------------- 5 files changed, 101 insertions(+), 56 deletions(-) create mode 100644 src/Client.php delete mode 100644 src/SocketClient.php diff --git a/README.md b/README.md index cbf5513..a091fb4 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ -# Reflect API UNIX socket client for PHP +# Reflect API client for PHP -Make requests to endpoints created with the [Reflect API framework](https://github.com/victorwesterlund/reflect) running the same machine. +Make requests to an API built using the Reflect API framework over HTTP or UNIX sockets. This program comes with both an extendable/instantiable class that you can integrate with existing PHP code, or as a stand-alone CLI which can be piped into other programs. --- -Make a request with `SocketClient->call()`. It will return the response as an array of length 2. +Make a request with `Client->call()`. It will return the response as an array of length 2. - The first value is the HTTP-equivalent response code. - The second value is the response body ```php -$client = new Reflect\SocketClient("/path/to/socket"); +$client = new Reflect\Client(""); $client->call("foo", Method::GET); // (array) [200, "bar"] $client->call("foo", Method::POST, [ @@ -26,7 +26,7 @@ Requires PHP 8.1 or newer, and of course an instance of the [Reflect socket serv 1. **Install with composer** ``` - composer require reflect/socket-server + composer require reflect/client ``` 2. **Initialize the class** @@ -34,7 +34,7 @@ Requires PHP 8.1 or newer, and of course an instance of the [Reflect socket serv ```php require_once "/vendor/autoload.php"; - $client = new Reflect\SocketClient("/path/to/socket"); + $client = new Reflect\Client(""); ``` 3. **Make API request** @@ -66,7 +66,7 @@ You can also run this from the command line with php client [payload] ``` -and it will return a serialized JSON array with the same structure as described in the `SocketClient->call()` return. +and it will return a serialized JSON array with the same structure as described in the `Client->call()` return. *Example* ```sh @@ -80,12 +80,12 @@ Requires PHP CLI 8.1 or greater, and of course an instance of the [Reflect socke 1. **Clone repo** ``` - git clone https://github.com/victorwesterlund/reflect-socket-client-php + git clone https://github.com/victorwesterlund/reflect-client-php ``` 2. **Run from command line** ``` - cd reflect-socket-client-php + cd reflect-client-php php client [payload] ``` diff --git a/client b/client index 165571c..201221a 100644 --- a/client +++ b/client @@ -4,7 +4,7 @@ die("Must be run from command line\n"); } - require_once __DIR__ . "/src/SocketClient.php"; + require_once __DIR__ . "/src/Client.php"; // Require 3 to 4 arguments if ($argc < 4 || $argc > 4) { @@ -13,7 +13,7 @@ } // Connect to the socket server - $client = new SocketClient($argv[1]); + $client = new Client($argv[1], ConType::AF_UNIX); // Get endpoint, method, and optional payload $args = $argv; diff --git a/composer.json b/composer.json index 035929c..670bbd1 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { - "name": "reflect/socket-client", - "description": "Extendable PHP interface for communicating with Reflect API over UNIX sockets", + "name": "reflect/client", + "description": "Extendable PHP interface for communicating with Reflect API over HTTP or UNIX sockets", "type": "library", "license": "GPL-2.0-only", "authors": [ diff --git a/src/Client.php b/src/Client.php new file mode 100644 index 0000000..8fc2c7c --- /dev/null +++ b/src/Client.php @@ -0,0 +1,88 @@ +_con = $con ?: $this::resolve_contype($endpoint); + $this->_endpoint = $endpoint; + + // Initialize socket properties + if ($this->_con === ConType::AF_UNIX) { + $this->socket = socket_create(AF_UNIX, SOCK_STREAM, 0); + $conn = socket_connect($this->socket, $this->_endpoint); + } + } + + // Resolve connection type from endpoint string. + // If the string is a valid URL we will treat it as HTTP otherwise we will + // assume it's a path on disk to a UNIX socket file. + private static function resolve_contype(string $endpoint): ConType { + return filter_var($endpoint, FILTER_VALIDATE_URL) + ? ConType::HTTP + : ConType::AF_UNIX; + } + + // Make request and return response over HTTP + private function http_call(string $endpoint, Method $method, array $payload = null): array { + $data = stream_context_create([ + "http" => [ + "header" => "Content-Type: application/json", + "method" => $method->value, + "ignore_errors" => true, + "content" => !empty($payload) ? json_encode($payload) : "" + ] + ]); + + $resp = file_get_contents($this->_endpoint . $endpoint, false, $data); + + // Get HTTP response code from $http_response_header which materializes out of + // thin air after file_get_contents(). The first header line and second word will + // contain the status code. + $resp_code = (int) explode(" ", $http_response_header[0])[1]; + + return [$resp_code, $resp]; + } + + // Make request and return response over socket + private function socket_txn(string $payload): string { + $tx = socket_write($this->socket, $payload, strlen($payload)); + $rx = socket_read($this->socket, 2024); + + if (!$tx || !$rx) { + throw new Error("Failed to complete transaction"); + } + + return $rx; + } + + // Create HTTP-like JSON with ["","","[payload]"] and return + // respone from endpoint as [""] + public function call(string $endpoint, Method $method, array $payload = null): array { + // Call endpoint over UNIX socket + if ($this->_con === ConType::AF_UNIX) { + return json_decode($this->socket_txn(json_encode([ + $endpoint, + $method->value, + $payload + ]))); + } + + // Call endpoint over HTTP + return $this->http_call(...func_get_args()); + } + } \ No newline at end of file diff --git a/src/SocketClient.php b/src/SocketClient.php deleted file mode 100644 index abc6620..0000000 --- a/src/SocketClient.php +++ /dev/null @@ -1,43 +0,0 @@ -socket = socket_create(AF_UNIX, SOCK_STREAM, 0); - $conn = socket_connect($this->socket, $path); - - if (!$conn) { - throw new Error("Unable to connect to socket"); - } - } - - // Make request and return response - private function txn(string $payload): string { - $tx = socket_write($this->socket, $payload, strlen($payload)); - $rx = socket_read($this->socket, 2024); - - if (!$tx || !$rx) { - throw new Error("Failed to complete transaction"); - } - - return $rx; - } - - // Create HTTP-like JSON with ["","","[payload]"] - public function call(string $endpoint, Method $method, array $payload = null): mixed { - return json_decode($this->txn(json_encode([ - $endpoint, - $method->value, - $payload - ]))); - } - } \ No newline at end of file