Proyectos de Subversion Moodle

Rev

| 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
 
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
}