Skip to content
This repository has been archived by the owner on May 4, 2024. It is now read-only.

Commit

Permalink
Merge pull request #6 from lookyman/async
Browse files Browse the repository at this point in the history
Rewritten for asynchronous requests and promises
  • Loading branch information
lookyman committed Aug 16, 2017
2 parents 80c6461 + 44d545d commit c156ac1
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 100 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,21 @@ use ParagonIE\Sapient\CryptographyKeys\SigningPublicKey;
use ParagonIE\Sapient\CryptographyKeys\SigningSecretKey;

$api = new Api(
new Client(), // Client must implement Http\Client\HttpClient
new Client(), // Client must implement Http\Client\HttpAsyncClient
new RequestFactory(), // RequestFactory must implement Interop\Http\Factory\RequestFactoryInterface
'https://chronicle.uri',
new SigningPublicKey(Base64UrlSafe::decode('chronicle public key')) // optional, omit if you don't care about validating API responses
);
$api->lastHash();
var_dump($api->lastHash()->wait());

// you must authenticate first before you can publish a message
$api->authenticate(
new SigningSecretKey(Base64UrlSafe::decode('your secret key')),
'your client id'
);
$api->publish('hello world');
var_dump($api->publish('hello world')->wait());
```

For implementations of client and request factory, you can use for example [Guzzle 6 HTTP Adapter](https://github.com/php-http/guzzle6-adapter) and [HTTP Factory for Guzzle](https://github.com/http-interop/http-factory-guzzle) respectively.

All endpoints return `Http\Promise\Promise`, so you can either just `->wait()` for the response, or handle it asynchronously with `->then()`. Responses are just plain arrays, look up the structure in the Chronicle's documentation.
8 changes: 4 additions & 4 deletions src/AbstractApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ protected function verifyAndReturnResponse(ResponseInterface $response): array
if ($this->chroniclePublicKey !== \null) {
$headers = $response->getHeader(Sapient::HEADER_SIGNATURE_NAME);
if (\count($headers) === 0) {
throw new HeaderMissingException(\sprintf('No signed response header (%s) found.', Sapient::HEADER_SIGNATURE_NAME));
throw new HeaderMissingException(\sprintf('No signed response header (%s) found', Sapient::HEADER_SIGNATURE_NAME));
}
foreach ($headers as $header) {
if (\ParagonIE_Sodium_Compat::crypto_sign_verify_detached(
Expand All @@ -52,10 +52,10 @@ protected function verifyAndReturnResponse(ResponseInterface $response): array
}
}
}
if ($verified) {
return \json_decode($body, \true);
if (!$verified) {
throw new InvalidMessageException('No valid signature given for this HTTP response');
}
throw new InvalidMessageException('No valid signature given for this HTTP response');
return \json_decode($body, \true);
}

}
74 changes: 47 additions & 27 deletions src/Api.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

namespace Lookyman\Chronicle;

use Http\Client\HttpClient;
use Http\Client\HttpAsyncClient;
use Http\Promise\Promise;
use Interop\Http\Factory\RequestFactoryInterface;
use ParagonIE\ConstantTime\Base64UrlSafe;
use ParagonIE\Sapient\Adapter\Generic\Stream;
Expand All @@ -13,14 +14,15 @@
use ParagonIE\Sapient\Sapient;
use Psr\Http\Message\MessageInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

final class Api extends AbstractApi implements ApiInterface
{

const CHRONICLE_CLIENT_KEY_ID = 'Chronicle-Client-Key-ID';

/**
* @var HttpClient
* @var HttpAsyncClient
*/
private $client;

Expand All @@ -45,7 +47,7 @@ final class Api extends AbstractApi implements ApiInterface
private $chronicleClientId;

public function __construct(
HttpClient $client,
HttpAsyncClient $client,
RequestFactoryInterface $requestFactory,
string $chronicleUri,
SigningPublicKey $chroniclePublicKey = \null
Expand All @@ -62,64 +64,74 @@ public function authenticate(SigningSecretKey $signingSecretKey, string $chronic
$this->chronicleClientId = $chronicleClientId;
}

public function lastHash(): array
public function lastHash(): Promise
{
return $this->verifyAndReturnResponse($this->client->sendRequest($this->requestFactory->createRequest(
return $this->client->sendAsyncRequest($this->requestFactory->createRequest(
'GET',
\sprintf(
'%s/chronicle/lasthash',
$this->chronicleUri
)
)));
))->then(function (ResponseInterface $response) {
return $this->verifyAndReturnResponse($response);
});
}

public function lookup(string $hash): array
public function lookup(string $hash): Promise
{
return $this->verifyAndReturnResponse($this->client->sendRequest($this->requestFactory->createRequest(
return $this->client->sendAsyncRequest($this->requestFactory->createRequest(
'GET',
\sprintf(
'%s/chronicle/lookup/%s',
$this->chronicleUri,
\urlencode($hash)
)
)));
))->then(function (ResponseInterface $response) {
return $this->verifyAndReturnResponse($response);
});
}

public function since(string $hash): array
public function since(string $hash): Promise
{
return $this->verifyAndReturnResponse($this->client->sendRequest($this->requestFactory->createRequest(
return $this->client->sendAsyncRequest($this->requestFactory->createRequest(
'GET',
\sprintf(
'%s/chronicle/since/%s',
$this->chronicleUri,
\urlencode($hash)
)
)));
))->then(function (ResponseInterface $response) {
return $this->verifyAndReturnResponse($response);
});
}

public function export(): array
public function export(): Promise
{
return $this->verifyAndReturnResponse($this->client->sendRequest($this->requestFactory->createRequest(
return $this->client->sendAsyncRequest($this->requestFactory->createRequest(
'GET',
\sprintf(
'%s/chronicle/export',
$this->chronicleUri
)
)));
))->then(function (ResponseInterface $response) {
return $this->verifyAndReturnResponse($response);
});
}

public function index(): array
public function index(): Promise
{
return $this->verifyAndReturnResponse($this->client->sendRequest($this->requestFactory->createRequest(
return $this->client->sendAsyncRequest($this->requestFactory->createRequest(
'GET',
\sprintf(
'%s/chronicle',
$this->chronicleUri
)
)));
))->then(function (ResponseInterface $response) {
return $this->verifyAndReturnResponse($response);
});
}

public function register(SigningPublicKey $publicKey, string $comment = \null): array
public function register(SigningPublicKey $publicKey, string $comment = \null): Promise
{
$message = \json_encode([
'publickey' => $publicKey->getString(),
Expand All @@ -133,10 +145,12 @@ public function register(SigningPublicKey $publicKey, string $comment = \null):
'Content-Type',
'application/json'
));
return $this->verifyAndReturnResponse($this->client->sendRequest($request));
return $this->client->sendAsyncRequest($request)->then(function (ResponseInterface $response) {
return $this->verifyAndReturnResponse($response);
});
}

public function revoke(string $clientId, SigningPublicKey $publicKey): array
public function revoke(string $clientId, SigningPublicKey $publicKey): Promise
{
$message = \json_encode([
'clientid' => $clientId,
Expand All @@ -150,17 +164,21 @@ public function revoke(string $clientId, SigningPublicKey $publicKey): array
'Content-Type',
'application/json'
));
return $this->verifyAndReturnResponse($this->client->sendRequest($request));
return $this->client->sendAsyncRequest($request)->then(function (ResponseInterface $response) {
return $this->verifyAndReturnResponse($response);
});
}

public function publish(string $message): array
public function publish(string $message): Promise
{
/** @var RequestInterface $request */
$request = $this->authenticateAndSignMessage($this->requestFactory->createRequest(
'POST',
\sprintf('%s/chronicle/publish', $this->chronicleUri)
)->withBody(Stream::fromString($message)));
return $this->verifyAndReturnResponse($this->client->sendRequest($request));
return $this->client->sendAsyncRequest($request)->then(function (ResponseInterface $response) {
return $this->verifyAndReturnResponse($response);
});
}

public function replica(string $source): CommonEndpointInterface
Expand All @@ -174,15 +192,17 @@ public function replica(string $source): CommonEndpointInterface
);
}

public function replicas(): array
public function replicas(): Promise
{
return $this->verifyAndReturnResponse($this->client->sendRequest($this->requestFactory->createRequest(
return $this->client->sendAsyncRequest($this->requestFactory->createRequest(
'GET',
\sprintf(
'%s/chronicle/replica',
$this->chronicleUri
)
)));
))->then(function (ResponseInterface $response) {
return $this->verifyAndReturnResponse($response);
});
}

private function authenticateAndSignMessage(MessageInterface $request): MessageInterface
Expand Down
11 changes: 6 additions & 5 deletions src/ApiInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,22 @@

namespace Lookyman\Chronicle;

use Http\Promise\Promise;
use ParagonIE\Sapient\CryptographyKeys\SigningPublicKey;

interface ApiInterface extends CommonEndpointInterface
{

public function index(): array;
public function index(): Promise;

public function register(SigningPublicKey $publicKey, string $comment = null): array;
public function register(SigningPublicKey $publicKey, string $comment = null): Promise;

public function revoke(string $clientId, SigningPublicKey $publicKey): array;
public function revoke(string $clientId, SigningPublicKey $publicKey): Promise;

public function publish(string $message): array;
public function publish(string $message): Promise;

public function replica(string $source): CommonEndpointInterface;

public function replicas(): array;
public function replicas(): Promise;

}
10 changes: 6 additions & 4 deletions src/CommonEndpointInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@

namespace Lookyman\Chronicle;

use Http\Promise\Promise;

interface CommonEndpointInterface
{

public function lastHash(): array;
public function lastHash(): Promise;

public function lookup(string $hash): array;
public function lookup(string $hash): Promise;

public function since(string $hash): array;
public function since(string $hash): Promise;

public function export(): array;
public function export(): Promise;

}
40 changes: 25 additions & 15 deletions src/Replica.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@

namespace Lookyman\Chronicle;

use Http\Client\HttpClient;
use Http\Client\HttpAsyncClient;
use Http\Promise\Promise;
use Interop\Http\Factory\RequestFactoryInterface;
use ParagonIE\Sapient\CryptographyKeys\SigningPublicKey;
use Psr\Http\Message\ResponseInterface;

final class Replica extends AbstractApi implements CommonEndpointInterface
{

/**
* @var HttpClient
* @var HttpAsyncClient
*/
private $client;

Expand All @@ -32,7 +34,7 @@ final class Replica extends AbstractApi implements CommonEndpointInterface
private $source;

public function __construct(
HttpClient $client,
HttpAsyncClient $client,
RequestFactoryInterface $requestFactory,
string $chronicleUri,
string $source,
Expand All @@ -45,54 +47,62 @@ public function __construct(
$this->source = $source;
}

public function lastHash(): array
public function lastHash(): Promise
{
return $this->verifyAndReturnResponse($this->client->sendRequest($this->requestFactory->createRequest(
return $this->client->sendAsyncRequest($this->requestFactory->createRequest(
'GET',
\sprintf(
'%s/chronicle/replica/%s/lasthash',
$this->chronicleUri,
\urlencode($this->source)
)
)));
))->then(function (ResponseInterface $response) {
return $this->verifyAndReturnResponse($response);
});
}

public function lookup(string $hash): array
public function lookup(string $hash): Promise
{
return $this->verifyAndReturnResponse($this->client->sendRequest($this->requestFactory->createRequest(
return $this->client->sendAsyncRequest($this->requestFactory->createRequest(
'GET',
\sprintf(
'%s/chronicle/replica/%s/lookup/%s',
$this->chronicleUri,
\urlencode($this->source),
\urlencode($hash)
)
)));
))->then(function (ResponseInterface $response) {
return $this->verifyAndReturnResponse($response);
});
}

public function since(string $hash): array
public function since(string $hash): Promise
{
return $this->verifyAndReturnResponse($this->client->sendRequest($this->requestFactory->createRequest(
return $this->client->sendAsyncRequest($this->requestFactory->createRequest(
'GET',
\sprintf(
'%s/chronicle/replica/%s/since/%s',
$this->chronicleUri,
\urlencode($this->source),
\urlencode($hash)
)
)));
))->then(function (ResponseInterface $response) {
return $this->verifyAndReturnResponse($response);
});
}

public function export(): array
public function export(): Promise
{
return $this->verifyAndReturnResponse($this->client->sendRequest($this->requestFactory->createRequest(
return $this->client->sendAsyncRequest($this->requestFactory->createRequest(
'GET',
\sprintf(
'%s/chronicle/replica/%s/export',
$this->chronicleUri,
\urlencode($this->source)
)
)));
))->then(function (ResponseInterface $response) {
return $this->verifyAndReturnResponse($response);
});
}

}
Loading

0 comments on commit c156ac1

Please sign in to comment.