Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
 
1441 ariadna 227
 
1 efrain 228
    /**
1441 ariadna 229
     * Checks if the socket is created. If PHP version is greater or equals to 8 then,
230
     * it will check if the var is instance of \Socket otherwise it will check if is
231
     * a resource.
232
     *
233
     * @return bool Returns true if the socket is created, false otherwise.
234
     */
235
    private function isSocketCreated(): bool
236
    {
237
        // Before version 8, sockets are resources
238
        // After version 8, sockets are instances of Socket
239
        if (PHP_MAJOR_VERSION >= 8) {
240
            $socketClass = '\Socket';
241
            return self::$socket instanceof $socketClass;
242
        } else {
243
            return is_resource(self::$socket);
244
        }
245
    }
246
 
247
    /**
1 efrain 248
     * Creates a UDP socket resource and stores it with the class, or retrieves
249
     * it if already instantiated and connected. Handles error-checking and
250
     * re-connecting if necessary. If $forceNewConnection is set to true, a new
251
     * socket will be created.
252
     *
253
     * @param bool $forceNewConnection
254
     * @return Resource
255
     */
256
    private function prepareSocket($forceNewConnection = false)
257
    {
1441 ariadna 258
        if (!$this->isSocketCreated()
1 efrain 259
            || $forceNewConnection
260
            || socket_last_error(self::$socket)
261
        ) {
262
            self::$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
263
            socket_clear_error(self::$socket);
264
            socket_connect(self::$socket, $this->getHost(), $this->getPort());
265
        }
266
 
267
        return self::$socket;
268
    }
269
 
270
    /**
271
     * Sends formatted monitoring event data via the UDP socket connection to
272
     * the CSM agent endpoint.
273
     *
274
     * @param array $eventData
275
     * @return int
276
     */
277
    private function sendEventData(array $eventData)
278
    {
279
        $socket = $this->prepareSocket();
280
        $datagram = json_encode($eventData);
281
        $result = socket_write($socket, $datagram, strlen($datagram));
282
        if ($result === false) {
283
            $this->prepareSocket(true);
284
        }
285
        return $result;
286
    }
287
 
288
    /**
289
     * Unwraps options, if needed, and returns them.
290
     *
291
     * @return ConfigurationInterface
292
     */
293
    private function unwrappedOptions()
294
    {
295
        if (!($this->options instanceof ConfigurationInterface)) {
296
            try {
297
                $this->options = ConfigurationProvider::unwrap($this->options);
298
            } catch (\Exception $e) {
299
                // Errors unwrapping CSM config defaults to disabling it
300
                $this->options = new Configuration(
301
                    false,
302
                    ConfigurationProvider::DEFAULT_HOST,
303
                    ConfigurationProvider::DEFAULT_PORT
304
                );
305
            }
306
        }
307
        return $this->options;
308
    }
1441 ariadna 309
}