Test Failed
Push — master ( 9d73a2...14c4f6 )
by hugh
06:42
created

Client::write()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 5
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 9
rs 10
1
<?php
2
/**
3
 * Created by IntelliJ IDEA.
4
 * User: hugh.li
5
 * Date: 2020/1/21
6
 * Time: 14:52
7
 */
8
9
namespace HughCube\ACMClient;
10
11
use GuzzleHttp\Client as HttpClient;
12
use GuzzleHttp\HandlerStack;
13
use GuzzleHttp\RequestOptions;
14
use HughCube\ACMClient\Exceptions\HttpException;
15
use HughCube\PUrl\Url;
16
use Psr\Http\Message\RequestInterface;
17
18
class Client
19
{
20
    /**
21
     * 获取节点的url
22
     */
23
    const GET_SERVER = "diamond-server/diamond";
24
25
    /**
26
     * 获取配置信息的url
27
     */
28
    const GET_CONFIG = "diamond-server/config.co";
29
30
    /**
31
     * 设置配置的url
32
     */
33
    const SET_CONFIG = "diamond-server/basestone.do?method=syncUpdateAll";
34
35
    /**
36
     * 删除配置的url
37
     */
38
    const DEL_CONFIG = "diamond-server/datum.do?method=deleteAllDatums";
39
40
    /**
41
     * @val HttpClient
42
     */
43
    private $httpClient;
44
45
    /**
46
     * @var Endpoint[]
47
     */
48
    protected $endpoints;
49
50
    /**
51
     * @var Config
52
     */
53
    private $config;
54
55
    public function __construct(Config $config)
56
    {
57
        $this->config = $config;
58
    }
59
60
    /**
61
     * @return Config
62
     */
63
    public function getConfig()
64
    {
65
        return $this->config;
66
    }
67
68
    /**
69
     * @return Endpoint[]
70
     * @throws HttpException
71
     */
72
    protected function getEndpoints()
73
    {
74
        if (!is_array($this->endpoints) || empty($this->endpoints)) {
0 ignored issues
show
introduced by
The condition is_array($this->endpoints) is always true.
Loading history...
75
            $endpoint = new Endpoint($this->config->getEndpoint(), null);
76
77
            $this->endpoints = [];
78
            $response = $this->sendRequest("GET", $endpoint->makeUrl(static::GET_SERVER));
79
            foreach (explode("\n", $response->getBody()->getContents()) as $server) {
80
                $server = trim($server);
81
                if (empty($server)) {
82
                    continue;
83
                }
84
85
                $parts = explode(":", $server);
86
                $parts[1] = (isset($parts[1]) && $parts[1]) ? $parts[1] : null;
87
                $this->endpoints[] = new Endpoint($parts[0], $parts[1]);
0 ignored issues
show
Bug introduced by
It seems like $parts[1] can also be of type string; however, parameter $port of HughCube\ACMClient\Endpoint::__construct() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

87
                $this->endpoints[] = new Endpoint($parts[0], /** @scrutinizer ignore-type */ $parts[1]);
Loading history...
88
            }
89
        }
90
91
        return $this->endpoints;
92
    }
93
94
    /**
95
     * @return $this
96
     */
97
    public function resetEndpoints()
98
    {
99
        $this->endpoints = null;
100
101
        return $this;
102
    }
103
104
    /**
105
     * @return Url
106
     * @throws HttpException
107
     */
108
    protected function makeUrl(string $path)
109
    {
110
        $endpoints = $this->getEndpoints();
111
        $key = array_rand($endpoints, 1);
112
113
        return $endpoints[$key]->makeUrl($path);
114
    }
115
116
    /**
117
     * @return HttpClient
118
     */
119
    protected function getHttpClient()
120
    {
121
        if (!$this->httpClient instanceof HttpClient) {
122
            $handler = HandlerStack::create();
123
            $handler->push($this->signatureHandler());
124
            $this->httpClient = new HttpClient([
125
                "handler" => $handler,
126
                RequestOptions::PROXY => $this->getConfig()->getProxy()
127
            ]);
128
        }
129
130
        return $this->httpClient;
131
    }
132
133
    /**
134
     * 添加请求头信息
135
     *
136
     * @return \Closure
137
     */
138
    private function signatureHandler()
139
    {
140
        return function (callable $handler) {
141
            return function (RequestInterface $request, array $options) use ($handler) {
142
                $request = $request
143
                    ->withHeader("timeStamp", time() * 1000)
144
                    ->withHeader("Spas-AccessKey", $this->getConfig()->getAccessKey());
145
146
                $signatureData = sprintf(
147
                    "%s+%s+%s",
148
                    $this->getConfig()->getNamespace(),
149
                    $this->getConfig()->getGroupName(),
150
                    $request->getHeaderLine("timeStamp")
151
                );
152
153
                $signature = base64_encode(hash_hmac('sha1', $signatureData, $this->config->getSecretKey(), true));
154
155
                $request = $request->withHeader("Spas-Signature", $signature);
156
157
                return $handler($request, $options);
158
            };
159
        };
160
    }
161
162
    /**
163
     * @param $method
164
     * @param Url $uri
165
     * @param array $params
166
     * @param array $options
167
     * @return \Psr\Http\Message\ResponseInterface
168
     * @throws HttpException
169
     */
170
    protected function sendRequest($method, Url $uri, array $params = [], array $options = [])
171
    {
172
        $params = array_merge($params, [
173
            "tenant" => $this->getConfig()->getNamespace(),
174
            "group" => $this->getConfig()->getGroupName()
175
        ]);
176
177
        if ("GET" === strtoupper($method)) {
178
            foreach ($params as $name => $value) {
179
                $uri = $uri->withQueryValue($name, $value);
180
            }
181
        }
182
183
        if ("POST" === strtoupper($method) && !empty($params)) {
184
            $options[RequestOptions::FORM_PARAMS] = $params;
185
        }
186
187
        try {
188
            $response = $this->getHttpClient()->request($method, $uri, $options);
189
        } catch (\Throwable $exception) {
190
            throw new HttpException($exception->getMessage(), $exception->getCode(), $exception);
191
        }
192
193
        return $response;
194
    }
195
196
    /**
197
     * 读取配置集
198
     *
199
     * @param string $dataId
200
     * @return string
201
     * @throws \Exception
202
     */
203
    public function read(string $dataId)
204
    {
205
        $response = $this->sendRequest(
206
            "GET",
207
            $this->makeUrl(static::GET_CONFIG),
208
            ["dataId" => $dataId]
209
        );
210
211
        return $response->getBody()->getContents();
212
    }
213
214
    /**
215
     * 写入配置集
216
     *
217
     * @param string $dataId
218
     * @param string $content
219
     * @return boolean
220
     * @throws \Exception
221
     */
222
    public function write(string $dataId, string $content)
223
    {
224
        $response = $this->sendRequest(
225
            "POST",
226
            $this->makeUrl(static::SET_CONFIG),
227
            ["dataId" => $dataId, "content" => $content]
228
        );
229
230
        return 200 == $response->getStatusCode();
231
    }
232
233
    /**
234
     * 删除配置集
235
     *
236
     * @param string $dataId
237
     * @return boolean
238
     * @throws \Exception
239
     */
240
    public function remove(string $dataId)
241
    {
242
        $response = $this->sendRequest(
243
            "POST",
244
            $this->makeUrl(static::DEL_CONFIG),
245
            ["dataId" => $dataId]
246
        );
247
248
        return 200 == $response->getStatusCode();
249
    }
250
251
    /**
252
     * 监听配置集
253
     *
254
     * @param string $dataId
255
     * @param string $content
256
     * @return boolean
257
     * @throws \Exception
258
     */
259
    public function watch(string $dataId, string $content)
260
    {
261
        $wordDelimiter = chr(37) . chr(48) . chr(50);
262
        $lineDelimiter = chr(37) . chr(48) . chr(49);
263
264
        $args = [
265
            $dataId,
266
            $this->getConfig()->getGroupName(),
267
            md5($content),
268
            $this->getConfig()->getNamespace()
269
        ];
270
271
        $response = $this->sendRequest(
272
            "POST",
273
            $this->makeUrl(static::GET_CONFIG),
274
            [
275
                "Probe-Modify-Request" => implode($wordDelimiter, $args) . $lineDelimiter
276
            ]
277
        );
278
279
        return 200 == $response->getStatusCode();
280
    }
281
}
282