├── tests ├── bootstrap.php ├── edgerc │ ├── .edgerc │ ├── .edgerc.invalid │ ├── .edgerc.testing │ ├── .edgerc.invalid-spaces │ └── .edgerc.default-testing ├── random_bytes.php ├── SimpleLog.php ├── Handler │ ├── AuthenticationTest.php │ ├── DebugTest.php │ └── VerboseTest.php ├── testdata.json └── ClientTest.php ├── src ├── Exception.php ├── Exception │ ├── HandlerException.php │ └── HandlerException │ │ └── IOException.php ├── Handler │ ├── Authentication.php │ ├── Debug.php │ └── Verbose.php ├── Cli.php └── Client.php ├── tools ├── check-version.sh ├── phpunit-4.0-to-junit.xsl └── build-phar.sh ├── .gitignore ├── bin └── http ├── box.json.dist ├── phpunit.xml.dist ├── examples ├── get-credentials.php ├── create-credentials.php ├── delete-credentials.php ├── update-credentials.php └── README.md ├── composer.json ├── CHANGELOG.md ├── README.md └── LICENSE /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | &2 echo "Version \"$1\" does not match Client.php: \"$VERIFY_VERSION\"") 6 | exit -1 7 | fi 8 | exit 0 -------------------------------------------------------------------------------- /src/Exception/HandlerException.php: -------------------------------------------------------------------------------- 1 | testMakeNonceRandomBytes() 6 | * 7 | * @param $size 8 | * @return string 9 | */ 10 | function random_bytes($size) 11 | { 12 | return __FUNCTION__; 13 | } 14 | -------------------------------------------------------------------------------- /src/Exception/HandlerException/IOException.php: -------------------------------------------------------------------------------- 1 | setAuth()}. 14 | */ 15 | 16 | require_once __DIR__ . '/../vendor/autoload.php'; 17 | 18 | $cli = new \Akamai\Open\EdgeGrid\Cli(); 19 | $cli->run(); -------------------------------------------------------------------------------- /box.json.dist: -------------------------------------------------------------------------------- 1 | { 2 | "algorithm": "SHA512", 3 | "directories": [ 4 | "src", 5 | "vendor/akamai-open/edgegrid-auth/src" 6 | ], 7 | "files": [ 8 | "LICENSE", 9 | "README.md", 10 | "CHANGELOG.md" 11 | ], 12 | "finder": [ 13 | { 14 | "name": "*.php", 15 | "exclude": [ 16 | "bin", 17 | "docs", 18 | "examples", 19 | "Tests", 20 | "tests", 21 | "tools" 22 | ], 23 | "in": "vendor" 24 | } 25 | ], 26 | "compression": "BZ2", 27 | "git-version": "package_version", 28 | "main": false, 29 | "output": "akamai-open-edgegrid-client.phar", 30 | "stub": "build/phar/stub.php" 31 | } 32 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./src 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ./tests 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /examples/get-credentials.php: -------------------------------------------------------------------------------- 1 | 'application/json', 22 | ]; 23 | 24 | $response = $client->get('/identity-management/v3/api-clients/self/credentials', $headers); 25 | 26 | echo $response->getBody(); -------------------------------------------------------------------------------- /examples/create-credentials.php: -------------------------------------------------------------------------------- 1 | 'application/json', 22 | ]; 23 | 24 | $response = $client->post('/identity-management/v3/api-clients/self/credentials', $headers); 25 | 26 | echo $response->getBody(); -------------------------------------------------------------------------------- /tools/phpunit-4.0-to-junit.xsl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /examples/delete-credentials.php: -------------------------------------------------------------------------------- 1 | 'application/json', 26 | ]; 27 | 28 | $response = $client->delete('/identity-management/v3/api-clients/self/credentials/123456', $headers); 29 | 30 | echo $response->getBody(); -------------------------------------------------------------------------------- /tools/build-phar.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export PATH=vendor/bin:$PATH 3 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 4 | if [[ -z $1 ]] 5 | then 6 | printf "What version are you building? [dev]: " 7 | 8 | read VERSION 9 | if [[ -z $VERSION ]] 10 | then 11 | VERSION="dev" 12 | fi 13 | 14 | export VERSION="$VERSION" 15 | else 16 | export VERSION="$1" 17 | fi 18 | 19 | if [[ $VERSION != "dev" ]] 20 | then 21 | $DIR/check-version.sh $VERSION 22 | if [[ $? -ne 0 ]] 23 | then 24 | exit -1 25 | fi 26 | fi 27 | 28 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 29 | cd $DIR && cd ../ 30 | if [[ ! -d "build/phar" ]] 31 | then 32 | mkdir -p build/phar 33 | fi 34 | 35 | if [[ -f $HOME/.composer/vendor/bin/box ]] 36 | then 37 | composer install --no-dev -o -q 38 | php -dphar.readonly=0 $HOME/.composer/vendor/bin/box compile 39 | composer install -q 40 | else 41 | composer install -o -q 42 | php -dphar.readonly=0 ./vendor/bin/box compile 43 | composer install -q 44 | fi 45 | 46 | 47 | mv akamai-open-edgegrid-client.phar "akamai-open-edgegrid-client-${VERSION}.phar" 48 | 49 | php "akamai-open-edgegrid-client-${VERSION}.phar" 50 | 51 | echo " test.php 55 | echo "Running smoke test"; 56 | php test.php 57 | rm test.php -------------------------------------------------------------------------------- /examples/update-credentials.php: -------------------------------------------------------------------------------- 1 | 'application/json', 28 | 'Accept' => 'application/json', 29 | ]; 30 | 31 | $body = [ 32 | 'json' => [ 33 | 'status' => 'INACTIVE', 34 | 'description' => 'Update this credential', 35 | 'expiresOn' => '2024-12-11T23:06:59.000Z' 36 | ] 37 | ]; 38 | 39 | $response = $client->put('/identity-management/v3/api-clients/self/credentials/123456', $body, $headers); 40 | 41 | echo $response->getBody(); -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "akamai-open/edgegrid-client", 3 | "description": "Implements the Akamai {OPEN} EdgeGrid Authentication specified by https://developer.akamai.com/introduction/Client_Auth.html", 4 | "keywords": ["akamai", "open", "edgegrid", "authentication", "client"], 5 | "type": "library", 6 | "license": "Apache-2.0", 7 | "homepage": "https://github.com/akamai-open/AkamaiOPEN-edgegrid-php", 8 | "require": { 9 | "php": ">=8.1", 10 | "akamai-open/edgegrid-auth": "^2.0", 11 | "guzzlehttp/guzzle": "^7.5.0", 12 | "psr/http-client": "^1.0.2", 13 | "psr/log": "^3.0", 14 | "monolog/monolog": "^3.3", 15 | "league/climate": "~3.2" 16 | }, 17 | "require-dev": { 18 | "phpunit/phpunit": "^9.5", 19 | "phpspec/prophecy": "~1.0", 20 | "squizlabs/php_codesniffer": "^3.7", 21 | "friendsofphp/php-cs-fixer": "^3.9", 22 | "humbug/box": ">=4.3.8" 23 | }, 24 | "autoload": { 25 | "psr-4": { 26 | "Akamai\\Open\\EdgeGrid\\": "src/" 27 | } 28 | }, 29 | "autoload-dev": { 30 | "psr-4": { 31 | "Akamai\\Open\\EdgeGrid\\Tests\\": ["tests", "vendor/akamai-open/edgegrid-auth/tests"] 32 | } 33 | }, 34 | "config": { 35 | "platform": { 36 | "php": "8.1" 37 | } 38 | }, 39 | "bin": [ 40 | "bin/http" 41 | ], 42 | "scripts": { 43 | "build": [ 44 | "@test", 45 | "@build-phar", 46 | "@fix-cs" 47 | ], 48 | "test": "phpunit", 49 | "build-phar": "./tools/build-phar.sh", 50 | "fix-cs": [ 51 | "phpcbf --standard=PSR12 ./src ./tests", 52 | "php-cs-fixer fix --rules=@PSR12 ./src", 53 | "php-cs-fixer fix --rules=@PSR12 ./tests" 54 | ], 55 | "check-version": "./tools/check-version.sh" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | This directory contains executable CRUD examples for Akamai API using the EdgeGrid PHP Client library. API calls used in these examples are available to all users. But, if you find one of the write examples doesn't work for you, talk with your account's admin about your privilege level. 4 | 5 | ## Run 6 | 7 | To run any of the files: 8 | 9 | 1. Provide the section header for the set of credentials you'd like to use. The default is `default`. 10 | 2. For update and delete operations, replace the dummy `credentialId` with your valid `credentialId`. 11 | 12 | > **Important:** Don't use the credentials you're actively using when running the update (inactivation) and delete operations. Otherwise, you'll block your access to the Akamai APIs. 13 | 14 | 3. Open a Terminal or shell instance and run the .php file. 15 | 16 | ``` 17 | $ php examples/.php 18 | ``` 19 | 20 | ## Sample files 21 | 22 | The example in each file contains a call to one of the Identity and Access Management (IAM) API endpoints. See the [IAM API reference](https://techdocs.akamai.com/iam-api/reference/api) doc for more information on each of the calls used. 23 | 24 | | Operation | Method | Endpoint | 25 | | --- | --- | --- | 26 | | [List your API client credentials.](/examples/get-credentials.php) | `GET` | `/identity-management/v3/api-clients/self/credentials` | 27 | | [Create new API client credentials.](/examples/create-credentials.php)
This is a *quick* client and grants you the default permissions associated with your account. | `POST` | `/identity-management/v3/api-clients/self/credentials` | 28 | | [Update your credentials by ID.](/examples/update-credentials.php) | `PUT` | `/identity-management/v3/api-clients/self/credentials/{credentialId}` | 29 | | [Delete your credentials by ID.](/examples/delete-credentials.php) | `DELETE` | `/identity-management/v3/api-clients/self/credentials/{credentialId}` | 30 | 31 | Suggested chained call order: 32 | 33 | 1. Get credentials to see your base information. 34 | 2. Create a client to create a new set of credentials. 35 | 3. Update credentials to inactivate the newly created set from step 2. 36 | 4. Delete a client to delete the inactivated credentials. 37 | 5. Get credentials to verify if they're gone (the status will be `DELETED`). -------------------------------------------------------------------------------- /tests/SimpleLog.php: -------------------------------------------------------------------------------- 1 | send()` and `->sendAsync()`). 100 | * Added the CLI interface to the PHAR release file ([docs](https://github.com/akamai-open/AkamaiOPEN-edgegrid-php#command-line-interface)). 101 | * Moved away from using `\Exception` to a more appropriate and package-specific exceptions. 102 | * Added a custom User-Agent. 103 | * Fixed an issue with string query arguments. 104 | * Enabled showing a request body when using the verbose handler. 105 | 106 | ## 0.3.0 (21 Jul, 2015) 107 | 108 | * Moved to using `GuzzleHttp\Middleware`. 109 | * Added `Authentication`, `Verbose`, and `Debug` middleware handlers. 110 | 111 | ## 0.2.1 (16 Jul, 2015) 112 | 113 | * Added PSR-3 Logging (defaults to monolog/monolog). 114 | * Added the `\Akamai\Open\EdgeGrid\Authentication::createFromEdgeRcFile()` method. 115 | * Fixed bugs. 116 | 117 | ## 0.2.0 (16 Jul, 2015) 118 | 119 | * Refactored the Authentication Signer out of the client for easier re-use. 120 | 121 | ## 0.1.0 (13 Jul, 2015) 122 | 123 | * Initial release. 124 | -------------------------------------------------------------------------------- /src/Handler/Authentication.php: -------------------------------------------------------------------------------- 1 | signer = $auth; 43 | if ($this->signer === null) { 44 | $this->signer = new Signer(); 45 | } 46 | } 47 | 48 | /** 49 | * Handler magic invoker 50 | * 51 | * @param callable $handler The next handler in the stack 52 | * @return \Closure 53 | * @throws \Akamai\Open\EdgeGrid\Exception\HandlerException 54 | */ 55 | public function __invoke(callable $handler) 56 | { 57 | return function ( 58 | RequestInterface $request, 59 | array $config 60 | ) use ($handler) { 61 | if ( 62 | $request->getUri()->getScheme() !== 'https' || 63 | strpos($request->getUri()->getHost(), 'akamaiapis.net') === false 64 | ) { 65 | return $handler($request, $config); 66 | } 67 | 68 | if (!$this->signer) { 69 | throw new HandlerException('Signer not set, make sure to call setSigner first'); 70 | } 71 | 72 | $request->getBody()->rewind(); 73 | 74 | $this->signer->setHttpMethod($request->getMethod()) 75 | ->setHost($request->getUri()->getHost()) 76 | ->setPath($request->getUri()->getPath()) 77 | ->setQuery($request->getUri()->getQuery()) 78 | ->setHeaders($request->getHeaders()) 79 | ->setBody($request->getBody()->getContents()); 80 | 81 | $request = $request->withHeader('Authorization', $this->signer->createAuthHeader()); 82 | 83 | return $handler($request, $config); 84 | }; 85 | } 86 | 87 | /** 88 | * Proxy to the underlying signer object 89 | * 90 | * @param $method 91 | * @param $args 92 | * @return mixed 93 | * @throws \Akamai\Open\EdgeGrid\Exception\HandlerException 94 | */ 95 | public function __call($method, $args) 96 | { 97 | if ($this->signer === null) { 98 | throw new HandlerException('Signer not set, make sure to call setSigner first'); 99 | } 100 | 101 | $return = call_user_func_array([$this->signer, $method], $args); 102 | if ($return == $this->signer) { 103 | return $this; 104 | } 105 | 106 | return $return; 107 | } 108 | 109 | /** 110 | * Create Handler using an .edgerc file 111 | * 112 | * Automatically create a valid authentication handler using 113 | * an .edgerc file 114 | * 115 | * @param string $section 116 | * @param null $file 117 | * 118 | * @return static 119 | */ 120 | public static function createFromEdgeRcFile($section = 'default', $file = null) 121 | { 122 | $signer = Signer::createFromEdgeRcFile($section, $file); 123 | $auth = new static(); 124 | $auth->setSigner($signer); 125 | 126 | return $auth; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/Handler/Debug.php: -------------------------------------------------------------------------------- 1 | [ 15 | "This indicates a problem with authorization.\n", 16 | "Please ensure that the credentials you created for this script\n", 17 | "have the necessary permissions in the Luna portal.\n", 18 | ], 19 | 400 => [ 20 | 'This indicates a problem with authentication or headers.', 21 | 'Please ensure that the .edgerc file is formatted correctly.', 22 | 'If you still have issues, please use gen_edgerc.php to generate the credentials', 23 | ], 24 | 401 => 400, 25 | 404 => [ 26 | "This means that the page does not exist as requested.\n", 27 | "Please ensure that the URL you're calling is correctly formatted\n", 28 | "or look at other examples to make sure yours matches.\n", 29 | ] 30 | ]; 31 | 32 | protected $fp; 33 | 34 | /** 35 | * Debug constructor. 36 | * 37 | * This method accepts a stream resource or a valid stream URL 38 | * (including file paths). If none is passed in stderr is used. 39 | * 40 | * @param resource|null $resource 41 | * @throws \Akamai\Open\EdgeGrid\Exception\HandlerException\IOException 42 | */ 43 | public function __construct($resource = null) 44 | { 45 | $fp = $resource; 46 | 47 | if (!is_resource($fp) && $fp !== null) { 48 | $fp = @fopen($fp, 'ab+'); 49 | if (!$fp) { 50 | throw new IOException('Unable to use resource: ' . (string) $resource); 51 | } 52 | } 53 | 54 | if ($fp === null) { 55 | $fp = fopen('php://stderr', 'ab'); 56 | } 57 | 58 | $this->fp = $fp; 59 | } 60 | 61 | /** 62 | * Handle the request/response 63 | * 64 | * @param callable $handler 65 | * @return \Closure 66 | */ 67 | public function __invoke(callable $handler) 68 | { 69 | $colors = [ 70 | 'red' => '', 71 | 'yellow' => '', 72 | 'cyan' => '', 73 | 'reset' => '', 74 | ]; 75 | 76 | if (PHP_SAPI === 'cli') { 77 | $colors = [ 78 | 'red' => "\x1b[31;01m", 79 | 'yellow' => "\x1b[33;01m", 80 | 'cyan' => "\x1b[36;01m", 81 | 'reset' => "\x1b[39;49;00m", 82 | ]; 83 | } 84 | 85 | return function (\Psr\Http\Message\RequestInterface $request, array $config) use ($handler, $colors) { 86 | return $handler($request, $config)->then( 87 | function (\Psr\Http\Message\ResponseInterface $response) use ($colors, $request) { 88 | $statusCode = $response->getStatusCode(); 89 | if ($statusCode > 399 && $statusCode < 600) { 90 | $body = trim($response->getBody()); 91 | $result = json_decode($body); 92 | if ($result !== null) { 93 | if (isset($result->detail)) { 94 | $detail = $result->detail; 95 | } else { 96 | $detail = json_encode($result, JSON_PRETTY_PRINT); 97 | } 98 | } else { 99 | $detail = (!empty(trim($body))) ? $body : 'No response body returned'; 100 | } 101 | 102 | $out = []; 103 | $out[] = "{$colors['red']}===> [ERROR] Call to %s failed with a %s result"; 104 | 105 | if (isset(self::$messages[$statusCode])) { 106 | $message = self::$messages[$statusCode]; 107 | if (is_int($message) && isset(self::$messages[$message])) { 108 | $message = self::$messages[$message]; 109 | } 110 | 111 | foreach ($message as $line) { 112 | $out[] = '===> [ERROR] ' . $line; 113 | } 114 | } 115 | 116 | $out[] = '===> [ERROR] Problem details:'; 117 | $out[] = $detail; 118 | 119 | $out = sprintf( 120 | implode("\n", $out), 121 | $request->getUri()->getPath(), 122 | $response->getStatusCode() . ' ' . $response->getReasonPhrase(), 123 | $detail 124 | ); 125 | 126 | fwrite($this->fp, $out); 127 | fwrite($this->fp, "{$colors['reset']}\n"); 128 | } 129 | 130 | return $response; 131 | }, 132 | function (\Exception $reason) { 133 | return new \GuzzleHttp\Promise\RejectedPromise($reason); 134 | } 135 | ); 136 | }; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /tests/Handler/AuthenticationTest.php: -------------------------------------------------------------------------------- 1 | prophet = new \Prophecy\Prophet(); 14 | } 15 | 16 | protected function tearDown(): void 17 | { 18 | $this->prophet->checkPredictions(); 19 | parent::tearDown(); 20 | } 21 | 22 | /** 23 | * @dataProvider createFromEdgeRcProvider 24 | * @param $section 25 | * @param $file 26 | */ 27 | public function testCreateFromEdgeRc($section, $file) 28 | { 29 | $_SERVER['HOME'] = __DIR__ . '/../edgerc'; 30 | 31 | $authentication = \Akamai\Open\EdgeGrid\Handler\Authentication::createFromEdgeRcFile($section, $file); 32 | $this->assertInstanceOf(\Akamai\Open\EdgeGrid\Handler\Authentication::class, $authentication); 33 | } 34 | 35 | /** 36 | * @param $name 37 | * @param $options 38 | * @param $request 39 | * @param $result 40 | * @dataProvider makeAuthHeaderProvider 41 | */ 42 | public function testMakeAuthHeader($name, $options, $request, $result) 43 | { 44 | $container = []; 45 | $handler = $this->getMockHandler([new Response(200)], $container); 46 | 47 | $timestamp = $this->prophet->prophesize(\Akamai\Open\EdgeGrid\Authentication\Timestamp::class); 48 | $timestamp->__toString()->willReturn($options['timestamp']); 49 | $timestamp->isValid()->willReturn(true); 50 | $nonce = $this->prophet->prophesize(\Akamai\Open\EdgeGrid\Authentication\Nonce::class); 51 | $nonce->__toString()->willReturn($options['nonce']); 52 | 53 | $auth = new \Akamai\Open\EdgeGrid\Handler\Authentication(); 54 | $auth->setSigner(); 55 | $auth->setAuth($options['client_token'], $options['client_secret'], $options['access_token']); 56 | $auth->setMaxBodySize($options['max_body']); 57 | $auth->setTimestamp($timestamp->reveal()); 58 | $auth->setNonce($nonce->reveal()); 59 | 60 | if (isset($options['headers_to_sign'])) { 61 | $auth->setHeadersToSign($options['headers_to_sign']); 62 | } 63 | 64 | // Because of PSR-7 immutability the history handler has 65 | // to be the last one, otherwise it doesn't get the latest 66 | // instance of the Request. 67 | $handler->before('history', $auth, 'signer'); 68 | 69 | $client = new \GuzzleHttp\Client( 70 | array_merge($options, [ 71 | 'base_uri' => $options['base_url'], 72 | 'handler' => $handler, 73 | ]) 74 | ); 75 | 76 | $headers = array(); 77 | if (isset($request['headers'])) { 78 | array_walk_recursive($request['headers'], function ($value, $key) use (&$headers) { 79 | $headers[$key] = $value; 80 | }); 81 | } 82 | 83 | $client->request( 84 | $request['method'], 85 | $request['path'], 86 | [ 87 | 'headers' => $headers, 88 | 'body' => $request['data'], 89 | ] 90 | ); 91 | 92 | $this->assertEquals(1, count($container)); 93 | $request = $container[0]['request']; 94 | $headers = $request->getHeaders(); 95 | 96 | $this->assertArrayHasKey('Authorization', $headers); 97 | $this->assertEquals(1, count($headers['Authorization'])); 98 | $this->assertEquals($result, $headers['Authorization'][0]); 99 | } 100 | 101 | public function testHandlerChainingNotAuthenticated() 102 | { 103 | $container = []; 104 | $handler = $this->getMockHandler([new Response(200)], $container); 105 | 106 | $auth = new \Akamai\Open\EdgeGrid\Handler\Authentication(); 107 | 108 | // Because of PSR-7 immutability the history handler has 109 | // to be the last one, otherwise it doesn't get the latest 110 | // instance of the Request. 111 | $handler->before('history', $auth, 'signer'); 112 | 113 | $client = new \GuzzleHttp\Client([ 114 | 'base_uri' => 'http://example.org', 115 | 'handler' => $handler, 116 | ]); 117 | 118 | $client->get('/test'); 119 | 120 | $this->assertEquals(1, count($container)); 121 | $request = $container[0]['request']; 122 | $this->assertInstanceOf(\Psr\Http\Message\RequestInterface::class, $request); 123 | } 124 | 125 | public function testRequireSetSignerCall() 126 | { 127 | $this->expectException(\Akamai\Open\EdgeGrid\Exception\HandlerException::class); 128 | $this->expectExceptionMessage('Signer not set, make sure to call setSigner first'); 129 | 130 | $container = []; 131 | $handler = $this->getMockHandler([new Response(200)], $container); 132 | 133 | $auth = new \Akamai\Open\EdgeGrid\Handler\Authentication(); 134 | 135 | // Because of PSR-7 immutability the history handler has 136 | // to be the last one, otherwise it doesn't get the latest 137 | // instance of the Request. 138 | $handler->before('history', $auth, 'signer'); 139 | 140 | $client = new \GuzzleHttp\Client( 141 | [ 142 | 'handler' => $handler, 143 | ] 144 | ); 145 | 146 | $client->get('https://test-akamaiapis.net'); 147 | } 148 | 149 | public function testProxyNonFluent() 150 | { 151 | $auth = new \Akamai\Open\EdgeGrid\Handler\Authentication(); 152 | $auth->setSigner(new \Akamai\Open\EdgeGrid\Authentication()); 153 | $auth->setHost('test.host'); 154 | 155 | $this->assertEquals('test.host', $auth->getHost()); 156 | } 157 | 158 | public function testProxyNoSigner() 159 | { 160 | $this->expectException(\Exception::class); 161 | $this->expectExceptionMessage('Signer not set, make sure to call setSigner first'); 162 | 163 | $auth = new \Akamai\Open\EdgeGrid\Handler\Authentication(); 164 | $auth->setHost('test.host'); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/Handler/Verbose.php: -------------------------------------------------------------------------------- 1 | outputStream = $outputStream; 66 | $this->errorStream = $errorStream; 67 | } 68 | 69 | /** 70 | * Handle the request/response 71 | * 72 | * @param callable $handler 73 | * @return \Closure 74 | */ 75 | public function __invoke(callable $handler) 76 | { 77 | $colors = [ 78 | 'red' => '', 79 | 'yellow' => '', 80 | 'cyan' => '', 81 | 'reset' => '', 82 | ]; 83 | 84 | if (PHP_SAPI === 'cli') { 85 | $colors = [ 86 | 'red' => "\x1b[31;01m", 87 | 'yellow' => "\x1b[33;01m", 88 | 'cyan' => "\x1b[36;01m", 89 | 'reset' => "\x1b[39;49;00m", 90 | ]; 91 | } 92 | 93 | return function ( 94 | \Psr\Http\Message\RequestInterface $request, 95 | array $config 96 | ) use ( 97 | $handler, 98 | $colors 99 | ) { 100 | fwrite($this->outputStream, "{$colors['cyan']}===> [VERBOSE] Request: \n"); 101 | fwrite($this->outputStream, "{$colors['yellow']}" . $this->getBody($request)); 102 | fwrite($this->outputStream, "{$colors['reset']}\n"); 103 | 104 | return $handler($request, $config)->then( 105 | function (\Psr\Http\Message\ResponseInterface $response) use ($colors) { 106 | $statusCode = $response->getStatusCode(); 107 | if ($statusCode > 299 && $statusCode < 400) { 108 | fwrite($this->outputStream, "{$colors['cyan']}===> [VERBOSE] Redirected: "); 109 | fwrite($this->outputStream, $response->getHeader('Location')[0]); 110 | fwrite($this->outputStream, "{$colors['reset']}\n"); 111 | } else { 112 | $responseBody = $this->getBody($response); 113 | 114 | if ($statusCode > 399 && $statusCode < 600) { 115 | fwrite($this->errorStream, "{$colors['red']}===> [ERROR] An error occurred: \n"); 116 | fwrite($this->errorStream, "{$colors['yellow']}" . $responseBody); 117 | fwrite($this->errorStream, "{$colors['reset']}\n"); 118 | } else { 119 | fwrite($this->outputStream, "{$colors['cyan']}===> [VERBOSE] Response: \n"); 120 | fwrite($this->outputStream, "{$colors['yellow']}" . $responseBody); 121 | fwrite($this->outputStream, "{$colors['reset']}\n"); 122 | } 123 | } 124 | 125 | return $response; 126 | }, 127 | function (\Exception $reason) use ($colors) { 128 | fwrite($this->outputStream, "{$colors['red']}===> [ERROR] An error occurred: \n"); 129 | fwrite($this->outputStream, "{$colors['yellow']}"); 130 | 131 | $code = $reason->getCode(); 132 | if (!empty($code)) { 133 | $code .= ': '; 134 | } 135 | 136 | $message = $reason->getMessage(); 137 | 138 | fwrite($this->outputStream, ((!empty($code)) ? $code : '') . $message); 139 | 140 | $response = $reason instanceof \GuzzleHttp\Exception\RequestException 141 | ? $reason->getResponse() 142 | : false; 143 | 144 | if ($response instanceof \Psr\Http\Message\ResponseInterface) { 145 | $body = $response->getBody()->getContents(); 146 | if (!empty($body)) { 147 | fwrite($this->outputStream, "\n{$colors['yellow']}" . $body); 148 | } 149 | } 150 | 151 | fwrite($this->outputStream, "{$colors['reset']}\n"); 152 | 153 | return new \GuzzleHttp\Promise\RejectedPromise($reason); 154 | } 155 | ); 156 | }; 157 | } 158 | 159 | /** 160 | * Get response body 161 | * 162 | * @param \Psr\Http\Message\MessageInterface $message 163 | * 164 | * @return string 165 | */ 166 | protected function getBody(\Psr\Http\Message\MessageInterface $message) 167 | { 168 | $body = trim($message->getBody()); 169 | 170 | if ($message->getBody()->getSize() === 0 || empty($body)) { 171 | if ($message instanceof \Psr\Http\Message\ResponseInterface) { 172 | return 'No response body returned'; 173 | } 174 | return 'No request body sent'; 175 | } 176 | $result = json_decode($body); 177 | if ($result !== null) { 178 | return json_encode($result, JSON_PRETTY_PRINT); 179 | } 180 | 181 | return $body; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/Cli.php: -------------------------------------------------------------------------------- 1 | climate = new \League\CLImate\CLImate(); 21 | } 22 | 23 | /** 24 | * Execute the CLI 25 | */ 26 | public function run() 27 | { 28 | if ($this->parseArguments()) { 29 | $this->executeCommand(); 30 | } 31 | } 32 | 33 | /** 34 | * Parse incoming arguments 35 | * 36 | * @return bool|void 37 | */ 38 | protected function parseArguments() 39 | { 40 | $args = $this->getNamedArgs(); 41 | 42 | $this->climate->arguments->add($args); 43 | 44 | if ($_SERVER['argc'] === 1) { 45 | $this->help(); 46 | return false; 47 | } 48 | 49 | if ($this->climate->arguments->defined('help')) { 50 | $this->help(); 51 | return; 52 | } 53 | 54 | if ($this->climate->arguments->defined('version')) { 55 | echo $this->version(); 56 | return; 57 | } 58 | 59 | try { 60 | $this->climate->arguments->parse($_SERVER['argv']); 61 | 62 | $padding = count($args); 63 | foreach ($this->climate->arguments->toArray() as $arg) { 64 | if ($arg === null) { 65 | --$padding; 66 | } 67 | } 68 | $argSize = count($_SERVER['argv']) - $padding - 1; 69 | for ($i = 0; $i < $argSize; $i++) { 70 | $args['arg-' . $i] = []; 71 | } 72 | $this->climate->arguments->add($args); 73 | $this->climate->arguments->parse($_SERVER['argv']); 74 | } catch (\Exception $e) { 75 | } 76 | 77 | return true; 78 | } 79 | 80 | /** 81 | * Execute the HTTP request 82 | * 83 | * @return mixed|\Psr\Http\Message\ResponseInterface 84 | */ 85 | protected function executeCommand() 86 | { 87 | static $methods = [ 88 | 'HEAD', 89 | 'GET', 90 | 'POST', 91 | 'PUT', 92 | 'DELETE' 93 | ]; 94 | 95 | \Akamai\Open\EdgeGrid\Client::setDebug(true); 96 | \Akamai\Open\EdgeGrid\Client::setVerbose(true); 97 | 98 | $args = $this->climate->arguments->all(); 99 | $client = new Client(); 100 | 101 | if ($this->climate->arguments->defined('auth-type')) { 102 | $auth = $this->climate->arguments->get('auth'); 103 | if ( 104 | $this->climate->arguments->get('auth-type') === 'edgegrid' || 105 | (!$this->climate->arguments->defined('auth-type')) 106 | ) { 107 | $section = 'default'; 108 | if ($this->climate->arguments->defined('auth')) { 109 | $section = (substr($auth, -1) === ':') ? substr($auth, 0, -1) : $auth; 110 | } 111 | $client = Client::createFromEdgeRcFile($section); 112 | } 113 | 114 | if (in_array($this->climate->arguments->get('auth-type'), ['basic', 'digest'])) { 115 | if (!$this->climate->arguments->defined('auth') || $this->climate->arguments->get('auth') === null) { 116 | $this->help(); 117 | return; 118 | } 119 | 120 | $auth = [ 121 | $auth, 122 | null, 123 | $this->climate->arguments->get('auth-type') 124 | ]; 125 | 126 | if (strpos(':', $auth[0]) !== false) { 127 | list($auth[0], $auth[1]) = explode(':', $auth[0]); 128 | } 129 | 130 | $client = new Client(['auth' => $auth]); 131 | } 132 | } 133 | 134 | $method = 'GET'; 135 | $options = []; 136 | $body = []; 137 | 138 | foreach ($args as $arg) { 139 | $value = $arg->value(); 140 | if (empty($value) || is_bool($value) || $arg->longPrefix()) { 141 | continue; 142 | } 143 | 144 | if (in_array(strtoupper($value), $methods)) { 145 | $method = $arg->value(); 146 | continue; 147 | } 148 | 149 | if (!isset($url) && preg_match('@^(http(s?)://|:).*$@', trim($value))) { 150 | $url = $value; 151 | 152 | if ($url[0] === ':') { 153 | $url = substr($url, 1); 154 | } 155 | 156 | continue; 157 | } 158 | 159 | $matches = []; 160 | if (preg_match('/^(?.*?):=(?@?)(?.*?)$/', $value, $matches)) { 161 | if (!$value = $this->getArgValue($matches)) { 162 | return false; 163 | } 164 | 165 | $body[$matches['key']] = json_decode($value); 166 | continue; 167 | } 168 | 169 | if ( 170 | preg_match('/^(?
.*?):(?.*?)$/', $value, $matches) 171 | && !preg_match('@^http(s?)://@', $value) 172 | ) { 173 | $options['headers'][$matches['header']] = $matches['value']; 174 | continue; 175 | } 176 | 177 | if (preg_match('/^(?.*?)=(?@?)(?.*?)$/', $value, $matches)) { 178 | if (!$value = $this->getArgValue($matches)) { 179 | return false; 180 | } 181 | 182 | $body[$matches['key']] = $matches['value']; 183 | continue; 184 | } 185 | 186 | if (!isset($url)) { 187 | $url = $value; 188 | continue; 189 | } 190 | 191 | $this->help(); 192 | $this->climate->error('Unknown argument: ' . $value); 193 | 194 | return false; 195 | } 196 | 197 | $stdin = ''; 198 | $fp = fopen('php://stdin', 'rb'); 199 | if ($fp) { 200 | stream_set_blocking($fp, false); 201 | $stdin = fgets($fp); 202 | if (!empty(trim($stdin))) { 203 | while (!feof($fp)) { 204 | $stdin .= fgets($fp); 205 | } 206 | fclose($fp); 207 | } 208 | $stdin = rtrim($stdin); 209 | } 210 | 211 | if (!empty($stdin) && !empty($body)) { 212 | $this->help(); 213 | $this->climate->error( 214 | 'error: Request body (from stdin or a file) and request data (key=value) cannot be mixed.' 215 | ); 216 | return; 217 | } 218 | 219 | if (!empty($stdin)) { 220 | $body = $stdin; 221 | } 222 | 223 | if (count($body) && !$this->climate->arguments->defined('form')) { 224 | if (!isset($options['headers']['Content-Type'])) { 225 | $options['headers']['Content-Type'] = 'application/json'; 226 | } 227 | if (!isset($options['headers']['Accept'])) { 228 | $options['headers']['Accept'] = 'application/json'; 229 | } 230 | $options['body'] = (!is_string($body)) ? json_encode($body) : $body; 231 | } 232 | 233 | if (count($body) && $this->climate->arguments->defined('form')) { 234 | if (!isset($options['headers']['Content-Type'])) { 235 | $options['headers']['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'; 236 | } 237 | 238 | $options['body'] = (!is_string($body)) ? http_build_query($body, '', null, PHP_QUERY_RFC1738) : $body; 239 | } 240 | 241 | $options['allow_redirects'] = false; 242 | if ($this->climate->arguments->defined('follow')) { 243 | $options['allow_redirects'] = true; 244 | } 245 | 246 | return $client->request($method, $url, $options); 247 | } 248 | 249 | /** 250 | * Display CLI help 251 | */ 252 | public function help() 253 | { 254 | $arguments = new \League\CLImate\Argument\Manager(); 255 | $arguments->description('Akamai {OPEN} Edgegrid Auth for PHP Client (v' . Client::VERSION . ')'); 256 | $arguments->add($this->getNamedArgs()); 257 | $arguments->usage($this->climate, $_SERVER['argv']); 258 | } 259 | 260 | /** 261 | * Return the client version 262 | * 263 | * @return string 264 | */ 265 | public function version() 266 | { 267 | return Client::VERSION; 268 | } 269 | 270 | /** 271 | * Handle named arguments 272 | * 273 | * @return array 274 | */ 275 | protected function getNamedArgs() 276 | { 277 | $args = [ 278 | 'help' => [ 279 | 'longPrefix' => 'help', 280 | 'prefix' => 'h', 281 | 'description' => 'Show this help output', 282 | 'noValue' => true 283 | ], 284 | 'auth-type' => [ 285 | 'longPrefix' => 'auth-type', 286 | 'prefix' => 'A', 287 | 'description' => '{basic, digest, edgegrid}' 288 | ], 289 | 'auth' => [ 290 | 'longPrefix' => 'auth', 291 | 'prefix' => 'a', 292 | 'description' => '.edgerc section name, or user[:password]' 293 | ], 294 | 'json' => [ 295 | 'longPrefix' => 'json', 296 | 'prefix' => 'j', 297 | 'description' => '(default) Data items from the command line are serialized as a JSON object.', 298 | 'noValue' => true 299 | ], 300 | 'follow' => [ 301 | 'longPrefix' => 'follow', 302 | 'description' => 'Set this flag if redirects are allowed', 303 | 'noValue' => true 304 | ], 305 | 'form' => [ 306 | 'longPrefix' => 'form', 307 | 'prefix' => 'f', 308 | 'description' => 'Data items from the command line are serialized as form fields', 309 | 'noValue' => true 310 | ], 311 | 'version' => [ 312 | 'longPrefix' => 'version', 313 | 'description' => 'Show version', 314 | 'noValue' => true 315 | ], 316 | 'METHOD' => [ 317 | 'description' => 'HTTP Method (default: GET)' 318 | ], 319 | 'URL' => [ 320 | 'required' => true, 321 | ] 322 | ]; 323 | 324 | return $args; 325 | } 326 | 327 | /** 328 | * Get argument values 329 | * 330 | * @param $matches 331 | * @return bool|string 332 | */ 333 | protected function getArgValue($matches) 334 | { 335 | $value = $matches['value']; 336 | if (!empty($matches['file'])) { 337 | if (!file_exists($matches['value']) || !is_readable($matches['value'])) { 338 | $this->climate->error('Unable to read input file: ' . $matches['value']); 339 | return false; 340 | } 341 | $value = file_get_contents($matches['value']); 342 | } 343 | 344 | return $value; 345 | } 346 | } 347 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # akamai-open/edgegrid-client 2 | 3 | [Akamai EdgeGrid Authentication](https://techdocs.akamai.com/developer/docs/set-up-authentication-credentials) for PHP. 4 | 5 | This library requires PHP 8+ and implements the Akamai EdgeGrid Authentication scheme on top of [Guzzle](https://github.com/guzzle/guzzle) as both a drop-in replacement client and middleware. 6 | 7 | ## Installation 8 | 9 | To install, use [`composer`](http://getcomposer.org): 10 | 11 | ```sh 12 | $ composer require akamai-open/edgegrid-client 13 | ``` 14 | 15 | ### Alternative installation method 16 | 17 | Download the PHAR file from the [releases](https://github.com/akamai/AkamaiOPEN-edgegrid-php/releases) page and include it inside your code: 18 | 19 | ```php 20 | include 'akamai-open-edgegrid-auth.phar'; 21 | 22 | // Library is ready to use 23 | ``` 24 | 25 | ## Authentication 26 | 27 | You can obtain the authentication credentials through an API client. Requests to the API are marked with a timestamp and a signature and are executed immediately. 28 | 29 | 1. [Create authentication credentials](https://techdocs.akamai.com/developer/docs/set-up-authentication-credentials). 30 | 31 | 2. Place your credentials in an EdgeGrid file `~/.edgerc`, in the `[default]` section. 32 | 33 | ``` 34 | [default] 35 | client_secret = C113nt53KR3TN6N90yVuAgICxIRwsObLi0E67/N8eRN= 36 | host = akab-h05tnam3wl42son7nktnlnnx-kbob3i3v.luna.akamaiapis.net 37 | access_token = akab-acc35t0k3nodujqunph3w7hzp7-gtm6ij 38 | client_token = akab-c113ntt0k3n4qtari252bfxxbsl-yvsdj 39 | ``` 40 | 41 | 3. Use your local `.edgerc` by providing credentials' section header and the path to your resource file. You can call your `.edgerc` file using the `\Akamai\Open\EdgeGrid\Client::createFromEdgeRcFile()` method. 42 | 43 | The location of your `.edgerc` file is optional, as it defaults to `~/.edgerc`. 44 | 45 | ```php 46 | $client = \Akamai\Open\EdgeGrid\Client::createFromEdgeRcFile('{credentials_section_name}', '{path/to/.edgerc}'); 47 | 48 | // Use $client just as you would \Guzzle\Http\Client 49 | $response = $client->get('/identity-management/v3/user-profile'); 50 | ``` 51 | 52 | Alternatively, you can hard code your credentials by passing the credential values to the `\Akamai\Open\EdgeGrid\Client->setAuth()` method. 53 | 54 | ```php 55 | $client = new Akamai\Open\EdgeGrid\Client([ 56 | 'base_uri' => 'https://akab-h05tnam3wl42son7nktnlnnx-kbob3i3v.luna.akamaiapis.net' 57 | ]); 58 | 59 | $client_token = 'akab-c113ntt0k3n4qtari252bfxxbsl-yvsdj'; 60 | $client_secret = 'C113nt53KR3TN6N90yVuAgICxIRwsObLi0E67/N8eRN='; 61 | $access_token = 'akab-acc35t0k3nodujqunph3w7hzp7-gtm6ij'; 62 | 63 | $client->setAuth($client_token, $client_secret, $access_token); 64 | 65 | // use $client just as you would \Guzzle\Http\Client 66 | $response = $client->get('/identity-management/v3/user-profile'); 67 | ``` 68 | 69 | ## Use 70 | 71 | The `Akamai\Open\EdgeGrid\Client` extends `\GuzzleHttp\Client` as a drop-in replacement. It works transparently to sign API requests without changing other ways you use Guzzle. 72 | 73 | Include the autoloader to import all the required classes. 74 | 75 | Provide your credentials section header and appropriate endpoint information. 76 | 77 | ```php 78 | get('/identity-management/v3/user-profile'); 85 | 86 | echo $response->getBody(); 87 | ``` 88 | 89 | ### Query string parameters 90 | 91 | You can pass the query parameters in the url after a question mark ("?") at the end of the main URL path. 92 | 93 | ```php 94 | 'application/json', 101 | ]; 102 | 103 | $response = $client->get('/identity-management/v3/user-profile?authGrants=true¬ifications=true&actions=true', $headers); 104 | 105 | echo $response->getBody(); 106 | ``` 107 | 108 | You can also pass the query parameters using a `query` request option. See the [Guzzle documentation](https://docs.guzzlephp.org/en/stable/quickstart.html#query-string-parameters) for details. 109 | 110 | ### Headers 111 | 112 | You can enter request headers as a PSR-7 request object. 113 | 114 | > **Note:** You don't need to include the `Content-Type` and `Content-Length` headers. The authentication layer adds these values. 115 | 116 | ```php 117 | 'application/json' 124 | ]; 125 | 126 | $response = $client->get('/identity-management/v3/user-profile', $headers); 127 | 128 | echo $response->getBody(); 129 | ``` 130 | 131 | See the [Guzzle documentation](https://docs.guzzlephp.org/en/stable/request-options.html#headers) for details on defining headers as a `headers` request option. 132 | 133 | ### Body data 134 | 135 | Use the [Guzzle syntax](https://docs.guzzlephp.org/en/stable/request-options.html#json) to pass simple JSON data as a `json` option in the request. 136 | 137 | ```php 138 | 'application/json', 145 | 'Accept' => 'application/json' 146 | ]; 147 | 148 | $body = [ 149 | 'json' => [ 150 | 'contractType' => 'Billing', 151 | 'country' => 'USA', 152 | 'firstName' => 'John', 153 | 'lastName' => 'Smith', 154 | 'phone' => '3456788765', 155 | 'preferredLanguage' => 'English', 156 | 'sessionTimeOut' => '30', 157 | 'timeZone' => 'GMT' 158 | ] 159 | ]; 160 | 161 | $response = $client->put('/identity-management/v3/user-profile/basic-info', $body, $headers); 162 | 163 | echo $response->getBody(); 164 | ``` 165 | 166 | ## Command line interface 167 | 168 | This library provides a command line interface (CLI) with a limited set of capabilities that mimic [httpie](http://httpie.org). 169 | 170 | ### Install 171 | 172 | Install the CLI with a composer `vendor/bin/http` or execute the PHAR file. 173 | 174 | ```sh 175 | # Composer installed 176 | $ ./vendor/bin/http --help 177 | 178 | # For Windows 179 | > php ./vendor/bin/http --help 180 | 181 | # PHAR download 182 | php akamai-open-edgegrid-client.phar --help 183 | ``` 184 | 185 | ### Arguments 186 | 187 | You can set authentication and specify an HTTP method (case insensitive), its headers, and any JSON body fields. 188 | 189 | > **Note:** Our CLI shows all HTTP and body data. JSON is formatted. 190 | 191 | |Argument|Description| 192 | |---|---| 193 | |`--auth-type={edgegrid,basic,digest}`|Set the authentication type. The default is `none`.| 194 | |`--auth user:` or `--a user:`|Set the `.edgerc` section to use. Unlike `httpie-edgegrid`, the colon (`:`) is optional.| 195 | |`Header-Name:value`|Headers and values are colon (`:`) separated.| 196 | |`jsonKey=value`|Sends `{"jsonKey": "value"}` in the `POST` or `PUT` body. This will also automatically set the `Content-Type` and `Accept` headers to `application/json`.| 197 | |`jsonKey:=[1,2,3]`|Allows you to specify raw JSON data, sending `{"jsonKey": [1, 2, 3]}` in the body.| 198 | 199 | ### Limitations 200 | 201 | * You can't send `multipart/mime` (file upload) data. 202 | * Client certs aren't supported. 203 | * Server certs can't be verified. 204 | * Output can't be customized. 205 | * Responses aren't syntax highlighted. 206 | 207 | ## Guzzle middleware 208 | 209 | This package provides three different middleware handlers you can add transparently when using the `Client`, to a standard `\GuzzleHttp\Client` or as a handler. 210 | 211 | * The `\Akamai\Open\EdgeGrid\Handler\Authentication` for transparent API request signing. 212 | * The `\Akamai\Open\EdgeGrid\Handler\Verbose` for output (or log) responses. 213 | * The `\Akamai\Open\EdgeGrid\Handler\Debug` for output (or log) errors. 214 | 215 | ### Transparent use 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 |
HandlerCall
AuthenticationClient->setAuthentication() or pass in an instance of \Akamai\EdgeGrid\Authentication to Client->__construct().
VerboseClient->setInstanceVerbose() or Client::setVerbose() passing in on of true|resource|[resource output, resource error]. The default is [STDOUT, STDERR].
DebugClient->setInstanceDebug(), Client::setDebug(), or set the debug config option with true|resource. The default is STDERR.
239 | 240 | ### Middleware 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 269 | 270 | 271 | 272 | 284 | 285 | 286 | 287 | 299 | 300 | 301 |
HandlerExample
Authentication 253 |
254 |     // Create the Authentication Handler
255 |     $auth = \Akamai\Open\EdgeGrid\Handler\Authentication::createFromEdgeRcFile();
256 |     // Or hard code your credentials
257 |     $auth = new \Akamai\Open\EdgeGrid\Handler\Authentication;
258 |     $auth->setAuth($client_token, $client_secret, $access_token);
259 |     // Create the handler stack
260 |     $handlerStack = \GuzzleHttp\HandlerStack::create();
261 |     // Add the Auth handler to the stack
262 |     $handlerStack->push($auth);
263 |     // Add the handler to a regular \GuzzleHttp\Client
264 |     $guzzle = new \GuzzleHttp\Client([
265 |     "handler" => $handlerStack
266 |     ]);
267 | 
268 |
Verbose 273 |
274 |     // Create the handler stack
275 |     $handlerStack = HandlerStack::create();
276 |     // Add the Auth handler to the stack
277 |     $handlerStack->push(new \Akamai\Open\EdgeGrid\Handler\Verbose());
278 |     // Add the handler to a regular \GuzzleHttp\Client
279 |     $guzzle = new \GuzzleHttp\Client([
280 |         "handler" => $handlerStack
281 |     ]);
282 | 
283 |
Debug 288 |
289 |     // Create the handler stack
290 |     $handlerStack = HandlerStack::create();
291 |     // Add the Auth handler to the stack
292 |     $handlerStack->push(new \Akamai\Open\EdgeGrid\Handler\Debug());
293 |     // Add the handler to a regular \GuzzleHttp\Client
294 |     $guzzle = new \GuzzleHttp\Client([
295 |         "handler" => $handlerStack
296 |     ]);
297 | 
298 |
302 | 303 | ## License 304 | 305 | Copyright © 2025 Akamai Technologies, Inc. All rights reserved 306 | 307 | Licensed under the Apache License, Version 2.0 (the "License"); 308 | you may not use these files except in compliance with the License. 309 | You may obtain a copy of the License at . 310 | 311 | Unless required by applicable law or agreed to in writing, software 312 | distributed under the License is distributed on an "AS IS" BASIS, 313 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 314 | See the License for the specific language governing permissions and 315 | limitations under the License. 316 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright © 2025 Akamai Technologies, Inc. All rights reserved 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use these files except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /tests/testdata.json: -------------------------------------------------------------------------------- 1 | { 2 | "base_url": "https://akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net/", 3 | "access_token": "akab-access-token-xxx-xxxxxxxxxxxxxxxx", 4 | "client_token":"akab-client-token-xxx-xxxxxxxxxxxxxxxx", 5 | "client_secret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=", 6 | "max_body": 2048, 7 | "headers_to_sign": [ "X-Test1", "X-Test2", "X-Test3" ], 8 | "nonce": "nonce-xx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", 9 | "timestamp": "20140321T19:34:21+0000", 10 | "tests": [ 11 | { 12 | "testName": "simple GET", 13 | "request": { 14 | "method": "GET", 15 | "path": "/", 16 | "headers": { 17 | "Host": "akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net" 18 | } 19 | }, 20 | "expectedAuthorization": "EG1-HMAC-SHA256 client_token=akab-client-token-xxx-xxxxxxxxxxxxxxxx;access_token=akab-access-token-xxx-xxxxxxxxxxxxxxxx;timestamp=20140321T19:34:21+0000;nonce=nonce-xx-xxxx-xxxx-xxxx-xxxxxxxxxxxx;signature=tL+y4hxyHxgWVD30X3pWnGKHcPzmrIF+LThiAOhMxYU=" 21 | }, 22 | { 23 | "testName": "GET with querystring", 24 | "request": { 25 | "method": "GET", 26 | "path": "/testapi/v1/t1?p1=1&p2=2", 27 | "headers": { 28 | "Host": "akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net" 29 | } 30 | }, 31 | "expectedAuthorization": "EG1-HMAC-SHA256 client_token=akab-client-token-xxx-xxxxxxxxxxxxxxxx;access_token=akab-access-token-xxx-xxxxxxxxxxxxxxxx;timestamp=20140321T19:34:21+0000;nonce=nonce-xx-xxxx-xxxx-xxxx-xxxxxxxxxxxx;signature=hKDH1UlnQySSHjvIcZpDMbQHihTQ0XyVAKZaApabdeA=" 32 | }, 33 | { 34 | "testName": "POST inside limit", 35 | "request": { 36 | "method": "POST", 37 | "path": "/testapi/v1/t3", 38 | "data": "datadatadatadatadatadatadatadata", 39 | "headers": { 40 | "Host": "akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net" 41 | } 42 | }, 43 | "expectedAuthorization": "EG1-HMAC-SHA256 client_token=akab-client-token-xxx-xxxxxxxxxxxxxxxx;access_token=akab-access-token-xxx-xxxxxxxxxxxxxxxx;timestamp=20140321T19:34:21+0000;nonce=nonce-xx-xxxx-xxxx-xxxx-xxxxxxxxxxxx;signature=hXm4iCxtpN22m4cbZb4lVLW5rhX8Ca82vCFqXzSTPe4=" 44 | }, 45 | { 46 | "testName": "POST too large", 47 | "request": { 48 | "method": "POST", 49 | "path": "/testapi/v1/t3", 50 | "data": "ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", 51 | "headers": { 52 | "Host": "akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net" 53 | } 54 | }, 55 | "expectedAuthorization": "EG1-HMAC-SHA256 client_token=akab-client-token-xxx-xxxxxxxxxxxxxxxx;access_token=akab-access-token-xxx-xxxxxxxxxxxxxxxx;timestamp=20140321T19:34:21+0000;nonce=nonce-xx-xxxx-xxxx-xxxx-xxxxxxxxxxxx;signature=6Q6PiTipLae6n4GsSIDTCJ54bEbHUBp+4MUXrbQCBoY=" 56 | }, 57 | { 58 | "testName": "POST length equals max_body", 59 | "request": { 60 | "method": "POST", 61 | "path": "/testapi/v1/t3", 62 | "data": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", 63 | "headers": { 64 | "Host": "akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net" 65 | } 66 | }, 67 | "expectedAuthorization": "EG1-HMAC-SHA256 client_token=akab-client-token-xxx-xxxxxxxxxxxxxxxx;access_token=akab-access-token-xxx-xxxxxxxxxxxxxxxx;timestamp=20140321T19:34:21+0000;nonce=nonce-xx-xxxx-xxxx-xxxx-xxxxxxxxxxxx;signature=6Q6PiTipLae6n4GsSIDTCJ54bEbHUBp+4MUXrbQCBoY=" 68 | }, 69 | { 70 | "testName": "POST empty body", 71 | "request": { 72 | "method": "POST", 73 | "path": "/testapi/v1/t6", 74 | "data": "", 75 | "headers": { 76 | "Host": "akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net" 77 | } 78 | }, 79 | "expectedAuthorization": "EG1-HMAC-SHA256 client_token=akab-client-token-xxx-xxxxxxxxxxxxxxxx;access_token=akab-access-token-xxx-xxxxxxxxxxxxxxxx;timestamp=20140321T19:34:21+0000;nonce=nonce-xx-xxxx-xxxx-xxxx-xxxxxxxxxxxx;signature=1gEDxeQGD5GovIkJJGcBaKnZ+VaPtrc4qBUHixjsPCQ=" 80 | }, 81 | { 82 | "testName": "POST nil body", 83 | "request": { 84 | "method": "POST", 85 | "path": "/testapi/v1/t6", 86 | "data": null, 87 | "headers": { 88 | "Host": "akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net" 89 | } 90 | }, 91 | "expectedAuthorization": "EG1-HMAC-SHA256 client_token=akab-client-token-xxx-xxxxxxxxxxxxxxxx;access_token=akab-access-token-xxx-xxxxxxxxxxxxxxxx;timestamp=20140321T19:34:21+0000;nonce=nonce-xx-xxxx-xxxx-xxxx-xxxxxxxxxxxx;signature=1gEDxeQGD5GovIkJJGcBaKnZ+VaPtrc4qBUHixjsPCQ=" 92 | }, 93 | { 94 | "testName": "Simple header signing with GET", 95 | "request": { 96 | "method": "GET", 97 | "path": "/testapi/v1/t4", 98 | "headers": { 99 | "Host": "akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net", 100 | "X-Test1": "test-simple-header" 101 | } 102 | }, 103 | "expectedAuthorization": "EG1-HMAC-SHA256 client_token=akab-client-token-xxx-xxxxxxxxxxxxxxxx;access_token=akab-access-token-xxx-xxxxxxxxxxxxxxxx;timestamp=20140321T19:34:21+0000;nonce=nonce-xx-xxxx-xxxx-xxxx-xxxxxxxxxxxx;signature=8F9AybcRw+PLxnvT+H0JRkjROrrUgsxJTnRXMzqvcwY=" 104 | }, 105 | { 106 | "testName": "Header containing spaces", 107 | "request": { 108 | "method": "GET", 109 | "path": "/testapi/v1/t4", 110 | "headers": { 111 | "Host": "akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net", 112 | "X-Test1": "\" test-header-with-spaces \"" 113 | } 114 | }, 115 | "expectedAuthorization": "EG1-HMAC-SHA256 client_token=akab-client-token-xxx-xxxxxxxxxxxxxxxx;access_token=akab-access-token-xxx-xxxxxxxxxxxxxxxx;timestamp=20140321T19:34:21+0000;nonce=nonce-xx-xxxx-xxxx-xxxx-xxxxxxxxxxxx;signature=ucq2AbjCNtobHfCTuS38fdkl5UDdWHZhQX46fYR8CqI=" 116 | }, 117 | { 118 | "testName": "Header with leading and interior spaces", 119 | "request": { 120 | "method": "GET", 121 | "path": "/testapi/v1/t4", 122 | "headers": { 123 | "Host": "akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net", 124 | "X-Test1": " first-thing second-thing" 125 | } 126 | }, 127 | "expectedAuthorization": "EG1-HMAC-SHA256 client_token=akab-client-token-xxx-xxxxxxxxxxxxxxxx;access_token=akab-access-token-xxx-xxxxxxxxxxxxxxxx;timestamp=20140321T19:34:21+0000;nonce=nonce-xx-xxxx-xxxx-xxxx-xxxxxxxxxxxx;signature=WtnneL539UadAAOJwnsXvPqT4Kt6z7HMgBEwAFpt3+c=" 128 | }, 129 | { 130 | "testName": "Headers out of order", 131 | "request": { 132 | "method": "GET", 133 | "path": "/testapi/v1/t4", 134 | "headers": { 135 | "Host": "akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net", 136 | "X-Test2": "t2", 137 | "X-Test1": "t1", 138 | "X-Test3": "t3" 139 | } 140 | }, 141 | "expectedAuthorization": "EG1-HMAC-SHA256 client_token=akab-client-token-xxx-xxxxxxxxxxxxxxxx;access_token=akab-access-token-xxx-xxxxxxxxxxxxxxxx;timestamp=20140321T19:34:21+0000;nonce=nonce-xx-xxxx-xxxx-xxxx-xxxxxxxxxxxx;signature=Wus73Nx8jOYM+kkBFF2q8D1EATRIMr0WLWwpLBgkBqY=" 142 | }, 143 | { 144 | "testName": "Extra header", 145 | "request": { 146 | "method": "GET", 147 | "path": "/testapi/v1/t5", 148 | "headers": { 149 | "Host": "akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net", 150 | "X-Test2": "t2", 151 | "X-Test1": "t1", 152 | "X-Test3": "t3", 153 | "X-Extra": "this won't be included" 154 | } 155 | }, 156 | "expectedAuthorization": "EG1-HMAC-SHA256 client_token=akab-client-token-xxx-xxxxxxxxxxxxxxxx;access_token=akab-access-token-xxx-xxxxxxxxxxxxxxxx;timestamp=20140321T19:34:21+0000;nonce=nonce-xx-xxxx-xxxx-xxxx-xxxxxxxxxxxx;signature=Knd/jc0A5Ghhizjayr0AUUvl2MZjBpS3FDSzvtq4Ixc=" 157 | }, 158 | { 159 | "testName": "PUT test", 160 | "request": { 161 | "method": "PUT", 162 | "path": "/testapi/v1/t6", 163 | "data": "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP" 164 | }, 165 | "expectedAuthorization": "EG1-HMAC-SHA256 client_token=akab-client-token-xxx-xxxxxxxxxxxxxxxx;access_token=akab-access-token-xxx-xxxxxxxxxxxxxxxx;timestamp=20140321T19:34:21+0000;nonce=nonce-xx-xxxx-xxxx-xxxx-xxxxxxxxxxxx;signature=GNBWEYSEWOLtu+7dD52da2C39aX/Jchpon3K/AmBqBU=" 166 | }, 167 | { 168 | "testName": "GET with query args as option", 169 | "request": { 170 | "method": "GET", 171 | "path": "/testapi/v1/t6", 172 | "query": { 173 | "test": "arg" 174 | } 175 | }, 176 | "expectedAuthorization": "EG1-HMAC-SHA256 client_token=akab-client-token-xxx-xxxxxxxxxxxxxxxx;access_token=akab-access-token-xxx-xxxxxxxxxxxxxxxx;timestamp=20140321T19:34:21+0000;nonce=nonce-xx-xxxx-xxxx-xxxx-xxxxxxxxxxxx;signature=MlHog3cBr9VL+L+KUpVsdyAHmXaOunCdKplWy4UlohI=" 177 | } 178 | ] 179 | } 180 | -------------------------------------------------------------------------------- /tests/Handler/DebugTest.php: -------------------------------------------------------------------------------- 1 | getMockHandler([new Response(400, [], json_encode(['detail' => 'error info']))]); 18 | $client = new Client( 19 | [ 20 | 'base_uri' => 'http://example.org', 21 | 'handler' => $handler, 22 | ] 23 | ); 24 | $client->setAuth('test', 'test', 'test'); 25 | 26 | $fp = fopen('php://memory', 'ab+'); 27 | $client->setInstanceDebug($fp); 28 | 29 | $this->expectOutputString(''); 30 | 31 | try { 32 | $client->get('/error'); 33 | } catch (\Exception $e) { 34 | } 35 | 36 | $output = $this->readStreamData($fp); 37 | 38 | $expectedOutput = << [ERROR] Call to /error failed with a 400 Bad Request result 40 | ===> [ERROR] This indicates a problem with authentication or headers. 41 | ===> [ERROR] Please ensure that the .edgerc file is formatted correctly. 42 | ===> [ERROR] If you still have issues, please use gen_edgerc.php to generate the credentials 43 | ===> [ERROR] Problem details: 44 | error info 45 | 46 | EOF; 47 | 48 | $this->assertEquals($expectedOutput, $output); 49 | } 50 | 51 | public function testStaticDebug() 52 | { 53 | $handler = $this->getMockHandler([new Response(400, [], json_encode(['detail' => 'error info']))]); 54 | 55 | $client = new Client( 56 | [ 57 | 'base_uri' => 'http://example.org', 58 | 'handler' => $handler, 59 | ] 60 | ); 61 | $client->setAuth('test', 'test', 'test'); 62 | 63 | $fp = fopen('php://memory', 'ab+'); 64 | Client::setDebug($fp); 65 | 66 | $this->expectOutputString(''); 67 | 68 | try { 69 | $client->get('/error'); 70 | } catch (\Exception $e) { 71 | } 72 | 73 | $output = $this->readStreamData($fp); 74 | 75 | $expectedOutput = << [ERROR] Call to /error failed with a 400 Bad Request result 77 | ===> [ERROR] This indicates a problem with authentication or headers. 78 | ===> [ERROR] Please ensure that the .edgerc file is formatted correctly. 79 | ===> [ERROR] If you still have issues, please use gen_edgerc.php to generate the credentials 80 | ===> [ERROR] Problem details: 81 | error info 82 | 83 | EOF; 84 | 85 | $this->assertEquals($expectedOutput, $output); 86 | } 87 | 88 | public function testDebugOverride() 89 | { 90 | $handler = $this->getMockHandler([new Response(400, [], json_encode(['detail' => 'error string']))]); 91 | $client = new Client( 92 | [ 93 | 'base_uri' => 'http://example.org', 94 | 'handler' => $handler, 95 | ] 96 | ); 97 | 98 | $fp = fopen('php://memory', 'ab+'); 99 | Client::setDebug($fp); 100 | $client->setInstanceDebug(false); 101 | 102 | $this->expectOutputString(''); 103 | 104 | try { 105 | $client->get('/error'); 106 | } catch (\Exception $e) { 107 | } 108 | 109 | $output = $this->readStreamData($fp); 110 | 111 | $expectedOutput = ''; 112 | 113 | $this->assertEquals($expectedOutput, $output); 114 | } 115 | 116 | public function testInstanceOverrideStream() 117 | { 118 | $handler = $this->getMockHandler([new Response(400, [], json_encode(['detail' => 'error string']))]); 119 | $client = new Client( 120 | [ 121 | 'base_uri' => 'http://example.org', 122 | 'handler' => $handler, 123 | ] 124 | ); 125 | 126 | $fp = fopen('php://memory', 'ab+'); 127 | Client::setDebug($fp); 128 | $fp2 = fopen('php://memory', 'ab+'); 129 | $client->setInstanceDebug($fp2); 130 | 131 | $this->expectOutputString(''); 132 | 133 | try { 134 | $client->get('/error'); 135 | } catch (\Exception $e) { 136 | } 137 | 138 | $output = $this->readStreamData($fp); 139 | 140 | $expectedOutput = ''; 141 | $this->assertEquals($expectedOutput, $output); 142 | 143 | $output = $this->readStreamData($fp2); 144 | 145 | $expectedOutput = << [ERROR] Call to /error failed with a 400 Bad Request result 147 | ===> [ERROR] This indicates a problem with authentication or headers. 148 | ===> [ERROR] Please ensure that the .edgerc file is formatted correctly. 149 | ===> [ERROR] If you still have issues, please use gen_edgerc.php to generate the credentials 150 | ===> [ERROR] Problem details: 151 | error string 152 | 153 | EOF; 154 | 155 | $this->assertEquals($expectedOutput, $output); 156 | } 157 | 158 | public function testDebugMessages() 159 | { 160 | $handler = $this->getMockHandler([ 161 | new Response(400, [], json_encode(['detail' => 'error message 1'])), 162 | new Response(401, [], json_encode(['detail' => 'error message 2'])), 163 | new Response(403, [], json_encode(['detail' => 'error message 3'])), 164 | new Response(404, [], json_encode(['detail' => 'error message 4'])) 165 | ]); 166 | 167 | $client = new Client( 168 | [ 169 | 'base_uri' => 'http://example.org', 170 | 'handler' => $handler, 171 | ] 172 | ); 173 | $client->setAuth('test', 'test', 'test'); 174 | 175 | $fp = fopen('php://memory', 'ab+'); 176 | $client->setInstanceDebug($fp); 177 | 178 | $this->expectOutputString(''); 179 | 180 | try { 181 | $client->get('/400'); 182 | } catch (\Exception $e) { 183 | } 184 | try { 185 | $client->get('/401'); 186 | } catch (\Exception $e) { 187 | } 188 | try { 189 | $client->get('/403'); 190 | } catch (\Exception $e) { 191 | } 192 | try { 193 | $client->get('/404'); 194 | } catch (\Exception $e) { 195 | } 196 | 197 | $output = $this->readStreamData($fp); 198 | 199 | $expectedOutput = << [ERROR] Call to /400 failed with a 400 Bad Request result 201 | ===> [ERROR] This indicates a problem with authentication or headers. 202 | ===> [ERROR] Please ensure that the .edgerc file is formatted correctly. 203 | ===> [ERROR] If you still have issues, please use gen_edgerc.php to generate the credentials 204 | ===> [ERROR] Problem details: 205 | error message 1 206 | ===> [ERROR] Call to /401 failed with a 401 Unauthorized result 207 | ===> [ERROR] This indicates a problem with authentication or headers. 208 | ===> [ERROR] Please ensure that the .edgerc file is formatted correctly. 209 | ===> [ERROR] If you still have issues, please use gen_edgerc.php to generate the credentials 210 | ===> [ERROR] Problem details: 211 | error message 2 212 | ===> [ERROR] Call to /403 failed with a 403 Forbidden result 213 | ===> [ERROR] This indicates a problem with authorization. 214 | 215 | ===> [ERROR] Please ensure that the credentials you created for this script 216 | 217 | ===> [ERROR] have the necessary permissions in the Luna portal. 218 | 219 | ===> [ERROR] Problem details: 220 | error message 3 221 | ===> [ERROR] Call to /404 failed with a 404 Not Found result 222 | ===> [ERROR] This means that the page does not exist as requested. 223 | 224 | ===> [ERROR] Please ensure that the URL you're calling is correctly formatted 225 | 226 | ===> [ERROR] or look at other examples to make sure yours matches. 227 | 228 | ===> [ERROR] Problem details: 229 | error message 4 230 | 231 | EOF; 232 | 233 | $this->assertEquals($expectedOutput, $output); 234 | } 235 | 236 | public function testResponseNoDetail() 237 | { 238 | $handler = $this->getMockHandler([new Response(500, [], json_encode(['nodetail' => 'error info']))]); 239 | 240 | $client = new Client( 241 | [ 242 | 'base_uri' => 'http://example.org', 243 | 'handler' => $handler, 244 | ] 245 | ); 246 | $client->setAuth('test', 'test', 'test'); 247 | 248 | $fp = fopen('php://memory', 'ab+'); 249 | Client::setDebug($fp); 250 | 251 | $this->expectOutputString(''); 252 | 253 | try { 254 | $client->get('/error'); 255 | } catch (\Exception $e) { 256 | } 257 | 258 | $output = $this->readStreamData($fp); 259 | 260 | $expectedOutput = << [ERROR] Call to /error failed with a 500 Internal Server Error result 262 | ===> [ERROR] Problem details: 263 | { 264 | "nodetail": "error info" 265 | } 266 | 267 | EOF; 268 | 269 | $this->assertEquals($expectedOutput, $output); 270 | } 271 | 272 | public function testResponseNoBody() 273 | { 274 | $handler = $this->getMockHandler([new Response(500, [])]); 275 | 276 | $client = new Client( 277 | [ 278 | 'base_uri' => 'http://example.org', 279 | 'handler' => $handler, 280 | ] 281 | ); 282 | $client->setAuth('test', 'test', 'test'); 283 | 284 | $fp = fopen('php://memory', 'ab+'); 285 | Client::setDebug($fp); 286 | 287 | $this->expectOutputString(''); 288 | 289 | try { 290 | $client->get('/error'); 291 | } catch (\Exception $e) { 292 | } 293 | 294 | $output = $this->readStreamData($fp); 295 | 296 | $expectedOutput = << [ERROR] Call to /error failed with a 500 Internal Server Error result 298 | ===> [ERROR] Problem details: 299 | No response body returned 300 | 301 | EOF; 302 | 303 | $this->assertEquals($expectedOutput, $output); 304 | } 305 | 306 | public function testResponseNoJsonBody() 307 | { 308 | $handler = $this->getMockHandler([new Response(500, [], 'error info')]); 309 | 310 | $client = new Client( 311 | [ 312 | 'base_uri' => 'http://example.org', 313 | 'handler' => $handler, 314 | ] 315 | ); 316 | $client->setAuth('test', 'test', 'test'); 317 | 318 | $fp = fopen('php://memory', 'ab+'); 319 | Client::setDebug($fp); 320 | 321 | $this->expectOutputString(''); 322 | 323 | try { 324 | $client->get('/error'); 325 | } catch (\Exception $e) { 326 | } 327 | 328 | $output = $this->readStreamData($fp); 329 | 330 | $expectedOutput = << [ERROR] Call to /error failed with a 500 Internal Server Error result 332 | ===> [ERROR] Problem details: 333 | error info 334 | 335 | EOF; 336 | 337 | $this->assertEquals($expectedOutput, $output); 338 | } 339 | 340 | public function testStringResource() 341 | { 342 | $handler = new \Akamai\Open\EdgeGrid\Handler\Debug('php://stdout'); 343 | 344 | $reflector = new \ReflectionClass($handler); 345 | $reflectedFP = $reflector->getProperty('fp'); 346 | $reflectedFP->setAccessible(true); 347 | 348 | $fp = $reflectedFP->getValue($handler); 349 | 350 | $this->assertEquals('php://stdout', stream_get_meta_data($fp)['uri']); 351 | } 352 | 353 | public function testInvalidStringResource() 354 | { 355 | $this->expectException(\Akamai\Open\EdgeGrid\Exception\HandlerException\IOException::class); 356 | $this->expectExceptionMessage('Unable to use resource: fake://stream'); 357 | 358 | $handler = new \Akamai\Open\EdgeGrid\Handler\Debug('fake://stream'); 359 | } 360 | 361 | public function testDebugResponseExceptionNoCode() 362 | { 363 | $handler = $this->getMockHandler([ 364 | new \GuzzleHttp\Exception\RequestException('Error message', new \GuzzleHttp\Psr7\Request('GET', '/test')) 365 | ]); 366 | 367 | $client = new Client( 368 | [ 369 | 'base_uri' => 'http://example.org', 370 | 'handler' => $handler, 371 | ] 372 | ); 373 | 374 | $fp = fopen('php://memory', 'ab+'); 375 | Client::setDebug($fp); 376 | 377 | $this->expectOutputString(''); 378 | 379 | try { 380 | $client->get('/error'); 381 | } catch (\GuzzleHttp\Exception\RequestException $e) { 382 | } 383 | 384 | $output = $this->readStreamData($fp); 385 | $this->assertEmpty($output); 386 | } 387 | 388 | public function testVerboseResponseExceptionWithCode() 389 | { 390 | $handler = $this->getMockHandler([ 391 | new \GuzzleHttp\Exception\RequestException( 392 | 'Error message', 393 | new \GuzzleHttp\Psr7\Request('GET', '/test'), 394 | new Response(500) 395 | ) 396 | ]); 397 | 398 | $client = new Client( 399 | [ 400 | 'base_uri' => 'http://example.org', 401 | 'handler' => $handler, 402 | ] 403 | ); 404 | 405 | $fp = fopen('php://memory', 'ab+'); 406 | Client::setDebug($fp); 407 | 408 | $this->expectOutputString(''); 409 | 410 | try { 411 | $client->get('/error'); 412 | } catch (\GuzzleHttp\Exception\RequestException $e) { 413 | } 414 | 415 | $output = $this->readStreamData($fp); 416 | $this->assertEmpty($output); 417 | } 418 | 419 | public function testVerboseResponseExceptionWithBody() 420 | { 421 | $handler = $this->getMockHandler([ 422 | new \GuzzleHttp\Exception\RequestException( 423 | 'Error message', 424 | new \GuzzleHttp\Psr7\Request('GET', '/test'), 425 | new Response(500, [], json_encode(['errorString' => 'An error'])) 426 | ) 427 | ]); 428 | 429 | $client = new Client( 430 | [ 431 | 'base_uri' => 'http://example.org', 432 | 'handler' => $handler, 433 | ] 434 | ); 435 | 436 | $fp = fopen('php://memory', 'ab+'); 437 | Client::setDebug($fp); 438 | 439 | $this->expectOutputString(''); 440 | 441 | try { 442 | $client->get('/error'); 443 | } catch (\GuzzleHttp\Exception\RequestException $e) { 444 | } 445 | 446 | $output = $this->readStreamData($fp); 447 | 448 | $this->assertEmpty($output); 449 | } 450 | 451 | public function getMockHandler($request, array &$container = null) 452 | { 453 | $client = new \Akamai\Open\EdgeGrid\Tests\ClientTest(); 454 | return $client->getMockHandler($request, $container); 455 | } 456 | 457 | protected function readStreamData($fp) 458 | { 459 | fseek($fp, 0); 460 | $output = ''; 461 | while (!feof($fp)) { 462 | $output .= fgets($fp); 463 | } 464 | 465 | return $output; 466 | } 467 | } 468 | -------------------------------------------------------------------------------- /tests/Handler/VerboseTest.php: -------------------------------------------------------------------------------- 1 | getMockHandler([new Response(200, [], json_encode(['test' => 'data']))]); 18 | $client = new Client( 19 | [ 20 | 'base_uri' => 'http://example.org', 21 | 'handler' => $handler, 22 | ] 23 | ); 24 | $client->setAuth('test', 'test', 'test'); 25 | 26 | $client->setInstanceVerbose(true); 27 | 28 | $expectedOutput = hex2bin( 29 | '1b5b33363b30316d3d3d3d3e205b564552424f53455d20526571756573743a200a1b5b33333' . 30 | 'b30316d4e6f207265717565737420626f64792073656e741b5b33393b34393b30306d0a1b5b' . 31 | '33363b30316d3d3d3d3e205b564552424f53455d20526573706f6e73653a200a1b5b33333b3' . 32 | '0316d7b0a202020202274657374223a202264617461220a7d1b5b33393b34393b30306d0a' 33 | ); 34 | $this->expectOutputString($expectedOutput); 35 | 36 | $client->get('/test'); 37 | } 38 | 39 | public function testInstanceVerboseMultiple() 40 | { 41 | $handler = $this->getMockHandler([ 42 | new Response(200, [], json_encode(['test' => 'data'])), 43 | new Response(200, [], json_encode(['test' => 'data2', ['foo', 'bar'], false, null, 123, 0.123])) 44 | ]); 45 | $client = new Client( 46 | [ 47 | 'base_uri' => 'http://example.org', 48 | 'handler' => $handler, 49 | ] 50 | ); 51 | $client->setAuth('test', 'test', 'test'); 52 | 53 | $client->setInstanceVerbose(true); 54 | 55 | $expectedOutput = hex2bin( 56 | '1b5b33363b30316d3d3d3d3e205b564552424f53455d20526571756573743a200a1b5b33333' . 57 | 'b30316d4e6f207265717565737420626f64792073656e741b5b33393b34393b30306d0a1b5b' . 58 | '33363b30316d3d3d3d3e205b564552424f53455d20526573706f6e73653a200a1b5b33333b3' . 59 | '0316d7b0a202020202274657374223a202264617461220a7d1b5b33393b34393b30306d0a1b' . 60 | '5b33363b30316d3d3d3d3e205b564552424f53455d20526571756573743a200a1b5b33333b3' . 61 | '0316d4e6f207265717565737420626f64792073656e741b5b33393b34393b30306d0a1b5b33' . 62 | '363b30316d3d3d3d3e205b564552424f53455d20526573706f6e73653a200a1b5b33333b303' . 63 | '16d7b0a202020202274657374223a20226461746132222c0a202020202230223a205b0a2020' . 64 | '20202020202022666f6f222c0a202020202020202022626172220a202020205d2c0a2020202' . 65 | '02231223a2066616c73652c0a202020202232223a206e756c6c2c0a202020202233223a2031' . 66 | '32332c0a202020202234223a20302e3132330a7d1b5b33393b34393b30306d0a' 67 | ); 68 | $this->expectOutputString($expectedOutput); 69 | 70 | $client->get('/test1'); 71 | $client->get('/test2'); 72 | } 73 | 74 | public function testStaticVerboseSingle() 75 | { 76 | $handler = $this->getMockHandler([new Response(200, [], json_encode(['test' => 'data']))]); 77 | $client = new Client( 78 | [ 79 | 'base_uri' => 'http://example.org', 80 | 'handler' => $handler, 81 | ] 82 | ); 83 | 84 | Client::setVerbose(true); 85 | 86 | $expectedOutput = hex2bin( 87 | '1b5b33363b30316d3d3d3d3e205b564552424f53455d20526571756573743a200a1b5b33333' . 88 | 'b30316d4e6f207265717565737420626f64792073656e741b5b33393b34393b30306d0a1b5b' . 89 | '33363b30316d3d3d3d3e205b564552424f53455d20526573706f6e73653a200a1b5b33333b3' . 90 | '0316d7b0a202020202274657374223a202264617461220a7d1b5b33393b34393b30306d0a' 91 | ); 92 | $this->expectOutputString($expectedOutput); 93 | 94 | $client->get('/test'); 95 | } 96 | 97 | public function testStaticVerboseMultiple() 98 | { 99 | $handler = $this->getMockHandler([ 100 | new Response(200, [], json_encode(['test' => 'data'])), 101 | new Response(200, [], json_encode(['test' => 'data2', ['foo', 'bar'], false, null, 123, 0.123])) 102 | ]); 103 | $client = new Client( 104 | [ 105 | 'base_uri' => 'http://example.org', 106 | 'handler' => $handler, 107 | ] 108 | ); 109 | 110 | Client::setVerbose(true); 111 | 112 | $expectedOutput = hex2bin( 113 | '1b5b33363b30316d3d3d3d3e205b564552424f53455d20526571756573743a200a1b5b33333' . 114 | 'b30316d4e6f207265717565737420626f64792073656e741b5b33393b34393b30306d0a1b5b' . 115 | '33363b30316d3d3d3d3e205b564552424f53455d20526573706f6e73653a200a1b5b33333b3' . 116 | '0316d7b0a202020202274657374223a202264617461220a7d1b5b33393b34393b30306d0a1b' . 117 | '5b33363b30316d3d3d3d3e205b564552424f53455d20526571756573743a200a1b5b33333b3' . 118 | '0316d4e6f207265717565737420626f64792073656e741b5b33393b34393b30306d0a1b5b33' . 119 | '363b30316d3d3d3d3e205b564552424f53455d20526573706f6e73653a200a1b5b33333b303' . 120 | '16d7b0a202020202274657374223a20226461746132222c0a202020202230223a205b0a2020' . 121 | '20202020202022666f6f222c0a202020202020202022626172220a202020205d2c0a2020202' . 122 | '02231223a2066616c73652c0a202020202232223a206e756c6c2c0a202020202233223a2031' . 123 | '32332c0a202020202234223a20302e3132330a7d1b5b33393b34393b30306d0a' 124 | ); 125 | $this->expectOutputString($expectedOutput); 126 | 127 | $client->get('/test'); 128 | $client->get('/test2'); 129 | } 130 | 131 | public function testVerboseOverrideSingle() 132 | { 133 | $handler = $this->getMockHandler([new Response(200, [], json_encode(['test' => 'data']))]); 134 | $client = new Client( 135 | [ 136 | 'base_uri' => 'http://example.org', 137 | 'handler' => $handler, 138 | ] 139 | ); 140 | 141 | Client::setVerbose(true); 142 | $client->setInstanceVerbose(false); 143 | 144 | $this->expectOutputString(''); 145 | 146 | $client->get('/test'); 147 | } 148 | 149 | public function testVerboseOverrideMultiple() 150 | { 151 | $handler = $this->getMockHandler([ 152 | new Response(200, [], json_encode(['test' => 'data'])), 153 | new Response(200, [], json_encode(['test' => 'data2', ['foo', 'bar'], false, null, 123, 0.123])) 154 | ]); 155 | $client = new Client( 156 | [ 157 | 'base_uri' => 'http://example.org', 158 | 'handler' => $handler, 159 | ] 160 | ); 161 | 162 | Client::setVerbose(true); 163 | $client->setInstanceVerbose(false); 164 | 165 | $this->expectOutputString(''); 166 | 167 | $client->get('/test'); 168 | $client->get('/test2'); 169 | } 170 | 171 | public function testVerboseRedirect() 172 | { 173 | $handler = $this->getMockHandler([ 174 | new Response(301, ['Location' => 'http://example.org/redirected'], json_encode(['test' => 'data'])), 175 | new Response(200, [], json_encode(['test' => 'data2', ['foo', 'bar'], false, null, 123, 0.123])) 176 | ]); 177 | 178 | $client = new Client( 179 | [ 180 | 'base_uri' => 'http://example.org', 181 | 'handler' => $handler, 182 | ] 183 | ); 184 | 185 | Client::setVerbose(true); 186 | 187 | $expectedOutput = hex2bin( 188 | '1b5b33363b30316d3d3d3d3e205b564552424f53455d20526571756573743a200a1b5b33333' . 189 | 'b30316d4e6f207265717565737420626f64792073656e741b5b33393b34393b30306d0a1b5b' . 190 | '33363b30316d3d3d3d3e205b564552424f53455d20526564697265637465643a20687474703' . 191 | 'a2f2f6578616d706c652e6f72672f726564697265637465641b5b33393b34393b30306d0a1b' . 192 | '5b33363b30316d3d3d3d3e205b564552424f53455d20526571756573743a200a1b5b33333b3' . 193 | '0316d4e6f207265717565737420626f64792073656e741b5b33393b34393b30306d0a1b5b33' . 194 | '363b30316d3d3d3d3e205b564552424f53455d20526573706f6e73653a200a1b5b33333b303' . 195 | '16d7b0a202020202274657374223a20226461746132222c0a202020202230223a205b0a2020' . 196 | '20202020202022666f6f222c0a202020202020202022626172220a202020205d2c0a2020202' . 197 | '02231223a2066616c73652c0a202020202232223a206e756c6c2c0a202020202233223a2031' . 198 | '32332c0a202020202234223a20302e3132330a7d1b5b33393b34393b30306d0a' 199 | ); 200 | $this->expectOutputString($expectedOutput); 201 | 202 | $client->get('/redirect'); 203 | } 204 | 205 | public function testVerboseNonJson() 206 | { 207 | $handler = $this->getMockHandler([ 208 | new Response(200, [], 'String body') 209 | ]); 210 | 211 | $client = new Client( 212 | [ 213 | 'base_uri' => 'http://example.org', 214 | 'handler' => $handler, 215 | ] 216 | ); 217 | 218 | Client::setVerbose(true); 219 | 220 | $expectedOutput = hex2bin( 221 | '1b5b33363b30316d3d3d3d3e205b564552424f53455d20526571756573743a200a1b5b33333' . 222 | 'b30316d4e6f207265717565737420626f64792073656e741b5b33393b34393b30306d0a1b5b' . 223 | '33363b30316d3d3d3d3e205b564552424f53455d20526573706f6e73653a200a1b5b33333b3' . 224 | '0316d537472696e6720626f64791b5b33393b34393b30306d0a' 225 | ); 226 | $this->expectOutputString($expectedOutput); 227 | 228 | try { 229 | $client->get('/error'); 230 | } catch (\GuzzleHttp\Exception\ClientException $e) { 231 | } 232 | } 233 | 234 | public function testVerboseRequestHandler() 235 | { 236 | $handler = $this->getMockHandler([ 237 | new Response(200, [], 'String body') 238 | ]); 239 | 240 | $client = new Client( 241 | [ 242 | 'base_uri' => 'http://example.org', 243 | ] 244 | ); 245 | 246 | Client::setVerbose(true); 247 | 248 | $expectedOutput = hex2bin( 249 | '1b5b33363b30316d3d3d3d3e205b564552424f53455d20526571756573743a200a1b5b33333' . 250 | 'b30316d4e6f207265717565737420626f64792073656e741b5b33393b34393b30306d0a1b5b' . 251 | '33363b30316d3d3d3d3e205b564552424f53455d20526573706f6e73653a200a1b5b33333b3' . 252 | '0316d537472696e6720626f64791b5b33393b34393b30306d0a' 253 | ); 254 | $this->expectOutputString($expectedOutput); 255 | 256 | try { 257 | $client->get('/error', ['handler' => $handler]); 258 | } catch (\GuzzleHttp\Exception\ClientException $e) { 259 | } 260 | } 261 | 262 | public function testVerboseNoResponse() 263 | { 264 | $handler = $this->getMockHandler([ 265 | new Response(101) 266 | ]); 267 | 268 | $client = new Client( 269 | [ 270 | 'base_uri' => 'http://example.org', 271 | 'handler' => $handler, 272 | ] 273 | ); 274 | 275 | Client::setVerbose(true); 276 | 277 | $expectedOutput = hex2bin( 278 | '1b5b33363b30316d3d3d3d3e205b564552424f53455d20526571756573743a200a1b5b33333' . 279 | 'b30316d4e6f207265717565737420626f64792073656e741b5b33393b34393b30306d0a1b5b' . 280 | '33363b30316d3d3d3d3e205b564552424f53455d20526573706f6e73653a200a1b5b33333b3' . 281 | '0316d4e6f20726573706f6e736520626f64792072657475726e65641b5b33393b34393b3030' . 282 | '6d0a' 283 | ); 284 | $this->expectOutputString($expectedOutput); 285 | 286 | 287 | try { 288 | $client->get('/error'); 289 | } catch (\GuzzleHttp\Exception\ClientException $e) { 290 | } 291 | } 292 | 293 | public function testVerboseError() 294 | { 295 | $handler = $this->getMockHandler([ 296 | new Response(404, [], json_encode(['test' => 'data2', ['foo', 'bar'], false, null, 123, 0.123])) 297 | ]); 298 | 299 | $client = new Client( 300 | [ 301 | 'base_uri' => 'http://example.org', 302 | 'handler' => $handler, 303 | ] 304 | ); 305 | 306 | $fp = fopen('php://memory', 'ab+'); 307 | Client::setVerbose([$fp, $fp]); 308 | 309 | $this->expectOutputString(''); 310 | try { 311 | $client->get('/error'); 312 | } catch (\GuzzleHttp\Exception\ClientException $e) { 313 | } 314 | 315 | fseek($fp, 0); 316 | $output = ''; 317 | do { 318 | $output .= fgets($fp); 319 | } while (!feof($fp)); 320 | 321 | $expectedOutput = hex2bin( 322 | '1b5b33363b30316d3d3d3d3e205b564552424f53455d20526571756573743a200a1b5b33333b30316d4e6f207265717565737420626f64792073656e741b5b33393b34393b30306d0a1b5b33313b30316d3d3d3d3e205b4552524f525d20416e206572726f72206f636375727265643a200a1b5b33333b30316d7b0a202020202274657374223a20226461746132222c0a202020202230223a205b0a202020202020202022666f6f222c0a202020202020202022626172220a202020205d2c0a202020202231223a2066616c73652c0a202020202232223a206e756c6c2c0a202020202233223a203132332c0a202020202234223a20302e3132330a7d1b5b33393b34393b30306d0a' 323 | ); 324 | 325 | $this->assertEquals($expectedOutput, $output); 326 | } 327 | 328 | public function testVerboseErrorNonJson() 329 | { 330 | $handler = $this->getMockHandler([ 331 | new Response(404, [], 'String body') 332 | ]); 333 | 334 | $client = new Client( 335 | [ 336 | 'base_uri' => 'http://example.org', 337 | 'handler' => $handler, 338 | ] 339 | ); 340 | 341 | $fp = fopen('php://memory', 'ab+'); 342 | Client::setVerbose($fp); 343 | 344 | $this->expectOutputString(''); 345 | try { 346 | $client->get('/error'); 347 | } catch (\GuzzleHttp\Exception\ClientException $e) { 348 | } 349 | 350 | fseek($fp, 0); 351 | $output = ''; 352 | do { 353 | $output .= fgets($fp); 354 | } while (!feof($fp)); 355 | 356 | $expectedOutput = hex2bin( 357 | '1b5b33363b30316d3d3d3d3e205b564552424f53455d20526571756573743a200a1b5b33333b30316d4e6f207265717565737420626f64792073656e741b5b33393b34393b30306d0a1b5b33313b30316d3d3d3d3e205b4552524f525d20416e206572726f72206f636375727265643a200a1b5b33333b30316d537472696e6720626f64791b5b33393b34393b30306d0a' 358 | ); 359 | 360 | $this->assertEquals($expectedOutput, $output); 361 | } 362 | 363 | public function testVerboseMixed() 364 | { 365 | $handler = $this->getMockHandler([ 366 | new Response(200, [], json_encode(['test' => 'data'])), 367 | new Response(404, [], json_encode(['test' => 'data2', ['foo', 'bar'], false, null, 123, 0.123])) 368 | ]); 369 | 370 | $client = new Client( 371 | [ 372 | 'base_uri' => 'http://example.org', 373 | 'handler' => $handler, 374 | ] 375 | ); 376 | 377 | $fp = fopen('php://memory', 'ab+'); 378 | Client::setVerbose(['php://output', $fp]); 379 | 380 | 381 | $expectedOutput = hex2bin( 382 | '1b5b33363b30316d3d3d3d3e205b564552424f53455d20526571756573743a200a1b5b33333b30316d4e6f207265717565737420626f64792073656e741b5b33393b34393b30306d0a1b5b33363b30316d3d3d3d3e205b564552424f53455d20526573706f6e73653a200a1b5b33333b30316d7b0a202020202274657374223a202264617461220a7d1b5b33393b34393b30306d0a1b5b33363b30316d3d3d3d3e205b564552424f53455d20526571756573743a200a1b5b33333b30316d4e6f207265717565737420626f64792073656e741b5b33393b34393b30306d0a' 383 | ); 384 | $this->expectOutputString($expectedOutput); 385 | 386 | try { 387 | $client->get('/success'); 388 | $client->get('/error'); 389 | } catch (\GuzzleHttp\Exception\ClientException $e) { 390 | } 391 | 392 | fseek($fp, 0); 393 | $output = ''; 394 | do { 395 | $output .= fgets($fp); 396 | } while (!feof($fp)); 397 | 398 | $expectedError = hex2bin( 399 | '1b5b33313b30316d3d3d3d3e205b4552524f525d20416e206572726f72206f6363757272656' . 400 | '43a200a1b5b33333b30316d7b0a202020202274657374223a20226461746132222c0a202020' . 401 | '202230223a205b0a202020202020202022666f6f222c0a202020202020202022626172220a2' . 402 | '02020205d2c0a202020202231223a2066616c73652c0a202020202232223a206e756c6c2c0a' . 403 | '202020202233223a203132332c0a202020202234223a20302e3132330a7d1b5b33393b34393' . 404 | 'b30306d0a' 405 | ); 406 | 407 | $this->assertEquals($expectedError, $output); 408 | } 409 | 410 | public function testVerboseResponseExceptionNoCode() 411 | { 412 | $handler = $this->getMockHandler([ 413 | new \GuzzleHttp\Exception\RequestException('Error message', new \GuzzleHttp\Psr7\Request('GET', '/test')) 414 | ]); 415 | 416 | $client = new Client( 417 | [ 418 | 'base_uri' => 'http://example.org', 419 | 'handler' => $handler, 420 | ] 421 | ); 422 | 423 | Client::setVerbose(true); 424 | 425 | $expectedOutput = hex2bin( 426 | '1b5b33363b30316d3d3d3d3e205b564552424f53455d20526571756573743a200a1b5b33333' . 427 | 'b30316d4e6f207265717565737420626f64792073656e741b5b33393b34393b30306d0a1b5b' . 428 | '33313b30316d3d3d3d3e205b4552524f525d20416e206572726f72206f636375727265643a2' . 429 | '00a1b5b33333b30316d4572726f72206d6573736167651b5b33393b34393b30306d0a' 430 | ); 431 | $this->expectOutputString($expectedOutput); 432 | 433 | try { 434 | $client->get('/error'); 435 | } catch (\GuzzleHttp\Exception\RequestException $e) { 436 | } 437 | } 438 | 439 | public function testVerboseResponseExceptionWithCode() 440 | { 441 | $handler = $this->getMockHandler([ 442 | new \GuzzleHttp\Exception\RequestException( 443 | 'Error message', 444 | new \GuzzleHttp\Psr7\Request('GET', '/test'), 445 | new Response(500) 446 | ) 447 | ]); 448 | 449 | $client = new Client( 450 | [ 451 | 'base_uri' => 'http://example.org', 452 | 'handler' => $handler, 453 | ] 454 | ); 455 | 456 | Client::setVerbose(true); 457 | 458 | $expectedOutput = hex2bin( 459 | '1b5b33363b30316d3d3d3d3e205b564552424f53455d20526571756573743a200a1b5b33333' . 460 | 'b30316d4e6f207265717565737420626f64792073656e741b5b33393b34393b30306d0a1b5b' . 461 | '33313b30316d3d3d3d3e205b4552524f525d20416e206572726f72206f636375727265643a2' . 462 | '00a1b5b33333b30316d3530303a204572726f72206d6573736167651b5b33393b34393b3030' . 463 | '6d0a' 464 | ); 465 | $this->expectOutputString($expectedOutput); 466 | 467 | try { 468 | $client->get('/error'); 469 | } catch (\GuzzleHttp\Exception\RequestException $e) { 470 | } 471 | } 472 | 473 | public function testVerboseResponseExceptionWithBody() 474 | { 475 | $handler = $this->getMockHandler([ 476 | new \GuzzleHttp\Exception\RequestException( 477 | 'Error message', 478 | new \GuzzleHttp\Psr7\Request('GET', '/test'), 479 | new Response(500, [], json_encode(['errorString' => 'An error'])) 480 | ) 481 | ]); 482 | 483 | $client = new Client( 484 | [ 485 | 'base_uri' => 'http://example.org', 486 | 'handler' => $handler, 487 | ] 488 | ); 489 | 490 | Client::setVerbose(true); 491 | 492 | $expectedOutput = hex2bin( 493 | '1b5b33363b30316d3d3d3d3e205b564552424f53455d20526571756573743a200a1b5b33333' . 494 | 'b30316d4e6f207265717565737420626f64792073656e741b5b33393b34393b30306d0a1b5b' . 495 | '33313b30316d3d3d3d3e205b4552524f525d20416e206572726f72206f636375727265643a2' . 496 | '00a1b5b33333b30316d3530303a204572726f72206d6573736167650a1b5b33333b30316d7b' . 497 | '226572726f72537472696e67223a22416e206572726f72227d1b5b33393b34393b30306d0a' 498 | ); 499 | $this->expectOutputString($expectedOutput); 500 | 501 | try { 502 | $client->get('/error'); 503 | } catch (\GuzzleHttp\Exception\RequestException $e) { 504 | } 505 | } 506 | 507 | public function testVerboseSingleStreamString() 508 | { 509 | $verbose = new \Akamai\Open\EdgeGrid\Handler\Verbose('php://memory'); 510 | 511 | $reflector = new \ReflectionClass($verbose); 512 | $reflectedOutputStream = $reflector->getProperty('outputStream'); 513 | $reflectedErrorStream = $reflector->getProperty('errorStream'); 514 | $reflectedOutputStream->setAccessible(true); 515 | $reflectedErrorStream->setAccessible(true); 516 | 517 | $fp = $reflectedOutputStream->getValue($verbose); 518 | $fp2 = $reflectedErrorStream->getvalue($verbose); 519 | 520 | $this->assertSame($fp, $fp2); 521 | $this->assertEquals(stream_get_meta_data($fp)['uri'], 'php://memory'); 522 | } 523 | 524 | public function testVerboseSingleStreamStringInvalid() 525 | { 526 | $this->expectException(\Akamai\Open\EdgeGrid\Exception\HandlerException\IOException::class); 527 | $this->expectExceptionMessage('Unable to use output stream: error://stream'); 528 | 529 | $verbose = new \Akamai\Open\EdgeGrid\Handler\Verbose('error://stream'); 530 | } 531 | 532 | public function testVerboseDualStreamString() 533 | { 534 | $verbose = new \Akamai\Open\EdgeGrid\Handler\Verbose('php://memory', 'php://temp'); 535 | 536 | $reflector = new \ReflectionClass($verbose); 537 | $reflectedOutputStream = $reflector->getProperty('outputStream'); 538 | $reflectedErrorStream = $reflector->getProperty('errorStream'); 539 | $reflectedOutputStream->setAccessible(true); 540 | $reflectedErrorStream->setAccessible(true); 541 | 542 | $fp = $reflectedOutputStream->getValue($verbose); 543 | $fp2 = $reflectedErrorStream->getvalue($verbose); 544 | 545 | $this->assertNotSame($fp, $fp2); 546 | $this->assertEquals(stream_get_meta_data($fp)['uri'], 'php://memory'); 547 | $this->assertEquals(stream_get_meta_data($fp2)['uri'], 'php://temp'); 548 | } 549 | 550 | public function testVerboseDualStreamStringErrorInvalid() 551 | { 552 | $this->expectException(\Akamai\Open\EdgeGrid\Exception\HandlerException\IOException::class); 553 | $this->expectExceptionMessage('Unable to use error stream: error://stream'); 554 | 555 | $verbose = new \Akamai\Open\EdgeGrid\Handler\Verbose('php://input', 'error://stream'); 556 | } 557 | 558 | /** 559 | * @expectedException \Akamai\Open\EdgeGrid\Exception\HandlerException\IOException 560 | * @expectedExceptionMessage Unable to use output stream: error://stream 561 | */ 562 | public function testVerboseDualStreamStringInvalid() 563 | { 564 | $this->expectException(\Akamai\Open\EdgeGrid\Exception\HandlerException\IOException::class); 565 | $this->expectExceptionMessage('Unable to use output stream: error://stream'); 566 | 567 | $verbose = new \Akamai\Open\EdgeGrid\Handler\Verbose('error://stream', 'error://stream2'); 568 | } 569 | 570 | public function getMockHandler($request, array &$container = null) 571 | { 572 | $client = new \Akamai\Open\EdgeGrid\Tests\ClientTest(); 573 | return $client->getMockHandler($request, $container); 574 | } 575 | } 576 | -------------------------------------------------------------------------------- /src/Client.php: -------------------------------------------------------------------------------- 1 | setAuth()}. 21 | * 22 | * @package Akamai\Open\EdgeGrid\Client 23 | */ 24 | class Client implements \Psr\Log\LoggerAwareInterface, \Psr\Http\Client\ClientInterface 25 | { 26 | use ClientTrait; 27 | 28 | public const VERSION = '2.1.1'; 29 | 30 | /** 31 | * @const int Default Timeout in seconds 32 | */ 33 | public const DEFAULT_REQUEST_TIMEOUT = 300; 34 | 35 | /** 36 | * @var bool|array|resource Whether verbose mode is enabled 37 | * 38 | * - true - Use STDERR 39 | * - array - output/error streams (different) 40 | * - resource - output/error stream (same) 41 | */ 42 | protected static $staticVerbose = false; 43 | 44 | /** 45 | * @var bool|resource Whether debug mode is enabled 46 | */ 47 | protected static $staticDebug = false; 48 | 49 | /** 50 | * @var \GuzzleHttp\Client 51 | */ 52 | protected $guzzler; 53 | 54 | /** 55 | * @var \Akamai\Open\EdgeGrid\Authentication 56 | */ 57 | protected $authentication; 58 | 59 | /** 60 | * @var \Akamai\Open\EdgeGrid\Handler\Verbose 61 | */ 62 | protected $verboseHandler; 63 | 64 | /** 65 | * @var \Akamai\Open\EdgeGrid\Handler\Debug 66 | */ 67 | protected $debugHandler; 68 | 69 | /** 70 | * @var bool|array|resource Whether verbose mode is enabled 71 | * 72 | * - true - Use STDOUT 73 | * - array - output/error streams (different) 74 | * - resource - output/error stream (same) 75 | */ 76 | protected $verbose = false; 77 | 78 | /** 79 | * @var bool|resource Whether debugging is enabled 80 | */ 81 | protected $debug = false; 82 | 83 | /** 84 | * @var bool Whether to override the static verbose setting 85 | */ 86 | protected $verboseOverride = false; 87 | 88 | /** 89 | * @var bool Whether to override the static debug setting 90 | */ 91 | protected $debugOverride = false; 92 | 93 | /** 94 | * @var \Psr\Log\LoggerInterface|null 95 | */ 96 | protected ?\Psr\Log\LoggerInterface $logger = null; 97 | 98 | /** 99 | * @var string 100 | */ 101 | protected $messageFormat = \GuzzleHttp\MessageFormatter::CLF; 102 | 103 | /** 104 | * \GuzzleHttp\Client-compatible constructor 105 | * 106 | * @param array $config Config options array 107 | * @param Authentication|null $authentication 108 | */ 109 | public function __construct( 110 | $config = [], 111 | ?Authentication $authentication = null 112 | ) { 113 | $config = $this->setAuthenticationHandler($config, $authentication); 114 | $config = $this->setBasicOptions($config); 115 | $config['headers']['User-Agent'] = 'Akamai-Open-Edgegrid-PHP/' . 116 | self::VERSION . ' ' . \GuzzleHttp\default_user_agent(); 117 | 118 | $this->guzzler = new GuzzleClient($config); 119 | } 120 | 121 | /** 122 | * Get client configuration options. 123 | * 124 | * @param string|null $option The config option to retrieve or all if null 125 | * 126 | * @return mixed 127 | */ 128 | public function getConfig($option = null) 129 | { 130 | return $this->guzzler->getConfig($option); 131 | } 132 | 133 | /** 134 | * Make a request 135 | * 136 | * @param string $method 137 | * @param string $uri 138 | * @param array $options 139 | * @return \Psr\Http\Message\ResponseInterface 140 | * @throws \GuzzleHttp\Exception\GuzzleException 141 | */ 142 | public function request(string $method, $uri = null, array $options = []): \Psr\Http\Message\ResponseInterface 143 | { 144 | $options = $this->setRequestOptions($options); 145 | 146 | $query = parse_url($uri, PHP_URL_QUERY); 147 | if (!empty($query)) { 148 | $uri = substr($uri, 0, (strlen($query) + 1) * -1); 149 | parse_str($query, $options['query']); 150 | } 151 | 152 | return $this->guzzler->request($method, $uri, $options); 153 | } 154 | 155 | /** 156 | * Make an Asynchronous request 157 | * 158 | * @param string $method 159 | * @param string $uri 160 | * @param array $options 161 | * @return \GuzzleHttp\Promise\PromiseInterface 162 | * @throws \GuzzleHttp\Exception\GuzzleException 163 | */ 164 | public function requestAsync($method, $uri = null, array $options = []): \GuzzleHttp\Promise\PromiseInterface 165 | { 166 | $options = $this->setRequestOptions($options); 167 | 168 | $query = parse_url($uri, PHP_URL_QUERY); 169 | if (!empty($query)) { 170 | $uri = substr($uri, 0, (strlen($query) + 1) * -1); 171 | parse_str($query, $options['query']); 172 | } 173 | 174 | return $this->guzzler->requestAsync($method, $uri, $options); 175 | } 176 | 177 | /** 178 | * Send an HTTP request 179 | * 180 | * @param \Psr\Http\Message\RequestInterface $request The HTTP request 181 | * @param array $options Request options 182 | * 183 | * @return \Psr\Http\Message\ResponseInterface 184 | */ 185 | public function send(\Psr\Http\Message\RequestInterface $request, array $options = []): \Psr\Http\Message\ResponseInterface 186 | { 187 | $options = $this->setRequestOptions($options); 188 | 189 | return $this->guzzler->send($request, $options); 190 | } 191 | 192 | /** 193 | * Send an HTTP request 194 | * 195 | * @param \Psr\Http\Message\RequestInterface $request The HTTP request 196 | * 197 | * @return \Psr\Http\Message\ResponseInterface 198 | */ 199 | public function sendRequest(\Psr\Http\Message\RequestInterface $request): \Psr\Http\Message\ResponseInterface 200 | { 201 | return $this->guzzleClient->sendRequest($request); 202 | } 203 | 204 | /** 205 | * Send an Asynchronous HTTP request 206 | * 207 | * @param \Psr\Http\Message\RequestInterface $request The HTTP request 208 | * @param array $options Request options 209 | * 210 | * @return \GuzzleHttp\Promise\PromiseInterface 211 | */ 212 | public function sendAsync(\Psr\Http\Message\RequestInterface $request, array $options = []): \GuzzleHttp\Promise\PromiseInterface 213 | { 214 | $options = $this->setRequestOptions($options); 215 | 216 | return $this->guzzler->sendAsync($request, $options); 217 | } 218 | 219 | /** 220 | * Create and send an HTTP OPTIONS request. 221 | * Implemented here as it is no longer provided by Guzzle built-in. 222 | * 223 | * @param string|UriInterface $uri URI object or string. 224 | * @param array $options Request options to apply. 225 | * 226 | * @throws \GuzzleHttp\Exception\GuzzleException 227 | */ 228 | public function options($uri, array $options = []): \Psr\Http\Message\ResponseInterface 229 | { 230 | return $this->request('OPTIONS', $uri, $options); 231 | } 232 | 233 | /** 234 | * Set Akamai {OPEN} Authentication Credentials 235 | * 236 | * @param string $client_token 237 | * @param string $client_secret 238 | * @param string $access_token 239 | * @return $this 240 | */ 241 | public function setAuth($client_token, $client_secret, $access_token) 242 | { 243 | $this->authentication->setAuth($client_token, $client_secret, $access_token); 244 | 245 | return $this; 246 | } 247 | 248 | /** 249 | * Specify the headers to include when signing the request 250 | * 251 | * This is specified by the API, currently no APIs use this 252 | * feature. 253 | * 254 | * @param array $headers 255 | * @return $this 256 | */ 257 | public function setHeadersToSign(array $headers) 258 | { 259 | $this->authentication->setHeadersToSign($headers); 260 | 261 | return $this; 262 | } 263 | 264 | /** 265 | * Set the max body size 266 | * 267 | * @param int $max_body_size 268 | * @return $this 269 | */ 270 | public function setMaxBodySize($max_body_size) 271 | { 272 | $this->authentication->setMaxBodySize($max_body_size); 273 | 274 | return $this; 275 | } 276 | 277 | /** 278 | * Set Request Host 279 | * 280 | * @param string $host 281 | * @return $this 282 | */ 283 | public function setHost($host) 284 | { 285 | if (substr($host, -1) === '/') { 286 | $host = substr($host, 0, -1); 287 | } 288 | 289 | $headers = $this->getConfig('headers'); 290 | $headers['Host'] = $host; 291 | $this->setConfigOption('headers', $headers); 292 | 293 | if (strpos('/', $host) === false) { 294 | $host = 'https://' . $host; 295 | } 296 | $this->setConfigOption('base_uri', $host); 297 | 298 | return $this; 299 | } 300 | 301 | /** 302 | * Set the HTTP request timeout 303 | * 304 | * @param int $timeout_in_seconds 305 | * @return $this 306 | */ 307 | public function setTimeout($timeout_in_seconds) 308 | { 309 | $this->setConfigOption('timeout', $timeout_in_seconds); 310 | 311 | return $this; 312 | } 313 | 314 | /** 315 | * Print formatted JSON responses to output 316 | * 317 | * @param bool|resource $enable 318 | * @return $this 319 | */ 320 | public function setInstanceVerbose($enable) 321 | { 322 | $this->verboseOverride = true; 323 | $this->verbose = $enable; 324 | return $this; 325 | } 326 | 327 | /** 328 | * Print HTTP requests/responses to output 329 | * 330 | * @param bool|resource $enable 331 | * @return $this 332 | */ 333 | public function setInstanceDebug($enable) 334 | { 335 | $this->debugOverride = true; 336 | $this->debug = $enable; 337 | return $this; 338 | } 339 | 340 | /** 341 | * Set a PSR-3 compatible logger (or use monolog by default) 342 | * 343 | * @param \Psr\Log\LoggerInterface $logger 344 | */ 345 | public function setLogger( 346 | ?\Psr\Log\LoggerInterface $logger = null, 347 | $messageFormat = null 348 | ): void { 349 | if ($logger === null) { 350 | $handler = new \Monolog\Handler\ErrorLogHandler(\Monolog\Handler\ErrorLogHandler::SAPI); 351 | $handler->setFormatter(new \Monolog\Formatter\LineFormatter('%message%')); 352 | $logger = new \Monolog\Logger('HTTP Log', [$handler]); 353 | } 354 | 355 | if ($messageFormat !== null) { 356 | $this->setMessageFormat($messageFormat); 357 | } 358 | $formatter = new \GuzzleHttp\MessageFormatter($this->messageFormat); 359 | 360 | $handler = \GuzzleHttp\Middleware::log($logger, $formatter); 361 | $this->logger = $logger; 362 | 363 | $handlerStack = $this->getConfig('handler'); 364 | $this->setLogHandler($handlerStack, $handler); 365 | } 366 | 367 | /** 368 | * Add logger using a given filename/format 369 | * 370 | * @param string $filename 371 | * @param string $format 372 | * @return \Akamai\Open\EdgeGrid\Client|bool 373 | */ 374 | public function setSimpleLog($filename, $format = '{code}') 375 | { 376 | if ($this->logger && !($this->logger instanceof \Monolog\Logger)) { 377 | return false; 378 | } 379 | 380 | $handler = new \Monolog\Handler\StreamHandler($filename); 381 | $handler->setFormatter(new \Monolog\Formatter\LineFormatter('%message%')); 382 | $log = new \Monolog\Logger('HTTP Log', [$handler]); 383 | 384 | $this->setLogger($log, $format); 385 | 386 | return $this; 387 | } 388 | 389 | /** 390 | * Create instance using environment (preferred) or .edgerc file (fallback) automatically. 391 | * 392 | * @param string $section 393 | * @param null $path 394 | * @return Client 395 | * @throws \Akamai\Open\EdgeGrid\Authentication\Exception\ConfigException 396 | */ 397 | public static function createInstance($section = 'default', $path = null, array $config = []) 398 | { 399 | $auth = \Akamai\Open\EdgeGrid\Authentication::createInstance($section, $path); 400 | 401 | if ($host = $auth->getHost()) { 402 | $config['base_uri'] = 'https://' . $host; 403 | } 404 | 405 | return new static($config, $auth); 406 | } 407 | 408 | public static function createFromEnv($section = 'default', array $config = []) 409 | { 410 | $auth = \Akamai\Open\EdgeGrid\Authentication::createFromEnv($section); 411 | 412 | if ($host = $auth->getHost()) { 413 | $config['base_uri'] = 'https://' . $host; 414 | } 415 | 416 | return new static($config, $auth); 417 | } 418 | 419 | /** 420 | * Factory method to create a client using credentials from `.edgerc` 421 | * 422 | * Automatically checks your HOME directory, and the current working 423 | * directory for credentials, if no path is supplied. 424 | * 425 | * @param string $section Credential section to use 426 | * @param string $path Path to .edgerc credentials file 427 | * @param array $config Options to pass to the constructor/guzzle 428 | * @return \Akamai\Open\EdgeGrid\Client 429 | */ 430 | public static function createFromEdgeRcFile($section = 'default', $path = null, array $config = []) 431 | { 432 | $auth = \Akamai\Open\EdgeGrid\Authentication::createFromEdgeRcFile($section, $path); 433 | 434 | if ($host = $auth->getHost()) { 435 | $config['base_uri'] = 'https://' . $host; 436 | } 437 | 438 | return new static($config, $auth); 439 | } 440 | 441 | /** 442 | * Print HTTP requests/responses to STDOUT 443 | * 444 | * @param bool|resource $enable 445 | */ 446 | public static function setDebug($enable) 447 | { 448 | self::$staticDebug = $enable; 449 | } 450 | 451 | /** 452 | * Print formatted JSON responses to STDOUT 453 | * 454 | * @param bool|resource|array $enable 455 | */ 456 | public static function setVerbose($enable) 457 | { 458 | self::$staticVerbose = $enable; 459 | } 460 | 461 | /** 462 | * Handle debug option 463 | * 464 | * @param array $config 465 | * @return bool|resource 466 | */ 467 | protected function getDebugOption(array $config) 468 | { 469 | if (isset($config['debug'])) { 470 | return ($config['debug'] === true) ? fopen('php://stderr', 'ab') : $config['debug']; 471 | } 472 | 473 | if ($this->debugOverride && $this->debug) { 474 | return ($this->debug === true) ? fopen('php://stderr', 'ab') : $this->debug; 475 | } elseif (!$this->debugOverride && static::$staticDebug) { 476 | return (static::$staticDebug === true) ? fopen('php://stderr', 'ab') : static::$staticDebug; 477 | } 478 | 479 | return false; 480 | } 481 | 482 | /** 483 | * Debugging status for the current request 484 | * 485 | * @return bool|resource 486 | */ 487 | protected function isDebug() 488 | { 489 | if (($this->debugOverride && !$this->debug) || (!$this->debugOverride && !static::$staticDebug)) { 490 | return false; 491 | } 492 | 493 | if ($this->debugOverride && $this->debug) { 494 | return $this->debug; 495 | } 496 | 497 | return static::$staticDebug; 498 | } 499 | 500 | /** 501 | * Verbose status for the current request 502 | * 503 | * @return array|bool|resource 504 | */ 505 | protected function isVerbose() 506 | { 507 | if (($this->verboseOverride && !$this->verbose) || (!$this->verboseOverride && !static::$staticVerbose)) { 508 | return false; 509 | } 510 | 511 | if ($this->verboseOverride && $this->verbose) { 512 | return $this->verbose; 513 | } 514 | 515 | return static::$staticVerbose; 516 | } 517 | 518 | /** 519 | * Set the Authentication instance 520 | * 521 | * @param array $config 522 | * @param Authentication|null $authentication 523 | */ 524 | protected function setAuthentication(array $config, ?Authentication $authentication = null) 525 | { 526 | $this->authentication = $authentication; 527 | if ($authentication === null) { 528 | $this->authentication = new Authentication(); 529 | } 530 | 531 | if (isset($config['timestamp'])) { 532 | $this->authentication->setTimestamp($config['timestamp']); 533 | } 534 | 535 | if (isset($config['nonce'])) { 536 | $this->authentication->setNonce($config['nonce']); 537 | } 538 | } 539 | 540 | /** 541 | * Set the Authentication Handler 542 | * 543 | * @param array $config 544 | * @param Authentication|null $authentication 545 | * @return array 546 | */ 547 | protected function setAuthenticationHandler(array $config, ?Authentication $authentication = null) 548 | { 549 | $this->setAuthentication($config, $authentication); 550 | 551 | $authenticationHandler = new AuthenticationHandler(); 552 | $authenticationHandler->setSigner($this->authentication); 553 | if (!isset($config['handler'])) { 554 | $config['handler'] = \GuzzleHttp\HandlerStack::create(); 555 | } 556 | try { 557 | if (!($config['handler'] instanceof \GuzzleHttp\HandlerStack)) { 558 | $config['handler'] = \GuzzleHttp\HandlerStack::create($config['handler']); 559 | } 560 | $config['handler']->before('history', $authenticationHandler, 'authentication'); 561 | } catch (\InvalidArgumentException $e) { 562 | // history middleware not added yet 563 | $config['handler']->push($authenticationHandler, 'authentication'); 564 | } 565 | return $config; 566 | } 567 | 568 | /** 569 | * Set timeout and base_uri options 570 | * 571 | * @param array $config 572 | * @return mixed 573 | */ 574 | protected function setBasicOptions(array $config) 575 | { 576 | if (!isset($config['timeout'])) { 577 | $config['timeout'] = static::DEFAULT_REQUEST_TIMEOUT; 578 | } 579 | 580 | if (isset($config['base_uri']) && strpos($config['base_uri'], 'http') === false) { 581 | $config['base_uri'] = 'https://' . $config['base_uri']; 582 | return $config; 583 | } 584 | return $config; 585 | } 586 | 587 | /** 588 | * Set values on the private \GuzzleHttp\Client->config 589 | * 590 | * This is a terrible hack, and illustrates why making 591 | * anything private makes it difficult to extend, and impossible 592 | * when there is no setter. 593 | * 594 | * @param string $what Config option to set 595 | * @param mixed $value Value to set the option to 596 | * @return void 597 | */ 598 | protected function setConfigOption($what, $value) 599 | { 600 | $closure = function () use ($what, $value) { 601 | /* @var $this \GuzzleHttp\Client */ 602 | $this->config[$what] = $value; 603 | }; 604 | 605 | $closure = $closure->bindTo($this->guzzler, \GuzzleHttp\Client::class); 606 | $closure(); 607 | } 608 | 609 | /** 610 | * Add the Debug handler to the HandlerStack 611 | * 612 | * @param array $options Guzzle Options 613 | * @param bool|resource|null $fp Stream to write to 614 | * @return array 615 | */ 616 | protected function setDebugHandler($options, $fp = null) 617 | { 618 | try { 619 | if (is_bool($fp)) { 620 | $fp = null; 621 | } 622 | 623 | $handler = $this->getConfig('handler'); 624 | // if we have a default handler, and we've already created a DebugHandler 625 | // we can bail out now (or we will add another one to the stack) 626 | if ($handler && $this->debugHandler) { 627 | return $options; 628 | } 629 | 630 | if (isset($options['handler'])) { 631 | $handler = $options['handler']; 632 | } 633 | 634 | if ($handler === null) { 635 | $handler = \GuzzleHttp\HandlerStack::create(); 636 | } 637 | 638 | if (!$this->debugHandler) { 639 | $this->debugHandler = new DebugHandler($fp); 640 | } 641 | 642 | $handler->after('allow_redirects', $this->debugHandler, 'debug'); 643 | } catch (\InvalidArgumentException $e) { 644 | $handler->push($this->debugHandler, 'debug'); 645 | } 646 | 647 | $options['handler'] = $handler; 648 | 649 | return $options; 650 | } 651 | 652 | /** 653 | * Add the Log handler to the HandlerStack 654 | * 655 | * @param \GuzzleHttp\HandlerStack $handlerStack 656 | * @param callable $logHandler 657 | * @return $this 658 | */ 659 | protected function setLogHandler(\GuzzleHttp\HandlerStack $handlerStack, callable $logHandler) 660 | { 661 | try { 662 | $handlerStack->after('history', $logHandler, 'logger'); 663 | } catch (\InvalidArgumentException $e) { 664 | try { 665 | $handlerStack->before('allow_redirects', $logHandler, 'logger'); 666 | } catch (\InvalidArgumentException $e) { 667 | $handlerStack->push($logHandler, 'logger'); 668 | } 669 | } 670 | 671 | return $this; 672 | } 673 | 674 | /** 675 | * Add the Verbose handler to the HandlerStack 676 | * 677 | * @param array $options Guzzle Options 678 | * @param bool|resource|array|null $fp Stream to write to 679 | * @return array 680 | */ 681 | protected function setVerboseHandler($options, $fp = null) 682 | { 683 | try { 684 | if (is_bool($fp) || $fp === null) { 685 | $fp = ['outputStream' => null, 'errorStream' => null]; 686 | } elseif (!is_array($fp)) { 687 | $fp = ['outputStream' => $fp, 'errorStream' => $fp]; 688 | } 689 | 690 | $handler = $this->getConfig('handler'); 691 | // if we have a default handler, and we've already created a VerboseHandler 692 | // we can bail out now (or we will add another one to the stack) 693 | if ($handler && $this->verboseHandler) { 694 | return $options; 695 | } 696 | 697 | if (isset($options['handler'])) { 698 | $handler = $options['handler']; 699 | } 700 | 701 | if ($handler === null) { 702 | $handler = \GuzzleHttp\HandlerStack::create(); 703 | } 704 | 705 | if (!$this->verboseHandler) { 706 | $this->verboseHandler = new VerboseHandler(array_shift($fp), array_shift($fp)); 707 | } 708 | 709 | $handler->after('allow_redirects', $this->verboseHandler, 'verbose'); 710 | } catch (\InvalidArgumentException $e) { 711 | $handler->push($this->verboseHandler, 'verbose'); 712 | } 713 | 714 | $options['handler'] = $handler; 715 | 716 | return $options; 717 | } 718 | 719 | /** 720 | * Set request specific options 721 | * 722 | * @param array $options 723 | * @return array 724 | */ 725 | protected function setRequestOptions(array $options) 726 | { 727 | if (isset($options['timestamp'])) { 728 | $this->authentication->setTimestamp($options['timestamp']); 729 | } elseif (!$this->getConfig('timestamp')) { 730 | $this->authentication->setTimestamp(); 731 | } 732 | 733 | if (isset($options['nonce'])) { 734 | $this->authentication->setNonce($options['nonce']); 735 | } 736 | 737 | if (isset($options['handler'])) { 738 | $options = $this->setAuthenticationHandler($options, $this->authentication); 739 | } 740 | 741 | if ($fp = $this->isVerbose()) { 742 | $options = $this->setVerboseHandler($options, $fp); 743 | } 744 | 745 | $options['debug'] = $this->getDebugOption($options); 746 | if ($fp = $this->isDebug()) { 747 | $options = $this->setDebugHandler($options, $fp); 748 | } 749 | 750 | if ($this->logger && isset($options['handler'])) { 751 | 752 | $formatter = new \GuzzleHttp\MessageFormatter($this->messageFormat); 753 | $handler = \GuzzleHttp\Middleware::log($this->logger, $formatter); 754 | 755 | $this->setLogHandler($options['handler'], $handler); 756 | return $options; 757 | } 758 | 759 | return $options; 760 | } 761 | 762 | public function setMessageFormat(string $format): void 763 | { 764 | $this->messageFormat = $format; 765 | } 766 | } 767 | -------------------------------------------------------------------------------- /tests/ClientTest.php: -------------------------------------------------------------------------------- 1 | prophet = new \Prophecy\Prophet(); 28 | } 29 | 30 | protected function tearDown(): void 31 | { 32 | $this->prophet->checkPredictions(); 33 | parent::tearDown(); 34 | } 35 | 36 | /** 37 | * @param $name 38 | * @param $options 39 | * @param $request 40 | * @param $result 41 | * @dataProvider makeAuthHeaderProvider 42 | */ 43 | public function testMakeAuthHeader($name, $options, $request, $result) 44 | { 45 | //$this->setName($name); 46 | 47 | // Mock the response, we don't care about it 48 | $container = []; 49 | $handler = $this->getMockHandler([new Response(200)], $container); 50 | 51 | $timestamp = $this->prophet->prophesize(\Akamai\Open\EdgeGrid\Authentication\Timestamp::class); 52 | $timestamp->__toString()->willReturn($options['timestamp']); 53 | $timestamp->isValid()->willReturn(true); 54 | $nonce = $this->prophet->prophesize(\Akamai\Open\EdgeGrid\Authentication\Nonce::class); 55 | $nonce->__toString()->willReturn($options['nonce']); 56 | 57 | $client = new Client( 58 | array_merge($options, [ 59 | 'base_uri' => $options['base_url'], 60 | 'handler' => $handler, 61 | 'timestamp' => $timestamp->reveal(), 62 | 'nonce' => $nonce->reveal() 63 | ]) 64 | ); 65 | 66 | $client->setAuth($options['client_token'], $options['client_secret'], $options['access_token']); 67 | $client->setMaxBodySize($options['max_body']); 68 | 69 | if (isset($options['headers_to_sign'])) { 70 | $client->setHeadersToSign($options['headers_to_sign']); 71 | } 72 | 73 | $headers = array(); 74 | if (isset($request['headers'])) { 75 | array_walk_recursive($request['headers'], function ($value, $key) use (&$headers) { 76 | $headers[$key] = $value; 77 | }); 78 | } 79 | 80 | $client->request( 81 | $request['method'], 82 | $request['path'], 83 | [ 84 | 'headers' => $headers, 85 | 'body' => $request['data'], 86 | ] 87 | ); 88 | 89 | $this->assertCount(1, $container); 90 | $request = $container[0]['request']; 91 | $headers = $request->getHeaders(); 92 | 93 | $this->assertArrayHasKey('Authorization', $headers); 94 | $this->assertCount(1, $headers['Authorization']); 95 | $this->assertEquals($result, $headers['Authorization'][0]); 96 | } 97 | 98 | /** 99 | * @param $name 100 | * @param $options 101 | * @param $request 102 | * @param $result 103 | * @dataProvider makeAuthHeaderProvider 104 | */ 105 | public function testMakeAuthHeaderPsr7($name, $options, $request, $result) 106 | { 107 | //$this->setName($name); 108 | 109 | // Mock the response, we don't care about it 110 | $container = []; 111 | $handler = $this->getMockHandler([new Response(200)], $container); 112 | 113 | $timestamp = $this->prophet->prophesize(\Akamai\Open\EdgeGrid\Authentication\Timestamp::class); 114 | $timestamp->__toString()->willReturn($options['timestamp']); 115 | $timestamp->isValid()->willReturn(true); 116 | $nonce = $this->prophet->prophesize(\Akamai\Open\EdgeGrid\Authentication\Nonce::class); 117 | $nonce->__toString()->willReturn($options['nonce']); 118 | 119 | $client = new Client( 120 | array_merge($options, [ 121 | 'base_uri' => $options['base_url'], 122 | 'handler' => $handler, 123 | 'timestamp' => $timestamp->reveal(), 124 | 'nonce' => $nonce->reveal() 125 | ]) 126 | ); 127 | 128 | $client->setAuth($options['client_token'], $options['client_secret'], $options['access_token']); 129 | $client->setMaxBodySize($options['max_body']); 130 | 131 | if (isset($options['headers_to_sign'])) { 132 | $client->setHeadersToSign($options['headers_to_sign']); 133 | } 134 | 135 | $headers = array(); 136 | if (isset($request['headers'])) { 137 | array_walk_recursive($request['headers'], function ($value, $key) use (&$headers) { 138 | $headers[$key] = $value; 139 | }); 140 | } 141 | 142 | $request = new \GuzzleHttp\Psr7\Request( 143 | $request['method'], 144 | $request['path'], 145 | $headers, 146 | $request['data'] 147 | ); 148 | 149 | $client->send($request); 150 | 151 | $this->assertCount(1, $container); 152 | $request = $container[0]['request']; 153 | $headers = $request->getHeaders(); 154 | 155 | $this->assertArrayHasKey('Authorization', $headers); 156 | $this->assertCount(1, $headers['Authorization']); 157 | $this->assertEquals($result, $headers['Authorization'][0]); 158 | } 159 | 160 | /** 161 | * @backupGlobals enabled 162 | * @dataProvider createFromEdgeRcProvider 163 | * @param $section 164 | * @param $file 165 | */ 166 | public function testCreateFromEdgeRcDefault($section, $file) 167 | { 168 | $_SERVER['HOME'] = __DIR__ . '/edgerc'; 169 | $client = \Akamai\Open\EdgeGrid\Client::createFromEdgeRcFile($section, $file); 170 | $clientReflector = new \ReflectionClass($client); 171 | 172 | $reflectedAuthentication = $clientReflector->getProperty('authentication'); 173 | $reflectedAuthentication->setAccessible(true); 174 | $authentication = $reflectedAuthentication->getValue($client); 175 | $authenticationReflector = new \ReflectionClass($authentication); 176 | 177 | $reflectedAuth = $authenticationReflector->getProperty('auth'); 178 | $reflectedAuth->setAccessible(true); 179 | 180 | $reflectedMaxBodySize = $authenticationReflector->getProperty('max_body_size'); 181 | $reflectedMaxBodySize->setAccessible(true); 182 | 183 | $this->assertInstanceOf(\Akamai\Open\EdgeGrid\Client::class, $client); 184 | $this->assertEquals( 185 | [ 186 | 'client_token' => 'akab-client-token-xxx-xxxxxxxxxxxxxxxx', 187 | 'client_secret' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=', 188 | 'access_token' => 'akab-access-token-xxx-xxxxxxxxxxxxxxxx' 189 | ], 190 | $reflectedAuth->getValue($authentication) 191 | ); 192 | $this->assertEquals( 193 | 'https://akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net', 194 | $client->getConfig('base_uri') 195 | ); 196 | $this->assertEquals(2048, $reflectedMaxBodySize->getValue($authentication)); 197 | } 198 | 199 | /** 200 | * @backupGlobals enabled 201 | */ 202 | public function testCreateFromEnvNoSection() 203 | { 204 | $_ENV['AKAMAI_HOST'] = 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net'; 205 | $_ENV['AKAMAI_CLIENT_TOKEN'] = 'akab-client-token-xxx-xxxxxxxxxxxxxxxx'; 206 | $_ENV['AKAMAI_CLIENT_SECRET'] = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx='; 207 | $_ENV['AKAMAI_ACCESS_TOKEN'] = 'akab-access-token-xxx-xxxxxxxxxxxxxxxx'; 208 | $_ENV['AKAMAI_MAX_SIZE'] = 2048; 209 | 210 | $client = \Akamai\Open\EdgeGrid\Client::createFromEnv(); 211 | $clientReflector = new \ReflectionClass($client); 212 | 213 | $reflectedAuthentication = $clientReflector->getProperty('authentication'); 214 | $reflectedAuthentication->setAccessible(true); 215 | $authentication = $reflectedAuthentication->getValue($client); 216 | $authenticationReflector = new \ReflectionClass($authentication); 217 | 218 | $reflectedAuth = $authenticationReflector->getProperty('auth'); 219 | $reflectedAuth->setAccessible(true); 220 | 221 | $reflectedMaxBodySize = $authenticationReflector->getProperty('max_body_size'); 222 | $reflectedMaxBodySize->setAccessible(true); 223 | 224 | $this->assertInstanceOf(\Akamai\Open\EdgeGrid\Client::class, $client); 225 | $this->assertInstanceOf(\Akamai\Open\EdgeGrid\Authentication::class, $authentication); 226 | 227 | $this->assertEquals( 228 | array( 229 | 'client_token' => 'akab-client-token-xxx-xxxxxxxxxxxxxxxx', 230 | 'client_secret' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=', 231 | 'access_token' => 'akab-access-token-xxx-xxxxxxxxxxxxxxxx' 232 | ), 233 | $reflectedAuth->getValue($authentication) 234 | ); 235 | 236 | /** @var \GuzzleHttp\Psr7\Uri $base_uri */ 237 | $base_uri = $client->getConfig('base_uri'); 238 | 239 | $this->assertEquals( 240 | 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net', 241 | $base_uri->getHost() 242 | ); 243 | $this->assertEquals(2048, $reflectedMaxBodySize->getValue($authentication)); 244 | } 245 | 246 | /** 247 | * @backupGlobals enabled 248 | */ 249 | public function testCreateFromEnvDefaultSection() 250 | { 251 | $_ENV['AKAMAI_DEFAULT_HOST'] = 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net'; 252 | $_ENV['AKAMAI_DEFAULT_CLIENT_TOKEN'] = 'akab-client-token-xxx-xxxxxxxxxxxxxxxx'; 253 | $_ENV['AKAMAI_DEFAULT_CLIENT_SECRET'] = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx='; 254 | $_ENV['AKAMAI_DEFAULT_ACCESS_TOKEN'] = 'akab-access-token-xxx-xxxxxxxxxxxxxxxx'; 255 | $_ENV['AKAMAI_DEFAULT_MAX_SIZE'] = 2048; 256 | 257 | $client = \Akamai\Open\EdgeGrid\Client::createFromEnv(); 258 | $clientReflector = new \ReflectionClass($client); 259 | 260 | $reflectedAuthentication = $clientReflector->getProperty('authentication'); 261 | $reflectedAuthentication->setAccessible(true); 262 | $authentication = $reflectedAuthentication->getValue($client); 263 | $authenticationReflector = new \ReflectionClass($authentication); 264 | 265 | $reflectedAuth = $authenticationReflector->getProperty('auth'); 266 | $reflectedAuth->setAccessible(true); 267 | 268 | $reflectedMaxBodySize = $authenticationReflector->getProperty('max_body_size'); 269 | $reflectedMaxBodySize->setAccessible(true); 270 | 271 | $this->assertInstanceOf(\Akamai\Open\EdgeGrid\Client::class, $client); 272 | $this->assertInstanceOf(\Akamai\Open\EdgeGrid\Authentication::class, $authentication); 273 | 274 | $this->assertEquals( 275 | array( 276 | 'client_token' => 'akab-client-token-xxx-xxxxxxxxxxxxxxxx', 277 | 'client_secret' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=', 278 | 'access_token' => 'akab-access-token-xxx-xxxxxxxxxxxxxxxx' 279 | ), 280 | $reflectedAuth->getValue($authentication) 281 | ); 282 | 283 | /** @var \GuzzleHttp\Psr7\Uri $base_uri */ 284 | $base_uri = $client->getConfig('base_uri'); 285 | 286 | $this->assertEquals( 287 | 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net', 288 | $base_uri->getHost() 289 | ); 290 | $this->assertEquals(2048, $reflectedMaxBodySize->getValue($authentication)); 291 | } 292 | 293 | /** 294 | * @backupGlobals enabled 295 | */ 296 | public function testCreateFromEnvPreferSection() 297 | { 298 | $_ENV['AKAMAI_HOST'] = false; 299 | $_ENV['AKAMAI_CLIENT_TOKEN'] = false; 300 | $_ENV['AKAMAI_CLIENT_SECRET'] = false; 301 | $_ENV['AKAMAI_ACCESS_TOKEN'] = false; 302 | $_ENV['AKAMAI_MAX_SIZE'] = 0; 303 | 304 | $_ENV['AKAMAI_TESTING_HOST'] = 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net'; 305 | $_ENV['AKAMAI_TESTING_CLIENT_TOKEN'] = 'akab-client-token-xxx-xxxxxxxxxxxxxxxx'; 306 | $_ENV['AKAMAI_TESTING_CLIENT_SECRET'] = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx='; 307 | $_ENV['AKAMAI_TESTING_ACCESS_TOKEN'] = 'akab-access-token-xxx-xxxxxxxxxxxxxxxx'; 308 | $_ENV['AKAMAI_TESTING_MAX_SIZE'] = 2048; 309 | 310 | $client = \Akamai\Open\EdgeGrid\Client::createFromEnv('testing'); 311 | $clientReflector = new \ReflectionClass($client); 312 | 313 | $reflectedAuthentication = $clientReflector->getProperty('authentication'); 314 | $reflectedAuthentication->setAccessible(true); 315 | $authentication = $reflectedAuthentication->getValue($client); 316 | $authenticationReflector = new \ReflectionClass($authentication); 317 | 318 | $reflectedAuth = $authenticationReflector->getProperty('auth'); 319 | $reflectedAuth->setAccessible(true); 320 | 321 | $reflectedMaxBodySize = $authenticationReflector->getProperty('max_body_size'); 322 | $reflectedMaxBodySize->setAccessible(true); 323 | 324 | $this->assertInstanceOf(\Akamai\Open\EdgeGrid\Client::class, $client); 325 | $this->assertInstanceOf(\Akamai\Open\EdgeGrid\Authentication::class, $authentication); 326 | 327 | $this->assertEquals( 328 | array( 329 | 'client_token' => 'akab-client-token-xxx-xxxxxxxxxxxxxxxx', 330 | 'client_secret' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=', 331 | 'access_token' => 'akab-access-token-xxx-xxxxxxxxxxxxxxxx' 332 | ), 333 | $reflectedAuth->getValue($authentication) 334 | ); 335 | 336 | /** @var \GuzzleHttp\Psr7\Uri $base_uri */ 337 | $base_uri = $client->getConfig('base_uri'); 338 | 339 | $this->assertEquals( 340 | 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net', 341 | $base_uri->getHost() 342 | ); 343 | $this->assertEquals(2048, $reflectedMaxBodySize->getValue($authentication)); 344 | } 345 | 346 | /** 347 | * @backupGlobals enabled 348 | */ 349 | public function testCreateFromEnvNoMaxSize() 350 | { 351 | $_ENV['AKAMAI_HOST'] = 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net'; 352 | $_ENV['AKAMAI_CLIENT_TOKEN'] = 'akab-client-token-xxx-xxxxxxxxxxxxxxxx'; 353 | $_ENV['AKAMAI_CLIENT_SECRET'] = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx='; 354 | $_ENV['AKAMAI_ACCESS_TOKEN'] = 'akab-access-token-xxx-xxxxxxxxxxxxxxxx'; 355 | 356 | $client = \Akamai\Open\EdgeGrid\Client::createFromEnv('testing'); 357 | $clientReflector = new \ReflectionClass($client); 358 | 359 | $reflectedAuthentication = $clientReflector->getProperty('authentication'); 360 | $reflectedAuthentication->setAccessible(true); 361 | $authentication = $reflectedAuthentication->getValue($client); 362 | $authenticationReflector = new \ReflectionClass($authentication); 363 | 364 | $reflectedAuth = $authenticationReflector->getProperty('auth'); 365 | $reflectedAuth->setAccessible(true); 366 | 367 | $reflectedMaxBodySize = $authenticationReflector->getProperty('max_body_size'); 368 | $reflectedMaxBodySize->setAccessible(true); 369 | 370 | $this->assertInstanceOf(\Akamai\Open\EdgeGrid\Client::class, $client); 371 | $this->assertInstanceOf(\Akamai\Open\EdgeGrid\Authentication::class, $authentication); 372 | 373 | $this->assertEquals( 374 | array( 375 | 'client_token' => 'akab-client-token-xxx-xxxxxxxxxxxxxxxx', 376 | 'client_secret' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=', 377 | 'access_token' => 'akab-access-token-xxx-xxxxxxxxxxxxxxxx' 378 | ), 379 | $reflectedAuth->getValue($authentication) 380 | ); 381 | $this->assertEquals( 382 | 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net', 383 | $authentication->getHost() 384 | ); 385 | $this->assertEquals(131072, $reflectedMaxBodySize->getValue($authentication)); 386 | } 387 | 388 | public function testCreateFromEnvInvalid() 389 | { 390 | $this->expectException(\Akamai\Open\EdgeGrid\Authentication\Exception\ConfigException::class); 391 | $this->expectExceptionMessage('Environment variables AKAMAI_HOST or AKAMAI_DEFAULT_HOST do not exist'); 392 | $client = \Akamai\Open\EdgeGrid\Client::createFromEnv(); 393 | } 394 | 395 | public function testCreateFromEnvInvalidSection() 396 | { 397 | $this->expectException(\Akamai\Open\EdgeGrid\Authentication\Exception\ConfigException::class); 398 | $this->expectExceptionMessage('Environment variable AKAMAI_TESTING_HOST does not exist'); 399 | $client = \Akamai\Open\EdgeGrid\Client::createFromEnv('testing'); 400 | } 401 | 402 | /** 403 | * @backupGlobals enabled 404 | */ 405 | public function testCreateInstancePreferEnv() 406 | { 407 | $_ENV['AKAMAI_HOST'] = 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net'; 408 | $_ENV['AKAMAI_CLIENT_TOKEN'] = 'akab-client-token-xxx-xxxxxxxxxxxxxxxx'; 409 | $_ENV['AKAMAI_CLIENT_SECRET'] = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx='; 410 | $_ENV['AKAMAI_ACCESS_TOKEN'] = 'akab-access-token-xxx-xxxxxxxxxxxxxxxx'; 411 | $_ENV['AKAMAI_MAX_SIZE'] = 2048; 412 | 413 | $client = \Akamai\Open\EdgeGrid\Client::createInstance( 414 | 'default', 415 | __DIR__ . '/edgerc/.edgerc.default-testing' 416 | ); 417 | $clientReflector = new \ReflectionClass($client); 418 | 419 | $reflectedAuthentication = $clientReflector->getProperty('authentication'); 420 | $reflectedAuthentication->setAccessible(true); 421 | $authentication = $reflectedAuthentication->getValue($client); 422 | $authenticationReflector = new \ReflectionClass($authentication); 423 | 424 | $reflectedAuth = $authenticationReflector->getProperty('auth'); 425 | $reflectedAuth->setAccessible(true); 426 | 427 | $reflectedMaxBodySize = $authenticationReflector->getProperty('max_body_size'); 428 | $reflectedMaxBodySize->setAccessible(true); 429 | 430 | $this->assertInstanceOf(\Akamai\Open\EdgeGrid\Client::class, $client); 431 | $this->assertInstanceOf(\Akamai\Open\EdgeGrid\Authentication::class, $authentication); 432 | 433 | $this->assertEquals( 434 | array( 435 | 'client_token' => 'akab-client-token-xxx-xxxxxxxxxxxxxxxx', 436 | 'client_secret' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=', 437 | 'access_token' => 'akab-access-token-xxx-xxxxxxxxxxxxxxxx' 438 | ), 439 | $reflectedAuth->getValue($authentication) 440 | ); 441 | $this->assertEquals( 442 | 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net', 443 | $authentication->getHost() 444 | ); 445 | $this->assertEquals(2048, $reflectedMaxBodySize->getValue($authentication)); 446 | } 447 | 448 | public function testCreateInstanceFallbackEdgeRc() 449 | { 450 | $authentication = \Akamai\Open\EdgeGrid\Authentication::createInstance('default', __DIR__ . '/edgerc/.edgerc'); 451 | 452 | $reflector = new \ReflectionClass($authentication); 453 | $reflectedAuth = $reflector->getProperty('auth'); 454 | $reflectedMaxBodySize = $reflector->getProperty('max_body_size'); 455 | $reflectedAuth->setAccessible(true); 456 | $reflectedMaxBodySize->setAccessible(true); 457 | 458 | $this->assertInstanceOf('\Akamai\Open\EdgeGrid\Authentication', $authentication); 459 | $this->assertEquals( 460 | array( 461 | 'client_token' => 'akab-client-token-xxx-xxxxxxxxxxxxxxxx', 462 | 'client_secret' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=', 463 | 'access_token' => 'akab-access-token-xxx-xxxxxxxxxxxxxxxx' 464 | ), 465 | $reflectedAuth->getValue($authentication) 466 | ); 467 | $this->assertEquals( 468 | 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net', 469 | $authentication->getHost() 470 | ); 471 | $this->assertEquals(2048, $reflectedMaxBodySize->getValue($authentication)); 472 | } 473 | 474 | /** 475 | * @backupGlobals enabled 476 | */ 477 | public function testCreateInstanceSection() 478 | { 479 | $_ENV['AKAMAI_TESTING_HOST'] = 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net'; 480 | $_ENV['AKAMAI_TESTING_CLIENT_TOKEN'] = 'akab-client-token-xxx-xxxxxxxxxxxxxxxx'; 481 | $_ENV['AKAMAI_TESTING_CLIENT_SECRET'] = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx='; 482 | $_ENV['AKAMAI_TESTING_ACCESS_TOKEN'] = 'akab-access-token-xxx-xxxxxxxxxxxxxxxx'; 483 | $_ENV['AKAMAI_TESTING_MAX_SIZE'] = 2048; 484 | 485 | $authentication = \Akamai\Open\EdgeGrid\Authentication::createInstance('testing', __DIR__ . '/edgerc/.edgerc'); 486 | 487 | $reflector = new \ReflectionClass($authentication); 488 | $reflectedAuth = $reflector->getProperty('auth'); 489 | $reflectedMaxBodySize = $reflector->getProperty('max_body_size'); 490 | $reflectedAuth->setAccessible(true); 491 | $reflectedMaxBodySize->setAccessible(true); 492 | 493 | $this->assertInstanceOf('\Akamai\Open\EdgeGrid\Authentication', $authentication); 494 | $this->assertEquals( 495 | array( 496 | 'client_token' => 'akab-client-token-xxx-xxxxxxxxxxxxxxxx', 497 | 'client_secret' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=', 498 | 'access_token' => 'akab-access-token-xxx-xxxxxxxxxxxxxxxx' 499 | ), 500 | $reflectedAuth->getValue($authentication) 501 | ); 502 | $this->assertEquals( 503 | 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net', 504 | $authentication->getHost() 505 | ); 506 | $this->assertEquals(2048, $reflectedMaxBodySize->getValue($authentication)); 507 | } 508 | 509 | /** 510 | * @backupGlobals enabled 511 | */ 512 | public function testCreateInstanceSectionFallback() 513 | { 514 | $_ENV['AKAMAI_HOST'] = false; 515 | $_ENV['AKAMAI_CLIENT_TOKEN'] = false; 516 | $_ENV['AKAMAI_CLIENT_SECRET'] = false; 517 | $_ENV['AKAMAI_ACCESS_TOKEN'] = false; 518 | $_ENV['AKAMAI_MAX_SIZE'] = 0; 519 | 520 | $authentication = \Akamai\Open\EdgeGrid\Authentication::createInstance( 521 | 'testing', 522 | __DIR__ . '/edgerc/.edgerc.testing' 523 | ); 524 | 525 | $reflector = new \ReflectionClass($authentication); 526 | $reflectedAuth = $reflector->getProperty('auth'); 527 | $reflectedMaxBodySize = $reflector->getProperty('max_body_size'); 528 | $reflectedAuth->setAccessible(true); 529 | $reflectedMaxBodySize->setAccessible(true); 530 | 531 | $this->assertInstanceOf('\Akamai\Open\EdgeGrid\Authentication', $authentication); 532 | $this->assertEquals( 533 | array( 534 | 'client_token' => 'akab-client-token-xxx-xxxxxxxxxxxxxxxx', 535 | 'client_secret' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=', 536 | 'access_token' => 'akab-access-token-xxx-xxxxxxxxxxxxxxxx' 537 | ), 538 | $reflectedAuth->getValue($authentication) 539 | ); 540 | $this->assertEquals( 541 | 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net', 542 | $authentication->getHost() 543 | ); 544 | $this->assertEquals(2048, $reflectedMaxBodySize->getValue($authentication)); 545 | } 546 | 547 | /** 548 | * @backupGlobals enabled 549 | */ 550 | public function testCreateInstanceSectionFallbackEnv() 551 | { 552 | $_ENV['AKAMAI_HOST'] = 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net'; 553 | $_ENV['AKAMAI_CLIENT_TOKEN'] = 'akab-client-token-xxx-xxxxxxxxxxxxxxxx'; 554 | $_ENV['AKAMAI_CLIENT_SECRET'] = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx='; 555 | $_ENV['AKAMAI_ACCESS_TOKEN'] = 'akab-access-token-xxx-xxxxxxxxxxxxxxxx'; 556 | $_ENV['AKAMAI_MAX_SIZE'] = 2048; 557 | 558 | $authentication = \Akamai\Open\EdgeGrid\Authentication::createInstance('testing', __DIR__ . '/edgerc/.edgerc'); 559 | 560 | $reflector = new \ReflectionClass($authentication); 561 | $reflectedAuth = $reflector->getProperty('auth'); 562 | $reflectedMaxBodySize = $reflector->getProperty('max_body_size'); 563 | $reflectedAuth->setAccessible(true); 564 | $reflectedMaxBodySize->setAccessible(true); 565 | 566 | $this->assertInstanceOf('\Akamai\Open\EdgeGrid\Authentication', $authentication); 567 | $this->assertEquals( 568 | array( 569 | 'client_token' => 'akab-client-token-xxx-xxxxxxxxxxxxxxxx', 570 | 'client_secret' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=', 571 | 'access_token' => 'akab-access-token-xxx-xxxxxxxxxxxxxxxx' 572 | ), 573 | $reflectedAuth->getValue($authentication) 574 | ); 575 | $this->assertEquals( 576 | 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net', 577 | $authentication->getHost() 578 | ); 579 | $this->assertEquals(2048, $reflectedMaxBodySize->getValue($authentication)); 580 | } 581 | 582 | /** 583 | * @backupGlobals enabled 584 | */ 585 | public function testCreateInstanceSectionFallbackInvalidEdgerc() 586 | { 587 | $_ENV['AKAMAI_HOST'] = 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net'; 588 | $_ENV['AKAMAI_CLIENT_TOKEN'] = 'akab-client-token-xxx-xxxxxxxxxxxxxxxx'; 589 | $_ENV['AKAMAI_CLIENT_SECRET'] = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx='; 590 | $_ENV['AKAMAI_ACCESS_TOKEN'] = 'akab-access-token-xxx-xxxxxxxxxxxxxxxx'; 591 | $_ENV['AKAMAI_MAX_SIZE'] = 2048; 592 | 593 | $authentication = \Akamai\Open\EdgeGrid\Authentication::createInstance( 594 | 'testing', 595 | __DIR__ . '/edgerc/.edgerc.invalid' 596 | ); 597 | 598 | $reflector = new \ReflectionClass($authentication); 599 | $reflectedAuth = $reflector->getProperty('auth'); 600 | $reflectedMaxBodySize = $reflector->getProperty('max_body_size'); 601 | $reflectedAuth->setAccessible(true); 602 | $reflectedMaxBodySize->setAccessible(true); 603 | 604 | $this->assertInstanceOf('\Akamai\Open\EdgeGrid\Authentication', $authentication); 605 | $this->assertEquals( 606 | array( 607 | 'client_token' => 'akab-client-token-xxx-xxxxxxxxxxxxxxxx', 608 | 'client_secret' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=', 609 | 'access_token' => 'akab-access-token-xxx-xxxxxxxxxxxxxxxx' 610 | ), 611 | $reflectedAuth->getValue($authentication) 612 | ); 613 | $this->assertEquals( 614 | 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net', 615 | $authentication->getHost() 616 | ); 617 | $this->assertEquals(2048, $reflectedMaxBodySize->getValue($authentication)); 618 | } 619 | 620 | public function testCreateInstanceSectionFallbackInvalidEdgercNoEnv() 621 | { 622 | $this->expectException(\Akamai\Open\EdgeGrid\Authentication\Exception\ConfigException::class); 623 | $this->expectExceptionMessage('Unable to create instance using environment or .edgerc file'); 624 | 625 | try { 626 | $client = \Akamai\Open\EdgeGrid\Client::createInstance( 627 | 'testing', 628 | __DIR__ . '/edgerc/.edgerc.invalid' 629 | ); 630 | } catch (\Akamai\Open\EdgeGrid\Authentication\Exception\ConfigException $e) { 631 | $this->assertInstanceOf( 632 | '\Akamai\Open\EdgeGrid\Authentication\Exception\ConfigException', 633 | $e->getPrevious() 634 | ); 635 | 636 | $this->assertEquals("Section \"testing\" does not exist!", $e->getPrevious()->getMessage()); 637 | 638 | throw $e; 639 | } 640 | } 641 | 642 | /** 643 | * @backupGlobals enabled 644 | */ 645 | public function testCreateInstanceInvalidEdgercInvalidEnv() 646 | { 647 | $this->expectException(\Akamai\Open\EdgeGrid\Authentication\Exception\ConfigException::class); 648 | $this->expectExceptionMessage('Unable to create instance using environment or .edgerc file'); 649 | 650 | $_ENV['AKAMAI_TESTING_HOST'] = 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net'; 651 | 652 | try { 653 | $client = \Akamai\Open\EdgeGrid\Client::createInstance("testing", __DIR__ . '/edgerc/.edgerc'); 654 | } catch (\Akamai\Open\EdgeGrid\Authentication\Exception\ConfigException $e) { 655 | } 656 | 657 | $this->assertTrue(isset($e), 'Exception not thrown'); 658 | 659 | $this->assertInstanceOf( 660 | \Akamai\Open\EdgeGrid\Authentication\Exception\ConfigException::class, 661 | $e->getPrevious() 662 | ); 663 | 664 | $this->assertEquals('Section "testing" does not exist!', $e->getPrevious()->getMessage()); 665 | 666 | throw $e; 667 | } 668 | 669 | /** 670 | * @backupGlobals enabled 671 | */ 672 | public function testCreateInstanceInvalidEdgercInvalidEnvSection() 673 | { 674 | $this->expectException(\Akamai\Open\EdgeGrid\Authentication\Exception\ConfigException::class); 675 | $this->expectExceptionMessage('Unable to create instance using environment or .edgerc file'); 676 | 677 | $_ENV['AKAMAI_TESTING_HOST'] = 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net'; 678 | 679 | try { 680 | $authentication = \Akamai\Open\EdgeGrid\Client::createInstance( 681 | 'testing', 682 | __DIR__ . '/edgerc/.edgerc' 683 | ); 684 | } catch (\Akamai\Open\EdgeGrid\Authentication\Exception\ConfigException $e) { 685 | $this->assertInstanceOf( 686 | '\Akamai\Open\EdgeGrid\Authentication\Exception\ConfigException', 687 | $e->getPrevious() 688 | ); 689 | 690 | $this->assertEquals( 691 | 'Section "testing" does not exist!', 692 | $e->getPrevious()->getMessage() 693 | ); 694 | 695 | throw $e; 696 | } 697 | } 698 | 699 | /** 700 | * @backupGlobals enabled 701 | */ 702 | public function testCreateInstanceInvalidEdgercInvalidEnvSectionInvalidDefaultEnv() 703 | { 704 | $this->expectException(\Akamai\Open\EdgeGrid\Authentication\Exception\ConfigException::class); 705 | $this->expectExceptionMessage('Unable to create instance using environment or .edgerc file'); 706 | 707 | $_ENV['AKAMAI_HOST'] = 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net'; 708 | 709 | try { 710 | $authentication = \Akamai\Open\EdgeGrid\Client::createInstance( 711 | 'testing', 712 | __DIR__ . '/edgerc/.edgerc' 713 | ); 714 | } catch (\Akamai\Open\EdgeGrid\Authentication\Exception\ConfigException $e) { 715 | $this->assertInstanceOf( 716 | '\Akamai\Open\EdgeGrid\Authentication\Exception\ConfigException', 717 | $e->getPrevious() 718 | ); 719 | 720 | $this->assertEquals( 721 | 'Environment variables AKAMAI_CLIENT_TOKEN or AKAMAI_DEFAULT_CLIENT_TOKEN do not exist', 722 | $e->getPrevious()->getMessage() 723 | ); 724 | 725 | throw $e; 726 | } 727 | } 728 | 729 | public function testHostnameWithTrailingSlash() 730 | { 731 | $client = new \Akamai\Open\EdgeGrid\Client(); 732 | $client->setHost('akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net/'); 733 | 734 | $this->assertEquals( 735 | 'akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net', 736 | $client->getConfig('headers')['Host'] 737 | ); 738 | } 739 | 740 | public function testDefaultTimeout() 741 | { 742 | // Mock the response, we don't care about it 743 | $container = []; 744 | $handler = $this->getMockHandler([new Response(200)], $container); 745 | 746 | $client = new Client( 747 | [ 748 | 'base_uri' => 'http://example.org', 749 | 'handler' => $handler, 750 | ] 751 | ); 752 | 753 | $this->assertArrayHasKey('timeout', $client->getConfig()); 754 | $this->assertEquals(Client::DEFAULT_REQUEST_TIMEOUT, $client->getConfig('timeout')); 755 | 756 | $client->setAuth('test', 'test', 'test'); 757 | 758 | $client->get('/test'); 759 | $this->assertEquals(Client::DEFAULT_REQUEST_TIMEOUT, $container[0]['options']['timeout']); 760 | } 761 | 762 | public function testTimeoutOption() 763 | { 764 | // Mock the response, we don't care about it 765 | $container = []; 766 | $handler = $this->getMockHandler([new Response(200), new Response(200)], $container); 767 | 768 | $client = new Client( 769 | [ 770 | 'base_uri' => 'http://example.org', 771 | 'handler' => $handler, 772 | 'timeout' => 2 773 | ] 774 | ); 775 | 776 | $this->assertArrayHasKey('timeout', $client->getConfig()); 777 | $this->assertEquals(2, $client->getConfig('timeout')); 778 | 779 | $client->setAuth('test', 'test', 'test'); 780 | 781 | $client->get('/test'); 782 | $this->assertEquals(2, end($container)['options']['timeout']); 783 | 784 | $client->get('/test', ['timeout' => 5]); 785 | $this->assertEquals(5, end($container)['options']['timeout']); 786 | } 787 | 788 | public function testSetTimeout() 789 | { 790 | // Mock the response, we don't care about it 791 | $container = []; 792 | $handler = $this->getMockHandler([new Response(200), new Response(200), new Response(200)], $container); 793 | $client = new Client( 794 | [ 795 | 'base_uri' => 'http://example.org', 796 | 'handler' => $handler, 797 | ] 798 | ); 799 | 800 | $this->assertArrayHasKey('timeout', $client->getConfig()); 801 | $this->assertEquals(Client::DEFAULT_REQUEST_TIMEOUT, $client->getConfig('timeout')); 802 | 803 | $client->get('/test', ['timeout' => 2]); 804 | $this->assertEquals(2, end($container)['options']['timeout']); 805 | $this->assertEquals(Client::DEFAULT_REQUEST_TIMEOUT, $client->getConfig('timeout')); 806 | 807 | $client->setTimeout(5); 808 | $client->get('/test'); 809 | 810 | $this->assertEquals(5, $client->getConfig('timeout')); 811 | $this->assertEquals(5, end($container)['options']['timeout']); 812 | } 813 | 814 | public function testStaticDebugSingle() 815 | { 816 | $container = []; 817 | $handler = $this->getMockHandler([new Response(200)], $container); 818 | $client = new Client( 819 | [ 820 | 'base_uri' => 'http://example.org', 821 | 'handler' => $handler, 822 | ] 823 | ); 824 | 825 | Client::setDebug(true); 826 | 827 | $client->get('/test'); 828 | $this->assertEquals(true, is_resource(end($container)['options']['debug'])); 829 | } 830 | 831 | public function testInstanceDebugSingle() 832 | { 833 | $container = []; 834 | $handler = $this->getMockHandler([new Response(200)], $container); 835 | $client = new Client( 836 | [ 837 | 'base_uri' => 'http://example.org', 838 | 'handler' => $handler, 839 | ] 840 | ); 841 | $client->setAuth('test', 'test', 'test'); 842 | 843 | $client->setInstanceDebug(true); 844 | $client->get('/test'); 845 | $this->assertEquals(true, is_resource(end($container)['options']['debug'])); 846 | } 847 | 848 | public function testDebugOverrideSingle() 849 | { 850 | $container = []; 851 | $handler = $this->getMockHandler([new Response(200), new Response(200)], $container); 852 | $client = new Client( 853 | [ 854 | 'base_uri' => 'http://example.org', 855 | 'handler' => $handler, 856 | ] 857 | ); 858 | $client->setAuth('test', 'test', 'test'); 859 | 860 | Client::setDebug(true); 861 | $client->setInstanceDebug(false); 862 | 863 | $client->get('/test'); 864 | $this->assertEquals(false, is_resource(end($container)['options']['debug'])); 865 | } 866 | 867 | public function testInstanceDebugOptionSingle() 868 | { 869 | $container = []; 870 | $handler = $this->getMockHandler([new Response(200), new Response(200), new Response(200)], $container); 871 | $client = new Client( 872 | [ 873 | 'base_uri' => 'http://example.org', 874 | 'handler' => $handler, 875 | ] 876 | ); 877 | $client->setAuth('test', 'test', 'test'); 878 | 879 | $client->get('/test', ['debug' => true]); 880 | $this->assertEquals(true, is_resource(end($container)['options']['debug'])); 881 | 882 | $client = new Client( 883 | [ 884 | 'base_uri' => 'http://example.org', 885 | 'handler' => $handler, 886 | ] 887 | ); 888 | $client->setAuth('test', 'test', 'test'); 889 | $client->setInstanceDebug(true); 890 | $client->get('/test', ['debug' => false]); 891 | $this->assertEquals(false, is_resource(end($container)['options']['debug'])); 892 | $this->assertFalse(end($container)['options']['debug']); 893 | 894 | $client = new Client( 895 | [ 896 | 'base_uri' => 'http://example.org', 897 | 'handler' => $handler, 898 | ] 899 | ); 900 | $client->setAuth('test', 'test', 'test'); 901 | Client::setDebug(true); 902 | $client->get('/test', ['debug' => false]); 903 | $this->assertEquals(false, is_resource(end($container)['options']['debug'])); 904 | $this->assertFalse(end($container)['options']['debug']); 905 | } 906 | 907 | public function testNonApiCall() 908 | { 909 | $container = []; 910 | $handler = $this->getMockHandler([new Response(200), new Response(200), new Response(200)], $container); 911 | $client = new Client( 912 | [ 913 | 'base_uri' => 'http://example.org', 914 | 'handler' => $handler, 915 | ] 916 | ); 917 | 918 | $response = $client->get('/test'); 919 | $this->assertInstanceOf(\GuzzleHttp\Psr7\Response::class, $response); 920 | $this->assertEquals('http', end($container)['request']->getUri()->getScheme()); 921 | $this->assertEquals('example.org', end($container)['request']->getUri()->getHost()); 922 | $this->assertArrayNotHasKey('Authentication', end($container)['request']->getHeaders()); 923 | 924 | $response = $client->get('http://example.com/test'); 925 | $this->assertInstanceOf(\GuzzleHttp\Psr7\Response::class, $response); 926 | $this->assertEquals('http', end($container)['request']->getUri()->getScheme()); 927 | $this->assertEquals('example.com', end($container)['request']->getUri()->getHost()); 928 | $this->assertArrayNotHasKey('Authentication', end($container)['request']->getHeaders()); 929 | 930 | $response = $client->get('https://example.net/test'); 931 | $this->assertInstanceOf(\GuzzleHttp\Psr7\Response::class, $response); 932 | $this->assertEquals('https', end($container)['request']->getUri()->getScheme()); 933 | $this->assertEquals('example.net', end($container)['request']->getUri()->getHost()); 934 | $this->assertArrayNotHasKey('Authentication', end($container)['request']->getHeaders()); 935 | } 936 | 937 | public function testForceHttps() 938 | { 939 | $client = new Client( 940 | [ 941 | 'base_uri' => 'example.org' 942 | ] 943 | ); 944 | 945 | $uri = $client->getConfig('base_uri'); 946 | $this->assertEquals('https', parse_url($uri, PHP_URL_SCHEME)); 947 | } 948 | 949 | /** 950 | * @param $method 951 | * @param $responses 952 | * @param $path 953 | * @param $expected 954 | * @internal param $response 955 | * @dataProvider loggingProvider 956 | */ 957 | public function testLogging($method, $responses, $path, $expected) 958 | { 959 | $handler = $this->getMockHandler($responses); 960 | $client = new Client( 961 | [ 962 | 'base_uri' => 'http://example.org', 963 | 'handler' => $handler, 964 | ] 965 | ); 966 | 967 | $logHandler = $this->setLogger($client); 968 | 969 | try { 970 | $client->{$method}($path); 971 | } catch (\Exception $e) { 972 | } 973 | 974 | $records = []; 975 | foreach ($logHandler->getRecords() as $record) { 976 | $records[] = $record['message']; 977 | } 978 | $this->assertEquals($expected, $records); 979 | } 980 | 981 | public function testLoggingRedirect() 982 | { 983 | $handler = $this->getMockHandler([ 984 | new Response(301, ['Location' => '/redirected']), 985 | new Response(200, ['Content-Type' => 'application/json']) 986 | ]); 987 | 988 | $client = new Client( 989 | [ 990 | 'base_uri' => 'http://example.org', 991 | 'handler' => $handler, 992 | ] 993 | ); 994 | 995 | $logHandler = $this->setLogger($client); 996 | 997 | try { 998 | $client->get('/redirect'); 999 | } catch (\Exception $e) { 1000 | } 1001 | 1002 | $records = []; 1003 | foreach ($logHandler->getRecords() as $record) { 1004 | $records[] = $record['message']; 1005 | } 1006 | $this->assertEquals(['GET /redirect 301 ', 'GET /redirected 200 application/json'], $records); 1007 | } 1008 | 1009 | public function testLoggingDefault() 1010 | { 1011 | $client = new Client(); 1012 | $client->setLogger(); 1013 | 1014 | $reflector = new \ReflectionClass($client); 1015 | $reflectedLogger = $reflector->getProperty('logger'); 1016 | $reflectedLogger->setAccessible(true); 1017 | 1018 | $logger = $reflectedLogger->getValue($client); 1019 | $this->assertInstanceOf( 1020 | \Monolog\Logger::class, 1021 | $logger 1022 | ); 1023 | } 1024 | 1025 | public function testLoggingRequestHandler() 1026 | { 1027 | $handler = $this->getMockHandler([ 1028 | new Response(200, ['Content-Type' => 'application/json']) 1029 | ]); 1030 | 1031 | $client = new Client( 1032 | [ 1033 | 'base_uri' => 'http://example.org', 1034 | ] 1035 | ); 1036 | $logHandler = $this->setLogger($client); 1037 | 1038 | $client->get('/test', ['handler' => $handler]); 1039 | $records = []; 1040 | foreach ($logHandler->getRecords() as $record) { 1041 | $records[] = $record['message']; 1042 | } 1043 | $this->assertEquals(['GET /test 200 application/json'], $records); 1044 | } 1045 | 1046 | public function testSetSimpleLog() 1047 | { 1048 | $handler = $this->getMockHandler([ 1049 | new Response(200, ['Content-Type' => 'application/json']) 1050 | ]); 1051 | 1052 | $client = new Client( 1053 | [ 1054 | 'base_uri' => 'http://example.org', 1055 | 'handler' => $handler, 1056 | ] 1057 | ); 1058 | 1059 | $fp = fopen('php://memory', 'wb+'); 1060 | $client->setSimpleLog($fp, '{method} {target} {code}'); 1061 | $client->get('/test'); 1062 | 1063 | fseek($fp, 0); 1064 | $this->assertEquals('GET /test 200', fgets($fp)); 1065 | } 1066 | 1067 | public function testSetSimpleLogInvalid() 1068 | { 1069 | $logger = new SimpleLog(); 1070 | $client = new Client(); 1071 | $client->setLogger($logger); 1072 | 1073 | $this->assertFalse($client->setSimpleLog('test')); 1074 | } 1075 | 1076 | public function makeAuthHeaderProvider() 1077 | { 1078 | $testdata = json_decode(file_get_contents(__DIR__ . '/testdata.json'), true); 1079 | $tests = $testdata['tests']; 1080 | unset($testdata['tests']); 1081 | 1082 | foreach ($tests as $test) { 1083 | yield [ 1084 | 'name' => $test['testName'], 1085 | 'options' => array_merge($testdata, $test['request']), 1086 | 'request' => array_merge(['data' => ''], $test['request']), 1087 | 'result' => $test['expectedAuthorization'], 1088 | ]; 1089 | } 1090 | } 1091 | 1092 | public function createFromEdgeRcProvider() 1093 | { 1094 | return [ 1095 | [ 1096 | 'section' => null, 1097 | 'file' => null, 1098 | ], 1099 | [ 1100 | 'section' => 'default', 1101 | 'file' => null, 1102 | ], 1103 | [ 1104 | 'section' => 'testing', 1105 | 'file' => __DIR__ . '/edgerc/.edgerc.testing', 1106 | ], 1107 | [ 1108 | 'section' => 'testing', 1109 | 'file' => __DIR__ . '/edgerc/.edgerc.default-testing', 1110 | ] 1111 | ]; 1112 | } 1113 | 1114 | public function loggingProvider() 1115 | { 1116 | return [ 1117 | [ 1118 | 'get', 1119 | [new Response(200)], 1120 | '/test', 1121 | [ 1122 | 'GET /test 200 ' 1123 | ] 1124 | ], 1125 | [ 1126 | 'get', 1127 | [new Response(404)], 1128 | '/error', 1129 | [ 1130 | 'GET /error 404 ' 1131 | ] 1132 | ], 1133 | [ 1134 | 'post', 1135 | [new Response(500)], 1136 | '/error', 1137 | [ 1138 | 'POST /error 500 ' 1139 | ] 1140 | ], 1141 | [ 1142 | 'put', 1143 | [new Response(200, ['Content-Type' => 'application/json'])], 1144 | '/test', 1145 | [ 1146 | 'PUT /test 200 application/json' 1147 | ] 1148 | ], 1149 | [ 1150 | 'options', 1151 | [new Response(405, ['Content-Type' => 'application/json'])], 1152 | '/error', 1153 | [ 1154 | 'OPTIONS /error 405 application/json' 1155 | ] 1156 | ], 1157 | [ 1158 | 'get', 1159 | [new Response(503, ['Content-Type' => 'text/csv'])], 1160 | '/error', 1161 | [ 1162 | 'GET /error 503 text/csv' 1163 | ] 1164 | ], 1165 | [ 1166 | 'get', 1167 | [ 1168 | new Response(301, ['Location' => '/redirect']), 1169 | new Response(200) 1170 | ], 1171 | '/notthere', 1172 | [ 1173 | 'GET /notthere 301 ', 1174 | 'GET /redirect 200 ', 1175 | ] 1176 | ] 1177 | ]; 1178 | } 1179 | 1180 | public function getMockHandler($requests, array &$container = null) 1181 | { 1182 | $mock = new MockHandler($requests); 1183 | $container = []; 1184 | $history = Middleware::history($container); 1185 | 1186 | $handler = HandlerStack::create($mock); 1187 | $handler->push($history, 'history'); 1188 | 1189 | return $handler; 1190 | } 1191 | 1192 | /** 1193 | * @param \Akamai\Open\EdgeGrid\Client $client 1194 | * @return \Monolog\Handler\TestHandler 1195 | */ 1196 | protected function setLogger(Client $client) 1197 | { 1198 | $handler = new \Monolog\Handler\TestHandler(); 1199 | $logger = new \Monolog\Logger('Test Logger', [$handler]); 1200 | $client->setLogger($logger, '{method} {target} {code} {res_header_content-type}'); 1201 | 1202 | return $handler; 1203 | } 1204 | } 1205 | --------------------------------------------------------------------------------