transferito
/
vendor
/
aws
/
aws-sdk-php
/
src
/
ClientSideMonitoring
/
AbstractMonitoringMiddleware.php
Exception
10 months ago
AbstractMonitoringMiddleware.php
10 months ago
ApiCallAttemptMonitoringMiddleware.php
10 months ago
ApiCallMonitoringMiddleware.php
10 months ago
Configuration.php
10 months ago
ConfigurationInterface.php
10 months ago
ConfigurationProvider.php
10 months ago
MonitoringMiddlewareInterface.php
10 months ago
AbstractMonitoringMiddleware.php
289 lines
| 1 | <?php |
| 2 | |
| 3 | namespace Aws\ClientSideMonitoring; |
| 4 | |
| 5 | use Aws\CommandInterface; |
| 6 | use Aws\Exception\AwsException; |
| 7 | use Aws\MonitoringEventsInterface; |
| 8 | use Aws\ResponseContainerInterface; |
| 9 | use Aws\ResultInterface; |
| 10 | use GuzzleHttp\Promise; |
| 11 | use Psr\Http\Message\RequestInterface; |
| 12 | use Psr\Http\Message\ResponseInterface; |
| 13 | |
| 14 | /** |
| 15 | * @internal |
| 16 | */ |
| 17 | abstract class AbstractMonitoringMiddleware |
| 18 | implements MonitoringMiddlewareInterface |
| 19 | { |
| 20 | private static $socket; |
| 21 | |
| 22 | private $nextHandler; |
| 23 | private $options; |
| 24 | protected $credentialProvider; |
| 25 | protected $region; |
| 26 | protected $service; |
| 27 | |
| 28 | protected static function getAwsExceptionHeader(AwsException $e, $headerName) |
| 29 | { |
| 30 | $response = $e->getResponse(); |
| 31 | if ($response !== null) { |
| 32 | $header = $response->getHeader($headerName); |
| 33 | if (!empty($header[0])) { |
| 34 | return $header[0]; |
| 35 | } |
| 36 | } |
| 37 | return null; |
| 38 | } |
| 39 | |
| 40 | protected static function getResultHeader(ResultInterface $result, $headerName) |
| 41 | { |
| 42 | if (isset($result['@metadata']['headers'][$headerName])) { |
| 43 | return $result['@metadata']['headers'][$headerName]; |
| 44 | } |
| 45 | return null; |
| 46 | } |
| 47 | |
| 48 | protected static function getExceptionHeader(\Exception $e, $headerName) |
| 49 | { |
| 50 | if ($e instanceof ResponseContainerInterface) { |
| 51 | $response = $e->getResponse(); |
| 52 | if ($response instanceof ResponseInterface) { |
| 53 | $header = $response->getHeader($headerName); |
| 54 | if (!empty($header[0])) { |
| 55 | return $header[0]; |
| 56 | } |
| 57 | } |
| 58 | } |
| 59 | return null; |
| 60 | } |
| 61 | |
| 62 | /** |
| 63 | * Constructor stores the passed in handler and options. |
| 64 | * |
| 65 | * @param callable $handler |
| 66 | * @param callable $credentialProvider |
| 67 | * @param $options |
| 68 | * @param $region |
| 69 | * @param $service |
| 70 | */ |
| 71 | public function __construct( |
| 72 | callable $handler, |
| 73 | callable $credentialProvider, |
| 74 | $options, |
| 75 | $region, |
| 76 | $service |
| 77 | ) { |
| 78 | $this->nextHandler = $handler; |
| 79 | $this->credentialProvider = $credentialProvider; |
| 80 | $this->options = $options; |
| 81 | $this->region = $region; |
| 82 | $this->service = $service; |
| 83 | } |
| 84 | |
| 85 | /** |
| 86 | * Standard invoke pattern for middleware execution to be implemented by |
| 87 | * child classes. |
| 88 | * |
| 89 | * @param CommandInterface $cmd |
| 90 | * @param RequestInterface $request |
| 91 | * @return Promise\PromiseInterface |
| 92 | */ |
| 93 | public function __invoke(CommandInterface $cmd, RequestInterface $request) |
| 94 | { |
| 95 | $handler = $this->nextHandler; |
| 96 | $eventData = null; |
| 97 | $enabled = $this->isEnabled(); |
| 98 | |
| 99 | if ($enabled) { |
| 100 | $cmd['@http']['collect_stats'] = true; |
| 101 | $eventData = $this->populateRequestEventData( |
| 102 | $cmd, |
| 103 | $request, |
| 104 | $this->getNewEvent($cmd, $request) |
| 105 | ); |
| 106 | } |
| 107 | |
| 108 | $g = function ($value) use ($eventData, $enabled) { |
| 109 | if ($enabled) { |
| 110 | $eventData = $this->populateResultEventData( |
| 111 | $value, |
| 112 | $eventData |
| 113 | ); |
| 114 | $this->sendEventData($eventData); |
| 115 | |
| 116 | if ($value instanceof MonitoringEventsInterface) { |
| 117 | $value->appendMonitoringEvent($eventData); |
| 118 | } |
| 119 | } |
| 120 | if ($value instanceof \Exception || $value instanceof \Throwable) { |
| 121 | return Promise\Create::rejectionFor($value); |
| 122 | } |
| 123 | return $value; |
| 124 | }; |
| 125 | |
| 126 | return Promise\Create::promiseFor($handler($cmd, $request))->then($g, $g); |
| 127 | } |
| 128 | |
| 129 | private function getClientId() |
| 130 | { |
| 131 | return $this->unwrappedOptions()->getClientId(); |
| 132 | } |
| 133 | |
| 134 | private function getNewEvent( |
| 135 | CommandInterface $cmd, |
| 136 | RequestInterface $request |
| 137 | ) { |
| 138 | $event = [ |
| 139 | 'Api' => $cmd->getName(), |
| 140 | 'ClientId' => $this->getClientId(), |
| 141 | 'Region' => $this->getRegion(), |
| 142 | 'Service' => $this->getService(), |
| 143 | 'Timestamp' => (int) floor(microtime(true) * 1000), |
| 144 | 'UserAgent' => substr( |
| 145 | $request->getHeaderLine('User-Agent') . ' ' . \Aws\default_user_agent(), |
| 146 | 0, |
| 147 | 256 |
| 148 | ), |
| 149 | 'Version' => 1 |
| 150 | ]; |
| 151 | return $event; |
| 152 | } |
| 153 | |
| 154 | private function getHost() |
| 155 | { |
| 156 | return $this->unwrappedOptions()->getHost(); |
| 157 | } |
| 158 | |
| 159 | private function getPort() |
| 160 | { |
| 161 | return $this->unwrappedOptions()->getPort(); |
| 162 | } |
| 163 | |
| 164 | private function getRegion() |
| 165 | { |
| 166 | return $this->region; |
| 167 | } |
| 168 | |
| 169 | private function getService() |
| 170 | { |
| 171 | return $this->service; |
| 172 | } |
| 173 | |
| 174 | /** |
| 175 | * Returns enabled flag from options, unwrapping options if necessary. |
| 176 | * |
| 177 | * @return bool |
| 178 | */ |
| 179 | private function isEnabled() |
| 180 | { |
| 181 | return $this->unwrappedOptions()->isEnabled(); |
| 182 | } |
| 183 | |
| 184 | /** |
| 185 | * Returns $eventData array with information from the request and command. |
| 186 | * |
| 187 | * @param CommandInterface $cmd |
| 188 | * @param RequestInterface $request |
| 189 | * @param array $event |
| 190 | * @return array |
| 191 | */ |
| 192 | protected function populateRequestEventData( |
| 193 | CommandInterface $cmd, |
| 194 | RequestInterface $request, |
| 195 | array $event |
| 196 | ) { |
| 197 | $dataFormat = static::getRequestData($request); |
| 198 | foreach ($dataFormat as $eventKey => $value) { |
| 199 | if ($value !== null) { |
| 200 | $event[$eventKey] = $value; |
| 201 | } |
| 202 | } |
| 203 | return $event; |
| 204 | } |
| 205 | |
| 206 | /** |
| 207 | * Returns $eventData array with information from the response, including |
| 208 | * the calculation for attempt latency. |
| 209 | * |
| 210 | * @param ResultInterface|\Exception $result |
| 211 | * @param array $event |
| 212 | * @return array |
| 213 | */ |
| 214 | protected function populateResultEventData( |
| 215 | $result, |
| 216 | array $event |
| 217 | ) { |
| 218 | $dataFormat = static::getResponseData($result); |
| 219 | foreach ($dataFormat as $eventKey => $value) { |
| 220 | if ($value !== null) { |
| 221 | $event[$eventKey] = $value; |
| 222 | } |
| 223 | } |
| 224 | return $event; |
| 225 | } |
| 226 | |
| 227 | /** |
| 228 | * Creates a UDP socket resource and stores it with the class, or retrieves |
| 229 | * it if already instantiated and connected. Handles error-checking and |
| 230 | * re-connecting if necessary. If $forceNewConnection is set to true, a new |
| 231 | * socket will be created. |
| 232 | * |
| 233 | * @param bool $forceNewConnection |
| 234 | * @return Resource |
| 235 | */ |
| 236 | private function prepareSocket($forceNewConnection = false) |
| 237 | { |
| 238 | if (!is_resource(self::$socket) |
| 239 | || $forceNewConnection |
| 240 | || socket_last_error(self::$socket) |
| 241 | ) { |
| 242 | self::$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); |
| 243 | socket_clear_error(self::$socket); |
| 244 | socket_connect(self::$socket, $this->getHost(), $this->getPort()); |
| 245 | } |
| 246 | |
| 247 | return self::$socket; |
| 248 | } |
| 249 | |
| 250 | /** |
| 251 | * Sends formatted monitoring event data via the UDP socket connection to |
| 252 | * the CSM agent endpoint. |
| 253 | * |
| 254 | * @param array $eventData |
| 255 | * @return int |
| 256 | */ |
| 257 | private function sendEventData(array $eventData) |
| 258 | { |
| 259 | $socket = $this->prepareSocket(); |
| 260 | $datagram = json_encode($eventData); |
| 261 | $result = socket_write($socket, $datagram, strlen($datagram)); |
| 262 | if ($result === false) { |
| 263 | $this->prepareSocket(true); |
| 264 | } |
| 265 | return $result; |
| 266 | } |
| 267 | |
| 268 | /** |
| 269 | * Unwraps options, if needed, and returns them. |
| 270 | * |
| 271 | * @return ConfigurationInterface |
| 272 | */ |
| 273 | private function unwrappedOptions() |
| 274 | { |
| 275 | if (!($this->options instanceof ConfigurationInterface)) { |
| 276 | try { |
| 277 | $this->options = ConfigurationProvider::unwrap($this->options); |
| 278 | } catch (\Exception $e) { |
| 279 | // Errors unwrapping CSM config defaults to disabling it |
| 280 | $this->options = new Configuration( |
| 281 | false, |
| 282 | ConfigurationProvider::DEFAULT_HOST, |
| 283 | ConfigurationProvider::DEFAULT_PORT |
| 284 | ); |
| 285 | } |
| 286 | } |
| 287 | return $this->options; |
| 288 | } |
| 289 | } |