diff --git a/4dev/tests/AAASetupData/requests/http_requests.php b/4dev/tests/AAASetupData/requests/http_requests.php index f7b19b40..c3559020 100644 --- a/4dev/tests/AAASetupData/requests/http_requests.php +++ b/4dev/tests/AAASetupData/requests/http_requests.php @@ -12,7 +12,7 @@ declare(strict_types=1); /** * build return json * - * @param array $http_headers + * @param array $http_headers * @param string $body * @return string */ @@ -34,15 +34,18 @@ $http_headers = array_filter($_SERVER, function ($value, $key) { header("Content-Type: application/json; charset=UTF-8"); +// if the header has Authorization and RunAuthTest then exit with 401 if (!empty($http_headers['HTTP_AUTHORIZATION']) && !empty($http_headers['HTTP_RUNAUTHTEST'])) { header("HTTP/1.1 401 Unauthorized"); - print buildContent($http_headers, '["code": 401, "content": {"Error" => "Not Authorized"}]'); + print buildContent($http_headers, '{"code": 401, "content": {"Error": "Not Authorized"}}'); exit; } -print buildContent( - $http_headers, - file_get_contents('php://input') ?: '["code": 500, "content": {"Error" => "file_get_contents failed"}]' -); +if (($file_get = file_get_contents('php://input')) === false) { + header("HTTP/1.1 404 Not Found"); + print buildContent($http_headers, '{"code": 404, "content": {"Error": "file_get_contents failed"}}'); + exit; +} +print buildContent($http_headers, $file_get); // __END__ diff --git a/4dev/tests/UrlRequests/CoreLibsUrlRequestsCurlTest.php b/4dev/tests/UrlRequests/CoreLibsUrlRequestsCurlTest.php index 02beb9eb..8b33796c 100644 --- a/4dev/tests/UrlRequests/CoreLibsUrlRequestsCurlTest.php +++ b/4dev/tests/UrlRequests/CoreLibsUrlRequestsCurlTest.php @@ -21,7 +21,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase private string $url_basic_start = ''; private string $url_basic_end = ''; private array $default_config = [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => '', 'query' => [], 'headers' => [], @@ -109,7 +109,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase 'no config' => [ 'config' => null, 'expected_set' => [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => '', 'query' => [], 'headers' => [], @@ -125,7 +125,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase 'setup all possible configs' => [ 'config' => [ 'auth' => ['user', 'passowrd', 'Basic'], - 'exception_on_not_authorized' => true, + 'http_errors' => false, 'base_uri' => 'http://foo.bar.com', 'headers' => [ 'something' => 'other', @@ -138,7 +138,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase ], 'expected_set' => [ 'auth' => ['user', 'passowrd', 'Basic'], - 'exception_on_not_authorized' => true, + 'http_errors' => false, 'base_uri' => 'http://foo.bar.com', 'headers' => [ 'something' => 'other', @@ -161,7 +161,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase 'base_uri' => 'http://bar.foo.com' ], 'expected_set' => [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => 'http://bar.foo.com', 'query' => [], 'headers' => [], @@ -179,7 +179,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase 'base_uri' => 'http://bar.foo.com' ], 'expected_set' => [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => 'http://bar.foo.com', 'query' => [], 'headers' => [], @@ -191,7 +191,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase 'set_header_add' => null, 'remove_header' => null, 'expected_change' => [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => 'http://bar.baz.com', 'query' => [], 'headers' => [], @@ -203,7 +203,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase 'set header new' => [ 'config' => null, 'expected_set' => [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => '', 'query' => [], 'headers' => [], @@ -217,7 +217,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase 'set_header_add' => false, 'remove_header' => null, 'expected_change' => [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => '', 'query' => [], 'headers' => [ @@ -234,7 +234,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase ], ], 'expected_set' => [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => '', 'query' => [], 'headers' => [ @@ -250,7 +250,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase 'set_header_add' => false, 'remove_header' => null, 'expected_change' => [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => '', 'query' => [], 'headers' => [ @@ -267,7 +267,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase ], ], 'expected_set' => [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => '', 'query' => [], 'headers' => [ @@ -283,7 +283,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase 'set_header_add' => true, 'remove_header' => null, 'expected_change' => [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => '', 'query' => [], 'headers' => [ @@ -301,7 +301,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase ], ], 'expected_set' => [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => '', 'query' => [], 'headers' => [ @@ -317,7 +317,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase 'remove-entry' => 'foo' ], 'expected_change' => [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => '', 'query' => [], 'headers' => [], @@ -332,7 +332,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase ], ], 'expected_set' => [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => '', 'query' => [], 'headers' => [ @@ -348,7 +348,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase 'remove-entry' => null ], 'expected_change' => [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => '', 'query' => [], 'headers' => [], @@ -363,7 +363,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase ], ], 'expected_set' => [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => '', 'query' => [], 'headers' => [ @@ -379,7 +379,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase 'remove-entry' => null ], 'expected_change' => [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => '', 'query' => [], 'headers' => [], @@ -394,7 +394,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase ], ], 'expected_set' => [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => '', 'query' => [], 'headers' => [ @@ -410,7 +410,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase 'remove-entry' => 'foo' ], 'expected_change' => [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => '', 'query' => [], 'headers' => [ @@ -427,7 +427,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase ], ], 'expected_set' => [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => '', 'query' => [], 'headers' => [ @@ -443,7 +443,7 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase 'remove-entry' => ['foo', 'bar',] ], 'expected_change' => [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => '', 'query' => [], 'headers' => [ @@ -1016,32 +1016,29 @@ final class CoreLibsUrlRequestsCurlTest extends TestCase { $curl = new \CoreLibs\UrlRequests\Curl(); $this->expectException(\RuntimeException::class); - $this->expectExceptionMessageMatches("/CurlError/"); + $this->expectExceptionMessageMatches("/CurlExecError/"); // invalid yrl $response = $curl->request('get', 'as-4939345!#$%'); } /** - * TODO: Exception:UnauthorizedRequest + * TODO: Exception:BadRequest * - * @testdox UrlRequests\Curl Exception:UnauthorizedRequest + * @testdox UrlRequests\Curl Exception:BadRequest * * @return void */ - public function testExceptionUnauthorizedRequest(): void + public function testExceptionBadRequest(): void { - $curl = new \CoreLibs\UrlRequests\Curl(["exception_on_not_authorized" => true]); + $curl = new \CoreLibs\UrlRequests\Curl(["http_errors" => true]); $this->expectException(\RuntimeException::class); - $this->expectExceptionMessageMatches("/UnauthorizedRequest/"); + $this->expectExceptionMessageMatches("/ClientError/"); $response = $curl->get($this->url_basic, [ "headers" => [ "Authorization" => "schmalztiegel", "RunAuthTest" => "yes", ] ]); - // $response = $curl->get('https://httpbin.org/bearer', [ - // "headers" => ["Authorization" => "schmalztiegel"] - // ]); } /** diff --git a/www/admin/UrlRequests.target.php b/www/admin/UrlRequests.target.php index ad53829f..c47a0fad 100644 --- a/www/admin/UrlRequests.target.php +++ b/www/admin/UrlRequests.target.php @@ -15,7 +15,7 @@ $log = new CoreLibs\Logging\Logging([ /** * build return json * - * @param array $http_headers + * @param array $http_headers * @param string $body * @return string */ @@ -25,7 +25,8 @@ function buildContent(array $http_headers, string $body): string 'HEADERS' => $http_headers, "REQUEST_TYPE" => $_SERVER['REQUEST_METHOD'], "PARAMS" => $_GET, - "BODY" => Json::jsonConvertToArray($body) + "BODY" => Json::jsonConvertToArray($body), + // "STRING_BODY" => $body, ]); } @@ -40,11 +41,15 @@ header("Content-Type: application/json; charset=UTF-8"); // if the header has Authorization and RunAuthTest then exit with 401 if (!empty($http_headers['HTTP_AUTHORIZATION']) && !empty($http_headers['HTTP_RUNAUTHTEST'])) { header("HTTP/1.1 401 Unauthorized"); - print buildContent($http_headers, '["code": 401, "content": {"Error" => "Not Authorized"}]'); + print buildContent($http_headers, '{"code": 401, "content": {"Error": "Not Authorized"}}'); exit; } -$file_get = file_get_contents('php://input') ?: '{"Error" => "file_get_contents failed"}'; +if (($file_get = file_get_contents('php://input')) === false) { + header("HTTP/1.1 404 Not Found"); + print buildContent($http_headers, '{"code": 404, "content": {"Error": "file_get_contents failed"}}'); + exit; +} // str_replace('\"', '"', trim($file_get, '"')); $log->debug('SERVER', $log->prAr($_SERVER)); diff --git a/www/admin/class_test.url-requests.curl.php b/www/admin/class_test.url-requests.curl.php index f657e7de..ed6918dd 100644 --- a/www/admin/class_test.url-requests.curl.php +++ b/www/admin/class_test.url-requests.curl.php @@ -244,7 +244,7 @@ print "
"; try { $uc = new Curl([ "base_uri" => 'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/', - "exception_on_not_authorized" => false, + "http_errors" => false, "headers" => [ "Authorization" => "schmalztiegel", "RunAuthTest" => "yes", @@ -262,7 +262,7 @@ print "AUTH REQUEST WITH EXCEPTION:
"; try { $uc = new Curl([ "base_uri" => 'https://soba.egplusww.jp/developers/clemens/core_data/php_libraries/trunk/www/admin/', - "exception_on_not_authorized" => true, + "http_errors" => true, "headers" => [ "Authorization" => "schmalztiegel", "RunAuthTest" => "yes", @@ -285,7 +285,7 @@ $uc = new Curl([ ] ]); $response = $uc->get('UrlRequests.target.php', ["headers" => null, "query" => ["test" => "one-test"]]); -print "AUTH REQUEST:
" . print_r($response, true) . "
"; +print "HEADER RESET REQUEST:
" . print_r($response, true) . "
"; print "[uc] SENT URL: " . $uc->getUrlSent() . "
"; print "[uc] SENT URL PARSED:
" . print_r($uc->getUrlParsedSent(), true) . "
"; print "[uc] SENT HEADERS:
" . print_r($uc->getHeadersSent(), true) . "
"; diff --git a/www/lib/CoreLibs/UrlRequests/Curl.php b/www/lib/CoreLibs/UrlRequests/Curl.php index 9387245c..80ab9af3 100644 --- a/www/lib/CoreLibs/UrlRequests/Curl.php +++ b/www/lib/CoreLibs/UrlRequests/Curl.php @@ -10,7 +10,7 @@ * https://docs.guzzlephp.org/en/stable/index.html * * Requests are guzzleHttp compatible - * Config for setup is guzzleHttp compatible (except the exception_on_not_authorized) + * Config for setup is guzzleHttp compatible (except the http_errors) * Any setters and getters are only for this class */ @@ -35,6 +35,12 @@ class Curl implements Interface\RequestsInterface private const HAVE_POST_FIELDS = ["post", "put", "patch", "delete"]; /** @var array list of requests that must have a body */ private const MANDATORY_POST_FIELDS = ["post", "put", "patch"]; + /** @var int http ok request */ + public const HTTP_OK = 200; + /** @var int http ok creted response */ + public const HTTP_CREATED = 201; + /** @var int http ok no content */ + public const HTTP_NO_CONTENT = 204; /** @var int error bad request */ public const HTTP_BAD_REQUEST = 400; /** @var int error not authorized Request */ @@ -47,18 +53,12 @@ class Curl implements Interface\RequestsInterface public const HTTP_CONFLICT = 409; /** @var int error unprocessable entity */ public const HTTP_UNPROCESSABLE_ENTITY = 422; - /** @var int http ok request */ - public const HTTP_OK = 200; - /** @var int http ok creted response */ - public const HTTP_CREATED = 201; - /** @var int http ok no content */ - public const HTTP_NO_CONTENT = 204; /** @var int major version for user agent */ public const MAJOR_VERSION = 1; // the config is set to be as much compatible to guzzelHttp as possible // phpcs:disable Generic.Files.LineLength - /** @var array{auth?:array{0:string,1:string,2:string},exception_on_not_authorized:bool,base_uri:string,headers:array>,query:array,timeout:float,connection_timeout:float} config settings as + /** @var array{auth?:array{0:string,1:string,2:string},http_errors:bool,base_uri:string,headers:array>,query:array,timeout:float,connection_timeout:float} config settings as *phpcs:enable Generic.Files.LineLength * auth: [0: user, 1: password, 2: auth type] * base_uri: base url to set, will prefix all urls given in calls @@ -66,10 +66,10 @@ class Curl implements Interface\RequestsInterface * timeout: default 0, in seconds (CURLOPT_TIMEOUT_MS) * connect_timeout: default 300, in seconds (CURLOPT_CONNECTTIMEOUT_MS) * : below is not a guzzleHttp config - * exception_on_not_authorized: bool true/false for throwing exception on auth error + * http_errors: default true, bool true/false for throwing exception on >= 400 HTTP errors */ private array $config = [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => '', 'query' => [], 'headers' => [], @@ -115,14 +115,14 @@ class Curl implements Interface\RequestsInterface * Set the main configuration * * phpcs:disable Generic.Files.LineLength - * @param array{auth?:array{0:string,1:string,2:string},exception_on_not_authorized?:bool,base_uri?:string,headers?:array>,query?:array,timeout?:float,connection_timeout?:float} $config + * @param array{auth?:array{0:string,1:string,2:string},http_errors?:bool,base_uri?:string,headers?:array>,query?:array,timeout?:float,connection_timeout?:float} $config * @return void * phpcs:enable Generic.Files.LineLength */ private function setConfiguration(array $config) { $default_config = [ - 'exception_on_not_authorized' => false, + 'http_errors' => true, 'base_uri' => '', 'query' => [], 'headers' => [], @@ -157,10 +157,10 @@ class Curl implements Interface\RequestsInterface } // only set if bool if ( - !isset($config['exception_on_not_authorized']) || - !is_bool($config['exception_on_not_authorized']) + !isset($config['http_errors']) || + !is_bool($config['http_errors']) ) { - $config['exception_on_not_authorized'] = false; + $config['http_errors'] = true; } if (!empty($config['base_uri'])) { if (($parsed_base_uri = $this->parseUrl($config['base_uri'])) !== false) { @@ -480,8 +480,8 @@ class Curl implements Interface\RequestsInterface if (!in_array($type, self::VALID_REQUEST_TYPES)) { throw new RuntimeException( json_encode([ - 'status' => 'FAILURE', - 'code' => 'C003', + 'status' => 'ERROR', + 'code' => 'R002', 'type' => 'InvalidRequestType', 'message' => 'Invalid request type set: ' . $type, 'context' => [ @@ -635,7 +635,7 @@ class Curl implements Interface\RequestsInterface // execute query $http_result = curl_exec($handle); if ($http_result === true) { - // only if CURLOPT_RETURNTRANSFER + // only if CURLOPT_RETURNTRANSFER is turned off return (string)self::HTTP_OK; } elseif ($http_result !== false) { return $http_result; @@ -666,7 +666,7 @@ class Curl implements Interface\RequestsInterface json_encode([ 'status' => 'FAILURE', 'code' => 'C002', - 'type' => 'CurlError', + 'type' => 'CurlExecError', 'message' => $message, 'context' => [ 'url' => $url, @@ -681,12 +681,13 @@ class Curl implements Interface\RequestsInterface // MARK: curl response handler /** - * Handle curl response and not auth 401 errors + * Handle curl response, will throw exception on anything that is lower 400 + * can be turned off by setting http_errors to false * - * @param string $http_result - * @param \CurlHandle $handle + * @param string $http_result result string from the url call + * @param \CurlHandle $handle Curl handler * @return string http response code - * @throws \RuntimeException Auth error + * @throws \RuntimeException if http_errors is true then will throw exception on any response code >= 400 */ private function handleCurlResponse( string $http_result, @@ -694,27 +695,28 @@ class Curl implements Interface\RequestsInterface ): string { $http_response = curl_getinfo($handle, CURLINFO_RESPONSE_CODE); if ( - empty($this->config['exception_on_not_authorized']) || - $http_response !== self::HTTP_NOT_AUTHORIZED + empty($this->config['http_errors']) || + $http_response < self::HTTP_BAD_REQUEST ) { return (string)$http_response; } + // set curl error number $err = curl_errno($handle); - // extract all the error codes - $result_ar = json_decode((string)$http_result, true); - - $url = curl_getinfo($handle, CURLINFO_EFFECTIVE_URL); - // throw Error here with all codes throw new RuntimeException( json_encode([ 'status' => 'ERROR', - 'code' => $http_response, - 'type' => 'UnauthorizedRequest', - 'message' => 'Request could not be finished successfully because of an authorization error', + 'code' => 'H' . (string)$http_response, + 'type' => $http_response < 500 ? 'ClientError' : 'ServerError', + 'message' => 'Request could not be finished successfully because of bad request response', 'context' => [ - 'url' => $url, - 'result' => $result_ar, + 'http_response' => $http_response, + // extract all the error content if returned + 'result' => json_decode((string)$http_result, true), + // curl internal error number + 'curl_errno' => $err, + // the full curl info block + 'curl_info' => curl_getinfo($handle), ], ]) ?: '', $err @@ -761,7 +763,7 @@ class Curl implements Interface\RequestsInterface throw new \UnexpectedValueException( json_encode([ 'status' => 'ERROR', - 'code' => 'C004', + 'code' => 'R001', 'type' => 'DuplicatedArrayKey', 'message' => 'Key already exists in the headers', 'context' => [