mirror of
https://github.com/SociallyDev/Spaces-API.git
synced 2025-07-05 20:41:31 -07:00
* Simplifies & beautifies everything * Introduces a new Class system. * Errors are defaulted to AWS's handler. * New function names & more efficient handling. * Should fix a majority of the errors. Please read the README for more!
289 lines
No EOL
7.9 KiB
PHP
289 lines
No EOL
7.9 KiB
PHP
<?php
|
|
|
|
namespace Aws\ClientSideMonitoring;
|
|
|
|
use Aws\CommandInterface;
|
|
use Aws\Exception\AwsException;
|
|
use Aws\MonitoringEventsInterface;
|
|
use Aws\ResponseContainerInterface;
|
|
use Aws\ResultInterface;
|
|
use GuzzleHttp\Promise;
|
|
use Psr\Http\Message\RequestInterface;
|
|
use Psr\Http\Message\ResponseInterface;
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
abstract class AbstractMonitoringMiddleware
|
|
implements MonitoringMiddlewareInterface
|
|
{
|
|
private static $socket;
|
|
|
|
private $nextHandler;
|
|
private $options;
|
|
protected $credentialProvider;
|
|
protected $region;
|
|
protected $service;
|
|
|
|
protected static function getAwsExceptionHeader(AwsException $e, $headerName)
|
|
{
|
|
$response = $e->getResponse();
|
|
if ($response !== null) {
|
|
$header = $response->getHeader($headerName);
|
|
if (!empty($header[0])) {
|
|
return $header[0];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
protected static function getResultHeader(ResultInterface $result, $headerName)
|
|
{
|
|
if (isset($result['@metadata']['headers'][$headerName])) {
|
|
return $result['@metadata']['headers'][$headerName];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
protected static function getExceptionHeader(\Exception $e, $headerName)
|
|
{
|
|
if ($e instanceof ResponseContainerInterface) {
|
|
$response = $e->getResponse();
|
|
if ($response instanceof ResponseInterface) {
|
|
$header = $response->getHeader($headerName);
|
|
if (!empty($header[0])) {
|
|
return $header[0];
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Constructor stores the passed in handler and options.
|
|
*
|
|
* @param callable $handler
|
|
* @param callable $credentialProvider
|
|
* @param $options
|
|
* @param $region
|
|
* @param $service
|
|
*/
|
|
public function __construct(
|
|
callable $handler,
|
|
callable $credentialProvider,
|
|
$options,
|
|
$region,
|
|
$service
|
|
) {
|
|
$this->nextHandler = $handler;
|
|
$this->credentialProvider = $credentialProvider;
|
|
$this->options = $options;
|
|
$this->region = $region;
|
|
$this->service = $service;
|
|
}
|
|
|
|
/**
|
|
* Standard invoke pattern for middleware execution to be implemented by
|
|
* child classes.
|
|
*
|
|
* @param CommandInterface $cmd
|
|
* @param RequestInterface $request
|
|
* @return Promise\PromiseInterface
|
|
*/
|
|
public function __invoke(CommandInterface $cmd, RequestInterface $request)
|
|
{
|
|
$handler = $this->nextHandler;
|
|
$eventData = null;
|
|
$enabled = $this->isEnabled();
|
|
|
|
if ($enabled) {
|
|
$cmd['@http']['collect_stats'] = true;
|
|
$eventData = $this->populateRequestEventData(
|
|
$cmd,
|
|
$request,
|
|
$this->getNewEvent($cmd, $request)
|
|
);
|
|
}
|
|
|
|
$g = function ($value) use ($eventData, $enabled) {
|
|
if ($enabled) {
|
|
$eventData = $this->populateResultEventData(
|
|
$value,
|
|
$eventData
|
|
);
|
|
$this->sendEventData($eventData);
|
|
|
|
if ($value instanceof MonitoringEventsInterface) {
|
|
$value->appendMonitoringEvent($eventData);
|
|
}
|
|
}
|
|
if ($value instanceof \Exception || $value instanceof \Throwable) {
|
|
return Promise\rejection_for($value);
|
|
}
|
|
return $value;
|
|
};
|
|
|
|
return Promise\promise_for($handler($cmd, $request))->then($g, $g);
|
|
}
|
|
|
|
private function getClientId()
|
|
{
|
|
return $this->unwrappedOptions()->getClientId();
|
|
}
|
|
|
|
private function getNewEvent(
|
|
CommandInterface $cmd,
|
|
RequestInterface $request
|
|
) {
|
|
$event = [
|
|
'Api' => $cmd->getName(),
|
|
'ClientId' => $this->getClientId(),
|
|
'Region' => $this->getRegion(),
|
|
'Service' => $this->getService(),
|
|
'Timestamp' => (int) floor(microtime(true) * 1000),
|
|
'UserAgent' => substr(
|
|
$request->getHeaderLine('User-Agent') . ' ' . \Aws\default_user_agent(),
|
|
0,
|
|
256
|
|
),
|
|
'Version' => 1
|
|
];
|
|
return $event;
|
|
}
|
|
|
|
private function getHost()
|
|
{
|
|
return $this->unwrappedOptions()->getHost();
|
|
}
|
|
|
|
private function getPort()
|
|
{
|
|
return $this->unwrappedOptions()->getPort();
|
|
}
|
|
|
|
private function getRegion()
|
|
{
|
|
return $this->region;
|
|
}
|
|
|
|
private function getService()
|
|
{
|
|
return $this->service;
|
|
}
|
|
|
|
/**
|
|
* Returns enabled flag from options, unwrapping options if necessary.
|
|
*
|
|
* @return bool
|
|
*/
|
|
private function isEnabled()
|
|
{
|
|
return $this->unwrappedOptions()->isEnabled();
|
|
}
|
|
|
|
/**
|
|
* Returns $eventData array with information from the request and command.
|
|
*
|
|
* @param CommandInterface $cmd
|
|
* @param RequestInterface $request
|
|
* @param array $event
|
|
* @return array
|
|
*/
|
|
protected function populateRequestEventData(
|
|
CommandInterface $cmd,
|
|
RequestInterface $request,
|
|
array $event
|
|
) {
|
|
$dataFormat = static::getRequestData($request);
|
|
foreach ($dataFormat as $eventKey => $value) {
|
|
if ($value !== null) {
|
|
$event[$eventKey] = $value;
|
|
}
|
|
}
|
|
return $event;
|
|
}
|
|
|
|
/**
|
|
* Returns $eventData array with information from the response, including
|
|
* the calculation for attempt latency.
|
|
*
|
|
* @param ResultInterface|\Exception $result
|
|
* @param array $event
|
|
* @return array
|
|
*/
|
|
protected function populateResultEventData(
|
|
$result,
|
|
array $event
|
|
) {
|
|
$dataFormat = static::getResponseData($result);
|
|
foreach ($dataFormat as $eventKey => $value) {
|
|
if ($value !== null) {
|
|
$event[$eventKey] = $value;
|
|
}
|
|
}
|
|
return $event;
|
|
}
|
|
|
|
/**
|
|
* Creates a UDP socket resource and stores it with the class, or retrieves
|
|
* it if already instantiated and connected. Handles error-checking and
|
|
* re-connecting if necessary. If $forceNewConnection is set to true, a new
|
|
* socket will be created.
|
|
*
|
|
* @param bool $forceNewConnection
|
|
* @return Resource
|
|
*/
|
|
private function prepareSocket($forceNewConnection = false)
|
|
{
|
|
if (!is_resource(self::$socket)
|
|
|| $forceNewConnection
|
|
|| socket_last_error(self::$socket)
|
|
) {
|
|
self::$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
|
|
socket_clear_error(self::$socket);
|
|
socket_connect(self::$socket, $this->getHost(), $this->getPort());
|
|
}
|
|
|
|
return self::$socket;
|
|
}
|
|
|
|
/**
|
|
* Sends formatted monitoring event data via the UDP socket connection to
|
|
* the CSM agent endpoint.
|
|
*
|
|
* @param array $eventData
|
|
* @return int
|
|
*/
|
|
private function sendEventData(array $eventData)
|
|
{
|
|
$socket = $this->prepareSocket();
|
|
$datagram = json_encode($eventData);
|
|
$result = socket_write($socket, $datagram, strlen($datagram));
|
|
if ($result === false) {
|
|
$this->prepareSocket(true);
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Unwraps options, if needed, and returns them.
|
|
*
|
|
* @return ConfigurationInterface
|
|
*/
|
|
private function unwrappedOptions()
|
|
{
|
|
if (!($this->options instanceof ConfigurationInterface)) {
|
|
try {
|
|
$this->options = ConfigurationProvider::unwrap($this->options);
|
|
} catch (\Exception $e) {
|
|
// Errors unwrapping CSM config defaults to disabling it
|
|
$this->options = new Configuration(
|
|
false,
|
|
ConfigurationProvider::DEFAULT_HOST,
|
|
ConfigurationProvider::DEFAULT_PORT
|
|
);
|
|
}
|
|
}
|
|
return $this->options;
|
|
}
|
|
} |