Passed
Push — master ( ea6601...6675f9 )
by Roeland
10:36 queued 12s
created

Client::getProxyUri()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 23
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 13
nc 5
nop 0
dl 0
loc 23
rs 8.8333
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @copyright Copyright (c) 2016, ownCloud, Inc.
7
 *
8
 * @author Daniel Kesselberg <[email protected]>
9
 * @author Lukas Reschke <[email protected]>
10
 * @author Robin Appelman <[email protected]>
11
 * @author Roeland Jago Douma <[email protected]>
12
 * @author Scott Shambarger <[email protected]>
13
 *
14
 * @license AGPL-3.0
15
 *
16
 * This code is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License, version 3,
18
 * as published by the Free Software Foundation.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License, version 3,
26
 * along with this program. If not, see <http://www.gnu.org/licenses/>
27
 *
28
 */
29
30
namespace OC\Http\Client;
31
32
use GuzzleHttp\Client as GuzzleClient;
33
use GuzzleHttp\RequestOptions;
34
use OCP\Http\Client\IClient;
35
use OCP\Http\Client\IResponse;
36
use OCP\ICertificateManager;
37
use OCP\IConfig;
38
39
/**
40
 * Class Client
41
 *
42
 * @package OC\Http
43
 */
44
class Client implements IClient {
45
	/** @var GuzzleClient */
46
	private $client;
47
	/** @var IConfig */
48
	private $config;
49
	/** @var ICertificateManager */
50
	private $certificateManager;
51
52
	/**
53
	 * @param IConfig $config
54
	 * @param ICertificateManager $certificateManager
55
	 * @param GuzzleClient $client
56
	 */
57
	public function __construct(
58
		IConfig $config,
59
		ICertificateManager $certificateManager,
60
		GuzzleClient $client
61
	) {
62
		$this->config = $config;
63
		$this->client = $client;
64
		$this->certificateManager = $certificateManager;
65
	}
66
67
	private function buildRequestOptions(array $options): array {
68
		$proxy = $this->getProxyUri();
69
70
		$defaults = [
71
			RequestOptions::VERIFY => $this->getCertBundle(),
72
			RequestOptions::TIMEOUT => 30,
73
		];
74
75
		// Only add RequestOptions::PROXY if Nextcloud is explicitly
76
		// configured to use a proxy. This is needed in order not to override
77
		// Guzzle default values.
78
		if($proxy !== null) {
79
			$defaults[RequestOptions::PROXY] = $proxy;
80
		}
81
82
		$options = array_merge($defaults, $options);
83
84
		if (!isset($options[RequestOptions::HEADERS]['User-Agent'])) {
85
			$options[RequestOptions::HEADERS]['User-Agent'] = 'Nextcloud Server Crawler';
86
		}
87
88
		return $options;
89
	}
90
91
	private function getCertBundle(): string {
92
		if ($this->certificateManager->listCertificates() !== []) {
93
			return $this->certificateManager->getAbsoluteBundlePath();
94
		}
95
96
		// If the instance is not yet setup we need to use the static path as
97
		// $this->certificateManager->getAbsoluteBundlePath() tries to instantiiate
98
		// a view
99
		if ($this->config->getSystemValue('installed', false)) {
100
			return $this->certificateManager->getAbsoluteBundlePath(null);
101
		}
102
103
		return \OC::$SERVERROOT . '/resources/config/ca-bundle.crt';
104
	}
105
106
	/**
107
	 * Returns a null or an associative array specifiying the proxy URI for
108
	 * 'http' and 'https' schemes, in addition to a 'no' key value pair
109
	 * providing a list of host names that should not be proxied to.
110
	 *
111
	 * @return array|null
112
	 *
113
	 * The return array looks like:
114
	 * [
115
	 *   'http' => 'username:[email protected]',
116
	 *   'https' => 'username:[email protected]',
117
	 *   'no' => ['foo.com', 'bar.com']
118
	 * ]
119
	 *
120
	 */
121
	private function getProxyUri(): ?array {
122
		$proxyHost = $this->config->getSystemValue('proxy', '');
123
124
		if ($proxyHost === '' || $proxyHost === null) {
125
			return null;
126
		}
127
128
		$proxyUserPwd = $this->config->getSystemValue('proxyuserpwd', '');
129
		if ($proxyUserPwd !== '' && $proxyUserPwd !== null) {
130
			$proxyHost = $proxyUserPwd . '@' . $proxyHost;
131
		}
132
133
		$proxy = [
134
			'http' => $proxyHost,
135
			'https' => $proxyHost,
136
		];
137
138
		$proxyExclude = $this->config->getSystemValue('proxyexclude', []);
139
		if ($proxyExclude !== [] && $proxyExclude !== null) {
140
			$proxy['no'] = $proxyExclude;
141
		}
142
143
		return $proxy;
144
	}
145
146
	/**
147
	 * Sends a GET request
148
	 *
149
	 * @param string $uri
150
	 * @param array $options Array such as
151
	 *              'query' => [
152
	 *                  'field' => 'abc',
153
	 *                  'other_field' => '123',
154
	 *                  'file_name' => fopen('/path/to/file', 'r'),
155
	 *              ],
156
	 *              'headers' => [
157
	 *                  'foo' => 'bar',
158
	 *              ],
159
	 *              'cookies' => ['
160
	 *                  'foo' => 'bar',
161
	 *              ],
162
	 *              'allow_redirects' => [
163
	 *                   'max'       => 10,  // allow at most 10 redirects.
164
	 *                   'strict'    => true,     // use "strict" RFC compliant redirects.
165
	 *                   'referer'   => true,     // add a Referer header
166
	 *                   'protocols' => ['https'] // only allow https URLs
167
	 *              ],
168
	 *              'save_to' => '/path/to/file', // save to a file or a stream
169
	 *              'verify' => true, // bool or string to CA file
170
	 *              'debug' => true,
171
	 *              'timeout' => 5,
172
	 * @return IResponse
173
	 * @throws \Exception If the request could not get completed
174
	 */
175
	public function get(string $uri, array $options = []): IResponse {
176
		$response = $this->client->request('get', $uri, $this->buildRequestOptions($options));
177
		$isStream = isset($options['stream']) && $options['stream'];
178
		return new Response($response, $isStream);
179
	}
180
181
	/**
182
	 * Sends a HEAD request
183
	 *
184
	 * @param string $uri
185
	 * @param array $options Array such as
186
	 *              'headers' => [
187
	 *                  'foo' => 'bar',
188
	 *              ],
189
	 *              'cookies' => ['
190
	 *                  'foo' => 'bar',
191
	 *              ],
192
	 *              'allow_redirects' => [
193
	 *                   'max'       => 10,  // allow at most 10 redirects.
194
	 *                   'strict'    => true,     // use "strict" RFC compliant redirects.
195
	 *                   'referer'   => true,     // add a Referer header
196
	 *                   'protocols' => ['https'] // only allow https URLs
197
	 *              ],
198
	 *              'save_to' => '/path/to/file', // save to a file or a stream
199
	 *              'verify' => true, // bool or string to CA file
200
	 *              'debug' => true,
201
	 *              'timeout' => 5,
202
	 * @return IResponse
203
	 * @throws \Exception If the request could not get completed
204
	 */
205
	public function head(string $uri, array $options = []): IResponse {
206
		$response = $this->client->request('head', $uri, $this->buildRequestOptions($options));
207
		return new Response($response);
208
	}
209
210
	/**
211
	 * Sends a POST request
212
	 *
213
	 * @param string $uri
214
	 * @param array $options Array such as
215
	 *              'body' => [
216
	 *                  'field' => 'abc',
217
	 *                  'other_field' => '123',
218
	 *                  'file_name' => fopen('/path/to/file', 'r'),
219
	 *              ],
220
	 *              'headers' => [
221
	 *                  'foo' => 'bar',
222
	 *              ],
223
	 *              'cookies' => ['
224
	 *                  'foo' => 'bar',
225
	 *              ],
226
	 *              'allow_redirects' => [
227
	 *                   'max'       => 10,  // allow at most 10 redirects.
228
	 *                   'strict'    => true,     // use "strict" RFC compliant redirects.
229
	 *                   'referer'   => true,     // add a Referer header
230
	 *                   'protocols' => ['https'] // only allow https URLs
231
	 *              ],
232
	 *              'save_to' => '/path/to/file', // save to a file or a stream
233
	 *              'verify' => true, // bool or string to CA file
234
	 *              'debug' => true,
235
	 *              'timeout' => 5,
236
	 * @return IResponse
237
	 * @throws \Exception If the request could not get completed
238
	 */
239
	public function post(string $uri, array $options = []): IResponse {
240
		if (isset($options['body']) && is_array($options['body'])) {
241
			$options['form_params'] = $options['body'];
242
			unset($options['body']);
243
		}
244
		$response = $this->client->request('post', $uri, $this->buildRequestOptions($options));
245
		return new Response($response);
246
	}
247
248
	/**
249
	 * Sends a PUT request
250
	 *
251
	 * @param string $uri
252
	 * @param array $options Array such as
253
	 *              'body' => [
254
	 *                  'field' => 'abc',
255
	 *                  'other_field' => '123',
256
	 *                  'file_name' => fopen('/path/to/file', 'r'),
257
	 *              ],
258
	 *              'headers' => [
259
	 *                  'foo' => 'bar',
260
	 *              ],
261
	 *              'cookies' => ['
262
	 *                  'foo' => 'bar',
263
	 *              ],
264
	 *              'allow_redirects' => [
265
	 *                   'max'       => 10,  // allow at most 10 redirects.
266
	 *                   'strict'    => true,     // use "strict" RFC compliant redirects.
267
	 *                   'referer'   => true,     // add a Referer header
268
	 *                   'protocols' => ['https'] // only allow https URLs
269
	 *              ],
270
	 *              'save_to' => '/path/to/file', // save to a file or a stream
271
	 *              'verify' => true, // bool or string to CA file
272
	 *              'debug' => true,
273
	 *              'timeout' => 5,
274
	 * @return IResponse
275
	 * @throws \Exception If the request could not get completed
276
	 */
277
	public function put(string $uri, array $options = []): IResponse {
278
		$response = $this->client->request('put', $uri, $this->buildRequestOptions($options));
279
		return new Response($response);
280
	}
281
282
	/**
283
	 * Sends a DELETE request
284
	 *
285
	 * @param string $uri
286
	 * @param array $options Array such as
287
	 *              'body' => [
288
	 *                  'field' => 'abc',
289
	 *                  'other_field' => '123',
290
	 *                  'file_name' => fopen('/path/to/file', 'r'),
291
	 *              ],
292
	 *              'headers' => [
293
	 *                  'foo' => 'bar',
294
	 *              ],
295
	 *              'cookies' => ['
296
	 *                  'foo' => 'bar',
297
	 *              ],
298
	 *              'allow_redirects' => [
299
	 *                   'max'       => 10,  // allow at most 10 redirects.
300
	 *                   'strict'    => true,     // use "strict" RFC compliant redirects.
301
	 *                   'referer'   => true,     // add a Referer header
302
	 *                   'protocols' => ['https'] // only allow https URLs
303
	 *              ],
304
	 *              'save_to' => '/path/to/file', // save to a file or a stream
305
	 *              'verify' => true, // bool or string to CA file
306
	 *              'debug' => true,
307
	 *              'timeout' => 5,
308
	 * @return IResponse
309
	 * @throws \Exception If the request could not get completed
310
	 */
311
	public function delete(string $uri, array $options = []): IResponse {
312
		$response = $this->client->request('delete', $uri, $this->buildRequestOptions($options));
313
		return new Response($response);
314
	}
315
316
	/**
317
	 * Sends a options request
318
	 *
319
	 * @param string $uri
320
	 * @param array $options Array such as
321
	 *              'body' => [
322
	 *                  'field' => 'abc',
323
	 *                  'other_field' => '123',
324
	 *                  'file_name' => fopen('/path/to/file', 'r'),
325
	 *              ],
326
	 *              'headers' => [
327
	 *                  'foo' => 'bar',
328
	 *              ],
329
	 *              'cookies' => ['
330
	 *                  'foo' => 'bar',
331
	 *              ],
332
	 *              'allow_redirects' => [
333
	 *                   'max'       => 10,  // allow at most 10 redirects.
334
	 *                   'strict'    => true,     // use "strict" RFC compliant redirects.
335
	 *                   'referer'   => true,     // add a Referer header
336
	 *                   'protocols' => ['https'] // only allow https URLs
337
	 *              ],
338
	 *              'save_to' => '/path/to/file', // save to a file or a stream
339
	 *              'verify' => true, // bool or string to CA file
340
	 *              'debug' => true,
341
	 *              'timeout' => 5,
342
	 * @return IResponse
343
	 * @throws \Exception If the request could not get completed
344
	 */
345
	public function options(string $uri, array $options = []): IResponse {
346
		$response = $this->client->request('options', $uri, $this->buildRequestOptions($options));
347
		return new Response($response);
348
	}
349
}
350