Commit | Line | Data |
---|---|---|
86fc0430 TD |
1 | <?php |
2 | namespace wcf\util; | |
3536d2fe AE |
3 | use wcf\system\exception\HTTPNotFoundException; |
4 | use wcf\system\exception\HTTPServerErrorException; | |
5 | use wcf\system\exception\HTTPUnauthorizedException; | |
86fc0430 TD |
6 | use wcf\system\exception\SystemException; |
7 | use wcf\system\io\RemoteFile; | |
8 | use wcf\system\Regex; | |
9 | use wcf\system\WCF; | |
10 | ||
11 | /** | |
8fe14bd6 | 12 | * Sends HTTP/1.1 requests. |
86fc0430 TD |
13 | * It supports POST, SSL, Basic Auth etc. |
14 | * | |
3536d2fe | 15 | * @author Tim Duesterhus |
ca4ba303 | 16 | * @copyright 2001-2014 WoltLab GmbH |
86fc0430 TD |
17 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> |
18 | * @package com.woltlab.wcf | |
19 | * @subpackage util | |
20 | * @category Community Framework | |
21 | */ | |
a195ffa6 | 22 | final class HTTPRequest { |
86fc0430 TD |
23 | /** |
24 | * given options | |
a17de04e | 25 | * @var array |
86fc0430 TD |
26 | */ |
27 | private $options = array(); | |
28 | ||
29 | /** | |
30 | * given post parameters | |
a17de04e | 31 | * @var array |
86fc0430 TD |
32 | */ |
33 | private $postParameters = array(); | |
34 | ||
5f70a0de TD |
35 | /** |
36 | * given files | |
06355ec3 | 37 | * @var array |
5f70a0de TD |
38 | */ |
39 | private $files = array(); | |
40 | ||
86fc0430 | 41 | /** |
60f613e2 | 42 | * indicates if request will be made via SSL |
a17de04e | 43 | * @var boolean |
86fc0430 TD |
44 | */ |
45 | private $useSSL = false; | |
46 | ||
47 | /** | |
48 | * target host | |
a17de04e | 49 | * @var string |
86fc0430 TD |
50 | */ |
51 | private $host; | |
52 | ||
53 | /** | |
54 | * target port | |
a17de04e | 55 | * @var integer |
86fc0430 TD |
56 | */ |
57 | private $port; | |
a17de04e | 58 | |
86fc0430 TD |
59 | /** |
60 | * target path | |
a17de04e | 61 | * @var string |
86fc0430 TD |
62 | */ |
63 | private $path; | |
64 | ||
65 | /** | |
66 | * target query string | |
a17de04e | 67 | * @var string |
86fc0430 TD |
68 | */ |
69 | private $query; | |
70 | ||
25cdc083 AE |
71 | /** |
72 | * request URL | |
73 | * @var string | |
74 | */ | |
75 | private $url = ''; | |
76 | ||
86fc0430 TD |
77 | /** |
78 | * request headers | |
a17de04e | 79 | * @var array<string> |
86fc0430 TD |
80 | */ |
81 | private $headers = array(); | |
82 | ||
8fe14bd6 TD |
83 | /** |
84 | * legacy headers | |
85 | * @var array<string> | |
86 | */ | |
87 | private $legacyHeaders = array(); | |
88 | ||
5f70a0de TD |
89 | /** |
90 | * request body | |
91 | * @var string | |
92 | */ | |
93 | private $body = ''; | |
94 | ||
86fc0430 TD |
95 | /** |
96 | * reply headers | |
a17de04e | 97 | * @var array<string> |
86fc0430 TD |
98 | */ |
99 | private $replyHeaders = array(); | |
100 | ||
101 | /** | |
102 | * reply body | |
a17de04e | 103 | * @var string |
86fc0430 TD |
104 | */ |
105 | private $replyBody = ''; | |
106 | ||
107 | /** | |
108 | * reply status code | |
a17de04e | 109 | * @var integer |
86fc0430 TD |
110 | */ |
111 | private $statusCode = 0; | |
112 | ||
113 | /** | |
a17de04e | 114 | * Constructs a new instance of HTTPRequest. |
86fc0430 TD |
115 | * |
116 | * @param string $url URL to connect to | |
117 | * @param array<string> $options | |
1b20f990 | 118 | * @param mixed $postParameters Parameters to send via POST |
5f70a0de | 119 | * @param array $files Files to attach to the request |
86fc0430 | 120 | */ |
1b20f990 | 121 | public function __construct($url, array $options = array(), $postParameters = array(), array $files = array()) { |
86fc0430 TD |
122 | $this->setURL($url); |
123 | ||
124 | $this->postParameters = $postParameters; | |
5f70a0de | 125 | $this->files = $files; |
86fc0430 TD |
126 | |
127 | $this->setOptions($options); | |
128 | ||
a195ffa6 | 129 | // set default headers |
8fe14bd6 TD |
130 | $this->addHeader('user-agent', "HTTP.PHP (HTTPRequest.class.php; WoltLab Community Framework/".WCF_VERSION."; ".WCF::getLanguage()->languageCode.")"); |
131 | $this->addHeader('accept', '*/*'); | |
132 | $this->addHeader('accept-language', WCF::getLanguage()->getFixedLanguageCode()); | |
133 | ||
4d28d5a2 | 134 | if (isset($this->options['maxLength'])) { |
5262964b | 135 | $this->addHeader('Range', 'bytes=0-'.($this->options['maxLength'] - 1)); |
4d28d5a2 SG |
136 | } |
137 | ||
86fc0430 | 138 | if ($this->options['method'] !== 'GET') { |
5f70a0de | 139 | if (empty($this->files)) { |
1b20f990 AE |
140 | if (is_array($postParameters)) { |
141 | $this->body = http_build_query($this->postParameters, '', '&'); | |
142 | } | |
143 | else if (is_string($postParameters) && !empty($postParameters)) { | |
144 | $this->body = $postParameters; | |
145 | } | |
146 | ||
8fe14bd6 | 147 | $this->addHeader('content-type', 'application/x-www-form-urlencoded'); |
5f70a0de TD |
148 | } |
149 | else { | |
150 | $boundary = StringUtil::getRandomID(); | |
8fe14bd6 | 151 | $this->addHeader('content-type', 'multipart/form-data; boundary='.$boundary); |
5f70a0de TD |
152 | |
153 | // source of the iterators: http://stackoverflow.com/a/7623716/782822 | |
154 | if (!empty($this->postParameters)) { | |
155 | $iterator = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($this->postParameters), \RecursiveIteratorIterator::SELF_FIRST); | |
156 | foreach ($iterator as $k => $v) { | |
157 | if (!$iterator->hasChildren()) { | |
158 | $key = ''; | |
159 | for ($i = 0, $max = $iterator->getDepth(); $i <= $max; $i++) { | |
160 | if ($i === 0) $key .= $iterator->getSubIterator($i)->key(); | |
161 | else $key .= '['.$iterator->getSubIterator($i)->key().']'; | |
162 | } | |
163 | ||
164 | $this->body .= "--".$boundary."\r\n"; | |
165 | $this->body .= 'Content-Disposition: form-data; name="'.$key.'"'."\r\n\r\n"; | |
166 | $this->body .= $v."\r\n"; | |
167 | } | |
168 | } | |
169 | } | |
170 | ||
171 | $iterator = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($this->files), \RecursiveIteratorIterator::SELF_FIRST); | |
172 | foreach ($iterator as $k => $v) { | |
173 | if (!$iterator->hasChildren()) { | |
174 | $key = ''; | |
175 | for ($i = 0, $max = $iterator->getDepth(); $i <= $max; $i++) { | |
176 | if ($i === 0) $key .= $iterator->getSubIterator($i)->key(); | |
177 | else $key .= '['.$iterator->getSubIterator($i)->key().']'; | |
178 | } | |
179 | ||
180 | $this->body .= "--".$boundary."\r\n"; | |
181 | $this->body .= 'Content-Disposition: form-data; name="'.$k.'"; filename="'.basename($v).'"'."\r\n"; | |
182 | $this->body .= 'Content-Type: '.(FileUtil::getMimeType($v) ?: 'application/octet-stream.')."\r\n\r\n"; | |
183 | $this->body .= file_get_contents($v)."\r\n"; | |
184 | } | |
185 | } | |
186 | ||
187 | $this->body .= "--".$boundary."--"; | |
188 | } | |
8fe14bd6 | 189 | $this->addHeader('content-length', strlen($this->body)); |
86fc0430 TD |
190 | } |
191 | if (isset($this->options['auth'])) { | |
8fe14bd6 | 192 | $this->addHeader('authorization', "Basic ".base64_encode($options['auth']['username'].":".$options['auth']['password'])); |
86fc0430 | 193 | } |
8fe14bd6 | 194 | $this->addHeader('connection', 'Close'); |
86fc0430 TD |
195 | } |
196 | ||
197 | /** | |
198 | * Parses the given URL and applies PROXY_SERVER_HTTP. | |
199 | * | |
a17de04e | 200 | * @param string $url |
86fc0430 TD |
201 | */ |
202 | private function setURL($url) { | |
adc039a5 | 203 | $parsedUrl = $originUrl = parse_url($url); |
86fc0430 TD |
204 | if (PROXY_SERVER_HTTP) { |
205 | $parsedUrl = parse_url(PROXY_SERVER_HTTP); | |
206 | $this->path = $url; | |
207 | } | |
208 | else { | |
86fc0430 TD |
209 | $this->path = isset($parsedUrl['path']) ? $parsedUrl['path'] : '/'; |
210 | } | |
d5bd7602 | 211 | |
86fc0430 TD |
212 | $this->useSSL = $parsedUrl['scheme'] === 'https'; |
213 | $this->host = $parsedUrl['host']; | |
214 | $this->port = isset($parsedUrl['port']) ? $parsedUrl['port'] : ($this->useSSL ? 443 : 80); | |
86fc0430 | 215 | $this->query = isset($parsedUrl['query']) ? $parsedUrl['query'] : ''; |
d5bd7602 AE |
216 | |
217 | // update the 'Host:' header if URL has changed | |
adc039a5 TD |
218 | if ($this->url != $url) { |
219 | $originUseSSL = $originUrl['scheme'] === 'https'; | |
220 | $originHost = $originUrl['host']; | |
221 | $originPort = isset($originUrl['port']) ? $originUrl['port'] : ($originUseSSL ? 443 : 80); | |
222 | $this->addHeader('host', $originHost.($originPort != ($originUseSSL ? 443 : 80) ? ':'.$originPort : '')); | |
d5bd7602 AE |
223 | } |
224 | ||
225 | $this->url = $url; | |
86fc0430 TD |
226 | } |
227 | ||
228 | /** | |
229 | * Executes the HTTP request. | |
230 | */ | |
231 | public function execute() { | |
232 | // connect | |
233 | $remoteFile = new RemoteFile(($this->useSSL ? 'ssl://' : '').$this->host, $this->port, $this->options['timeout']); | |
234 | ||
8fe14bd6 | 235 | $request = $this->options['method']." ".$this->path.($this->query ? '?'.$this->query : '')." HTTP/1.1\r\n"; |
86fc0430 | 236 | |
a195ffa6 | 237 | // add headers |
86fc0430 TD |
238 | foreach ($this->headers as $name => $values) { |
239 | foreach ($values as $value) { | |
240 | $request .= $name.": ".$value."\r\n"; | |
241 | } | |
242 | } | |
243 | $request .= "\r\n"; | |
8fe14bd6 | 244 | |
a195ffa6 | 245 | // add post parameters |
5f70a0de | 246 | if ($this->options['method'] !== 'GET') $request .= $this->body."\r\n\r\n"; |
d5bd7602 | 247 | |
86fc0430 TD |
248 | $remoteFile->puts($request); |
249 | ||
250 | $inHeader = true; | |
251 | $this->replyHeaders = array(); | |
252 | $this->replyBody = ''; | |
8fe14bd6 | 253 | $chunkLength = 0; |
5262964b | 254 | $bodyLength = 0; |
c7fe2510 | 255 | |
8d2262a1 | 256 | $chunkedTransferRegex = new Regex('(^|,)[ \t]*chunked[ \t]*$', Regex::CASE_INSENSITIVE); |
7ced4c6a TD |
257 | // read http response, until one of is true |
258 | // a) EOF is reached | |
259 | // b) bodyLength is at least maxLength | |
260 | // c) bodyLength is at least Content-Length | |
261 | while (!( | |
262 | $remoteFile->eof() || | |
263 | (isset($this->options['maxLength']) && $bodyLength >= $this->options['maxLength']) || | |
264 | (isset($this->replyHeaders['content-length']) && $bodyLength >= end($this->replyHeaders['content-length'])) | |
265 | )) { | |
266 | ||
8fe14bd6 | 267 | if ($chunkLength) { |
5262964b | 268 | if (isset($this->options['maxLength'])) $chunkLength = min($chunkLength, $this->options['maxLength'] - $bodyLength); |
8fe14bd6 TD |
269 | $line = $remoteFile->read($chunkLength); |
270 | } | |
8d2262a1 | 271 | else if (!$inHeader && (!isset($this->replyHeaders['transfer-encoding']) || !$chunkedTransferRegex->match(end($this->replyHeaders['transfer-encoding'])))) { |
4b3a6b71 TD |
272 | $length = 1024; |
273 | if (isset($this->options['maxLength'])) $length = min($length, $this->options['maxLength'] - $bodyLength); | |
7ced4c6a TD |
274 | if (isset($this->replyHeaders['content-length'])) $length = min($length, end($this->replyHeaders['content-length']) - $bodyLength); |
275 | ||
276 | $line = $remoteFile->read($length); | |
277 | } | |
8fe14bd6 TD |
278 | else { |
279 | $line = $remoteFile->gets(); | |
280 | } | |
281 | ||
86fc0430 TD |
282 | if ($inHeader) { |
283 | if (rtrim($line) === '') { | |
284 | $inHeader = false; | |
8fe14bd6 TD |
285 | $this->parseReplyHeaders(); |
286 | ||
86fc0430 TD |
287 | continue; |
288 | } | |
289 | $this->replyHeaders[] = $line; | |
290 | } | |
291 | else { | |
8fe14bd6 | 292 | if (isset($this->replyHeaders['transfer-encoding']) && $chunkedTransferRegex->match(end($this->replyHeaders['transfer-encoding']))) { |
8fe14bd6 TD |
293 | // last chunk finished |
294 | if ($chunkLength === 0) { | |
295 | // read hex data and trash chunk-extension | |
296 | list($hex) = explode(';', $line, 2); | |
297 | $chunkLength = hexdec($hex); | |
298 | ||
299 | // $chunkLength === 0 -> no more data | |
300 | if ($chunkLength === 0) { | |
301 | // clear remaining response | |
7ced4c6a | 302 | while (!$remoteFile->gets(1024)); |
8fe14bd6 | 303 | |
8f3e6138 TD |
304 | // remove chunked from transfer-encoding |
305 | $this->replyHeaders['transfer-encoding'] = array_filter(array_map(function ($element) use ($chunkedTransferRegex) { | |
306 | return $chunkedTransferRegex->replace($element, ''); | |
307 | }, $this->replyHeaders['transfer-encoding']), 'trim'); | |
308 | if (empty($this->replyHeaders['transfer-encoding'])) unset($this->replyHeaders['transfer-encoding']); | |
309 | ||
8fe14bd6 TD |
310 | // break out of main reading loop |
311 | break; | |
312 | } | |
313 | } | |
314 | else { | |
315 | $this->replyBody .= $line; | |
316 | $chunkLength -= strlen($line); | |
8f3e6138 TD |
317 | $bodyLength += strlen($line); |
318 | if ($chunkLength === 0) $remoteFile->read(2); // CRLF | |
8fe14bd6 TD |
319 | } |
320 | } | |
321 | else { | |
322 | $this->replyBody .= $line; | |
8f3e6138 | 323 | $bodyLength += strlen($line); |
5262964b | 324 | } |
86fc0430 TD |
325 | } |
326 | } | |
327 | ||
5262964b TD |
328 | if (isset($this->options['maxLength'])) $this->replyBody = substr($this->replyBody, 0, $this->options['maxLength']); |
329 | ||
8fe14bd6 TD |
330 | $remoteFile->close(); |
331 | ||
86fc0430 TD |
332 | $this->parseReply(); |
333 | } | |
334 | ||
335 | /** | |
8fe14bd6 | 336 | * Parses the reply headers. |
86fc0430 | 337 | */ |
8fe14bd6 | 338 | private function parseReplyHeaders() { |
86fc0430 | 339 | $headers = array(); |
8fe14bd6 | 340 | $lastKey = ''; |
86fc0430 TD |
341 | foreach ($this->replyHeaders as $header) { |
342 | if (strpos($header, ':') === false) { | |
8fe14bd6 | 343 | $headers[trim($header)] = array(trim($header)); |
86fc0430 TD |
344 | continue; |
345 | } | |
8fe14bd6 TD |
346 | |
347 | // 4.2 Header fields can be | |
348 | // extended over multiple lines by preceding each extra line with at | |
349 | // least one SP or HT. | |
350 | if (ltrim($header, "\t ") !== $header) { | |
351 | $headers[$lastKey][] = array_pop($headers[$lastKey]).' '.trim($header); | |
352 | } | |
353 | else { | |
354 | list($key, $value) = explode(':', $header, 2); | |
355 | ||
356 | $lastKey = $key; | |
357 | if (!isset($headers[$key])) $headers[$key] = array(); | |
358 | $headers[$key][] = trim($value); | |
359 | } | |
86fc0430 | 360 | } |
8fe14bd6 TD |
361 | // 4.2 Field names are case-insensitive. |
362 | $this->replyHeaders = array_change_key_case($headers); | |
363 | if (isset($this->replyHeaders['transfer-encoding'])) $this->replyHeaders['transfer-encoding'] = array(implode(',', $this->replyHeaders['transfer-encoding'])); | |
364 | $this->legacyHeaders = array_map('end', $headers); | |
86fc0430 | 365 | |
a0eb8370 | 366 | // get status code |
86fc0430 | 367 | $statusLine = reset($this->replyHeaders); |
8fe14bd6 TD |
368 | $regex = new Regex('^HTTP/1.\d+ +(\d{3})'); |
369 | if (!$regex->match($statusLine[0])) throw new SystemException("Unexpected status '".$statusLine."'"); | |
86fc0430 | 370 | $matches = $regex->getMatches(); |
aeaa135c | 371 | $this->statusCode = $matches[1]; |
8fe14bd6 TD |
372 | } |
373 | ||
374 | /** | |
375 | * Parses the reply. | |
376 | */ | |
377 | private function parseReply() { | |
378 | // 4.4 Messages MUST NOT include both a Content-Length header field and a | |
379 | // non-identity transfer-coding. If the message does include a non- | |
380 | // identity transfer-coding, the Content-Length MUST be ignored. | |
5262964b | 381 | if (isset($this->replyHeaders['content-length']) && (!isset($this->replyHeaders['transfer-encoding']) || strtolower(end($this->replyHeaders['transfer-encoding'])) !== 'identity') && !isset($this->options['maxLength'])) { |
8009cc11 | 382 | if (strlen($this->replyBody) != end($this->replyHeaders['content-length'])) { |
a0eb8370 DR |
383 | throw new SystemException('Body length does not match length given in header'); |
384 | } | |
385 | } | |
386 | ||
387 | // validate status code | |
aeaa135c | 388 | switch ($this->statusCode) { |
86fc0430 TD |
389 | case '301': |
390 | case '302': | |
391 | case '303': | |
392 | case '307': | |
393 | // redirect | |
c7fe2510 | 394 | if ($this->options['maxDepth'] <= 0) throw new SystemException("Received status code '".$this->statusCode."' from server, but recursion level is exhausted"); |
86fc0430 TD |
395 | |
396 | $newRequest = clone $this; | |
397 | $newRequest->options['maxDepth']--; | |
c7fe2510 | 398 | |
8fe14bd6 | 399 | // 10.3.4 The response to the request can be found under a different URI and SHOULD |
c7fe2510 | 400 | // be retrieved using a GET method on that resource. |
c7fe2510 | 401 | if ($this->statusCode == '303') { |
86fc0430 TD |
402 | $newRequest->options['method'] = 'GET'; |
403 | $newRequest->postParameters = array(); | |
8fe14bd6 TD |
404 | $newRequest->addHeader('content-length', ''); |
405 | $newRequest->addHeader('content-type', ''); | |
86fc0430 | 406 | } |
c7fe2510 | 407 | |
86fc0430 | 408 | try { |
8fe14bd6 | 409 | $newRequest->setURL(end($this->replyHeaders['location'])); |
86fc0430 TD |
410 | } |
411 | catch (SystemException $e) { | |
8fe14bd6 | 412 | throw new SystemException("Received 'Location: ".end($this->replyHeaders['location'])."' from server, which is invalid.", 0, $e); |
86fc0430 | 413 | } |
86fc0430 | 414 | |
283df336 TD |
415 | try { |
416 | $newRequest->execute(); | |
417 | ||
418 | // update data with data from the inner request | |
419 | $this->url = $newRequest->url; | |
420 | $this->statusCode = $newRequest->statusCode; | |
421 | $this->replyHeaders = $newRequest->replyHeaders; | |
df37e22d | 422 | $this->legacyHeaders = $newRequest->legacyHeaders; |
283df336 TD |
423 | $this->replyBody = $newRequest->replyBody; |
424 | } | |
425 | catch (SystemException $e) { | |
426 | // update data with data from the inner request | |
427 | $this->url = $newRequest->url; | |
428 | $this->statusCode = $newRequest->statusCode; | |
429 | $this->replyHeaders = $newRequest->replyHeaders; | |
df37e22d | 430 | $this->legacyHeaders = $newRequest->legacyHeaders; |
283df336 TD |
431 | $this->replyBody = $newRequest->replyBody; |
432 | ||
433 | throw $e; | |
434 | } | |
435 | ||
86fc0430 TD |
436 | return; |
437 | break; | |
a17de04e | 438 | |
4d28d5a2 SG |
439 | case '206': |
440 | // check, if partial content was expected | |
5262964b | 441 | if (!isset($this->headers['range'])) { |
4d28d5a2 SG |
442 | throw new HTTPServerErrorException("Received unexpected status code '206' from server"); |
443 | } | |
5262964b | 444 | else if (!isset($this->replyHeaders['content-range'])) { |
4d28d5a2 SG |
445 | throw new HTTPServerErrorException("Content-Range is missing in reply header"); |
446 | } | |
447 | break; | |
448 | ||
3536d2fe | 449 | case '401': |
5cd59413 | 450 | case '402': |
3536d2fe AE |
451 | case '403': |
452 | throw new HTTPUnauthorizedException("Received status code '".$this->statusCode."' from server"); | |
453 | break; | |
454 | ||
455 | case '404': | |
456 | throw new HTTPNotFoundException("Received status code '404' from server"); | |
457 | break; | |
8fe14bd6 | 458 | |
86fc0430 | 459 | default: |
8fe14bd6 TD |
460 | // 6.1.1 However, applications MUST |
461 | // understand the class of any status code, as indicated by the first | |
462 | // digit, and treat any unrecognized response as being equivalent to the | |
463 | // x00 status code of that class, with the exception that an | |
464 | // unrecognized response MUST NOT be cached. | |
465 | switch (substr($this->statusCode, 0, 1)) { | |
466 | case '2': // 200 and unknown 2XX | |
467 | case '3': // 300 and unknown 3XX | |
468 | // we are fine | |
469 | break; | |
470 | case '5': // 500 and unknown 5XX | |
471 | throw new HTTPServerErrorException("Received status code '".$this->statusCode."' from server"); | |
472 | break; | |
473 | default: | |
474 | throw new SystemException("Received unhandled status code '".$this->statusCode."' from server"); | |
475 | break; | |
476 | } | |
86fc0430 TD |
477 | break; |
478 | } | |
86fc0430 TD |
479 | } |
480 | ||
481 | /** | |
482 | * Returns an array with the replied data. | |
8fe14bd6 | 483 | * Note that the 'headers' element is deprecated and may be removed in the future. |
86fc0430 | 484 | * |
a17de04e | 485 | * @return array |
86fc0430 TD |
486 | */ |
487 | public function getReply() { | |
a195ffa6 TD |
488 | return array( |
489 | 'statusCode' => $this->statusCode, | |
8fe14bd6 TD |
490 | 'headers' => $this->legacyHeaders, |
491 | 'httpHeaders' => $this->replyHeaders, | |
25cdc083 AE |
492 | 'body' => $this->replyBody, |
493 | 'url' => $this->url | |
a195ffa6 | 494 | ); |
86fc0430 TD |
495 | } |
496 | ||
497 | /** | |
498 | * Sets options and applies default values when an option is omitted. | |
499 | * | |
a17de04e | 500 | * @param array $options |
86fc0430 TD |
501 | */ |
502 | private function setOptions(array $options) { | |
503 | if (!isset($options['timeout'])) { | |
c7fe2510 | 504 | $options['timeout'] = 10; |
86fc0430 TD |
505 | } |
506 | ||
507 | if (!isset($options['method'])) { | |
5f70a0de | 508 | $options['method'] = (!empty($this->postParameters) || !empty($this->files) ? 'POST' : 'GET'); |
86fc0430 TD |
509 | } |
510 | ||
511 | if (!isset($options['maxDepth'])) { | |
512 | $options['maxDepth'] = 2; | |
513 | } | |
514 | ||
515 | if (isset($options['auth'])) { | |
516 | if (!isset($options['auth']['username'])) { | |
c7fe2510 | 517 | throw new SystemException('Username is missing in authentification data.'); |
86fc0430 TD |
518 | } |
519 | if (!isset($options['auth']['password'])) { | |
c7fe2510 | 520 | throw new SystemException('Password is missing in authentification data.'); |
86fc0430 TD |
521 | } |
522 | } | |
523 | ||
524 | $this->options = $options; | |
525 | } | |
526 | ||
527 | /** | |
528 | * Adds a header to this request. | |
c7fe2510 | 529 | * When an empty value is given existing headers of this name will be removed. When append |
86fc0430 TD |
530 | * is set to false existing values will be overwritten. |
531 | * | |
a17de04e MS |
532 | * @param string $name |
533 | * @param string $value | |
534 | * @param boolean $append | |
86fc0430 TD |
535 | */ |
536 | public function addHeader($name, $value, $append = false) { | |
8fe14bd6 TD |
537 | // 4.2 Field names are case-insensitive. |
538 | $name = strtolower($name); | |
539 | ||
86fc0430 TD |
540 | if ($value === '') { |
541 | unset($this->headers[$name]); | |
542 | return; | |
543 | } | |
544 | ||
545 | if ($append && isset($this->headers[$name])) { | |
546 | $this->headers[$name][] = $value; | |
547 | } | |
a377993e TD |
548 | else { |
549 | $this->headers[$name] = array($value); | |
550 | } | |
86fc0430 TD |
551 | } |
552 | ||
553 | /** | |
554 | * Resets reply data when cloning. | |
555 | */ | |
556 | private function __clone() { | |
557 | $this->replyHeaders = array(); | |
558 | $this->replyBody = ''; | |
559 | $this->statusCode = 0; | |
560 | } | |
58e1d71f | 561 | } |