]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - extlib/HTTP/Request2/Adapter/Curl.php
c80c94584e5d0a35a38ab783edf97f2b71423d92
[quix0rs-gnu-social.git] / extlib / HTTP / Request2 / Adapter / Curl.php
1 <?php\r
2 /**\r
3  * Adapter for HTTP_Request2 wrapping around cURL extension\r
4  *\r
5  * PHP version 5\r
6  *\r
7  * LICENSE:\r
8  *\r
9  * Copyright (c) 2008-2012, Alexey Borzov <avb@php.net>\r
10  * All rights reserved.\r
11  *\r
12  * Redistribution and use in source and binary forms, with or without\r
13  * modification, are permitted provided that the following conditions\r
14  * are met:\r
15  *\r
16  *    * Redistributions of source code must retain the above copyright\r
17  *      notice, this list of conditions and the following disclaimer.\r
18  *    * Redistributions in binary form must reproduce the above copyright\r
19  *      notice, this list of conditions and the following disclaimer in the\r
20  *      documentation and/or other materials provided with the distribution.\r
21  *    * The names of the authors may not be used to endorse or promote products\r
22  *      derived from this software without specific prior written permission.\r
23  *\r
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS\r
25  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\r
26  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
27  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\r
28  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\r
29  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\r
30  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\r
31  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\r
32  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
33  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
34  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
35  *\r
36  * @category HTTP\r
37  * @package  HTTP_Request2\r
38  * @author   Alexey Borzov <avb@php.net>\r
39  * @license  http://opensource.org/licenses/bsd-license.php New BSD License\r
40  * @version  SVN: $Id: Curl.php 324746 2012-04-03 15:09:16Z avb $\r
41  * @link     http://pear.php.net/package/HTTP_Request2\r
42  */\r
43 \r
44 /**\r
45  * Base class for HTTP_Request2 adapters\r
46  */\r
47 require_once 'HTTP/Request2/Adapter.php';\r
48 \r
49 /**\r
50  * Adapter for HTTP_Request2 wrapping around cURL extension\r
51  *\r
52  * @category HTTP\r
53  * @package  HTTP_Request2\r
54  * @author   Alexey Borzov <avb@php.net>\r
55  * @license  http://opensource.org/licenses/bsd-license.php New BSD License\r
56  * @version  Release: 2.1.1\r
57  * @link     http://pear.php.net/package/HTTP_Request2\r
58  */\r
59 class HTTP_Request2_Adapter_Curl extends HTTP_Request2_Adapter\r
60 {\r
61     /**\r
62      * Mapping of header names to cURL options\r
63      * @var  array\r
64      */\r
65     protected static $headerMap = array(\r
66         'accept-encoding' => CURLOPT_ENCODING,\r
67         'cookie'          => CURLOPT_COOKIE,\r
68         'referer'         => CURLOPT_REFERER,\r
69         'user-agent'      => CURLOPT_USERAGENT\r
70     );\r
71 \r
72     /**\r
73      * Mapping of SSL context options to cURL options\r
74      * @var  array\r
75      */\r
76     protected static $sslContextMap = array(\r
77         'ssl_verify_peer' => CURLOPT_SSL_VERIFYPEER,\r
78         'ssl_cafile'      => CURLOPT_CAINFO,\r
79         'ssl_capath'      => CURLOPT_CAPATH,\r
80         'ssl_local_cert'  => CURLOPT_SSLCERT,\r
81         'ssl_passphrase'  => CURLOPT_SSLCERTPASSWD\r
82     );\r
83 \r
84     /**\r
85      * Mapping of CURLE_* constants to Exception subclasses and error codes\r
86      * @var  array\r
87      */\r
88     protected static $errorMap = array(\r
89         CURLE_UNSUPPORTED_PROTOCOL  => array('HTTP_Request2_MessageException',\r
90                                              HTTP_Request2_Exception::NON_HTTP_REDIRECT),\r
91         CURLE_COULDNT_RESOLVE_PROXY => array('HTTP_Request2_ConnectionException'),\r
92         CURLE_COULDNT_RESOLVE_HOST  => array('HTTP_Request2_ConnectionException'),\r
93         CURLE_COULDNT_CONNECT       => array('HTTP_Request2_ConnectionException'),\r
94         // error returned from write callback\r
95         CURLE_WRITE_ERROR           => array('HTTP_Request2_MessageException',\r
96                                              HTTP_Request2_Exception::NON_HTTP_REDIRECT),\r
97         CURLE_OPERATION_TIMEOUTED   => array('HTTP_Request2_MessageException',\r
98                                              HTTP_Request2_Exception::TIMEOUT),\r
99         CURLE_HTTP_RANGE_ERROR      => array('HTTP_Request2_MessageException'),\r
100         CURLE_SSL_CONNECT_ERROR     => array('HTTP_Request2_ConnectionException'),\r
101         CURLE_LIBRARY_NOT_FOUND     => array('HTTP_Request2_LogicException',\r
102                                              HTTP_Request2_Exception::MISCONFIGURATION),\r
103         CURLE_FUNCTION_NOT_FOUND    => array('HTTP_Request2_LogicException',\r
104                                              HTTP_Request2_Exception::MISCONFIGURATION),\r
105         CURLE_ABORTED_BY_CALLBACK   => array('HTTP_Request2_MessageException',\r
106                                              HTTP_Request2_Exception::NON_HTTP_REDIRECT),\r
107         CURLE_TOO_MANY_REDIRECTS    => array('HTTP_Request2_MessageException',\r
108                                              HTTP_Request2_Exception::TOO_MANY_REDIRECTS),\r
109         CURLE_SSL_PEER_CERTIFICATE  => array('HTTP_Request2_ConnectionException'),\r
110         CURLE_GOT_NOTHING           => array('HTTP_Request2_MessageException'),\r
111         CURLE_SSL_ENGINE_NOTFOUND   => array('HTTP_Request2_LogicException',\r
112                                              HTTP_Request2_Exception::MISCONFIGURATION),\r
113         CURLE_SSL_ENGINE_SETFAILED  => array('HTTP_Request2_LogicException',\r
114                                              HTTP_Request2_Exception::MISCONFIGURATION),\r
115         CURLE_SEND_ERROR            => array('HTTP_Request2_MessageException'),\r
116         CURLE_RECV_ERROR            => array('HTTP_Request2_MessageException'),\r
117         CURLE_SSL_CERTPROBLEM       => array('HTTP_Request2_LogicException',\r
118                                              HTTP_Request2_Exception::INVALID_ARGUMENT),\r
119         CURLE_SSL_CIPHER            => array('HTTP_Request2_ConnectionException'),\r
120         CURLE_SSL_CACERT            => array('HTTP_Request2_ConnectionException'),\r
121         CURLE_BAD_CONTENT_ENCODING  => array('HTTP_Request2_MessageException'),\r
122     );\r
123 \r
124     /**\r
125      * Response being received\r
126      * @var  HTTP_Request2_Response\r
127      */\r
128     protected $response;\r
129 \r
130     /**\r
131      * Whether 'sentHeaders' event was sent to observers\r
132      * @var  boolean\r
133      */\r
134     protected $eventSentHeaders = false;\r
135 \r
136     /**\r
137      * Whether 'receivedHeaders' event was sent to observers\r
138      * @var boolean\r
139      */\r
140     protected $eventReceivedHeaders = false;\r
141 \r
142     /**\r
143      * Position within request body\r
144      * @var  integer\r
145      * @see  callbackReadBody()\r
146      */\r
147     protected $position = 0;\r
148 \r
149     /**\r
150      * Information about last transfer, as returned by curl_getinfo()\r
151      * @var  array\r
152      */\r
153     protected $lastInfo;\r
154 \r
155     /**\r
156      * Creates a subclass of HTTP_Request2_Exception from curl error data\r
157      *\r
158      * @param resource $ch curl handle\r
159      *\r
160      * @return HTTP_Request2_Exception\r
161      */\r
162     protected static function wrapCurlError($ch)\r
163     {\r
164         $nativeCode = curl_errno($ch);\r
165         $message    = 'Curl error: ' . curl_error($ch);\r
166         if (!isset(self::$errorMap[$nativeCode])) {\r
167             return new HTTP_Request2_Exception($message, 0, $nativeCode);\r
168         } else {\r
169             $class = self::$errorMap[$nativeCode][0];\r
170             $code  = empty(self::$errorMap[$nativeCode][1])\r
171                      ? 0 : self::$errorMap[$nativeCode][1];\r
172             return new $class($message, $code, $nativeCode);\r
173         }\r
174     }\r
175 \r
176     /**\r
177      * Sends request to the remote server and returns its response\r
178      *\r
179      * @param HTTP_Request2 $request HTTP request message\r
180      *\r
181      * @return   HTTP_Request2_Response\r
182      * @throws   HTTP_Request2_Exception\r
183      */\r
184     public function sendRequest(HTTP_Request2 $request)\r
185     {\r
186         if (!extension_loaded('curl')) {\r
187             throw new HTTP_Request2_LogicException(\r
188                 'cURL extension not available', HTTP_Request2_Exception::MISCONFIGURATION\r
189             );\r
190         }\r
191 \r
192         $this->request              = $request;\r
193         $this->response             = null;\r
194         $this->position             = 0;\r
195         $this->eventSentHeaders     = false;\r
196         $this->eventReceivedHeaders = false;\r
197 \r
198         try {\r
199             if (false === curl_exec($ch = $this->createCurlHandle())) {\r
200                 $e = self::wrapCurlError($ch);\r
201             }\r
202         } catch (Exception $e) {\r
203         }\r
204         if (isset($ch)) {\r
205             $this->lastInfo = curl_getinfo($ch);\r
206             curl_close($ch);\r
207         }\r
208 \r
209         $response = $this->response;\r
210         unset($this->request, $this->requestBody, $this->response);\r
211 \r
212         if (!empty($e)) {\r
213             throw $e;\r
214         }\r
215 \r
216         if ($jar = $request->getCookieJar()) {\r
217             $jar->addCookiesFromResponse($response, $request->getUrl());\r
218         }\r
219 \r
220         if (0 < $this->lastInfo['size_download']) {\r
221             $request->setLastEvent('receivedBody', $response);\r
222         }\r
223         return $response;\r
224     }\r
225 \r
226     /**\r
227      * Returns information about last transfer\r
228      *\r
229      * @return   array   associative array as returned by curl_getinfo()\r
230      */\r
231     public function getInfo()\r
232     {\r
233         return $this->lastInfo;\r
234     }\r
235 \r
236     /**\r
237      * Creates a new cURL handle and populates it with data from the request\r
238      *\r
239      * @return   resource    a cURL handle, as created by curl_init()\r
240      * @throws   HTTP_Request2_LogicException\r
241      */\r
242     protected function createCurlHandle()\r
243     {\r
244         $ch = curl_init();\r
245 \r
246         curl_setopt_array($ch, array(\r
247             // setup write callbacks\r
248             CURLOPT_HEADERFUNCTION => array($this, 'callbackWriteHeader'),\r
249             CURLOPT_WRITEFUNCTION  => array($this, 'callbackWriteBody'),\r
250             // buffer size\r
251             CURLOPT_BUFFERSIZE     => $this->request->getConfig('buffer_size'),\r
252             // connection timeout\r
253             CURLOPT_CONNECTTIMEOUT => $this->request->getConfig('connect_timeout'),\r
254             // save full outgoing headers, in case someone is interested\r
255             CURLINFO_HEADER_OUT    => true,\r
256             // request url\r
257             CURLOPT_URL            => $this->request->getUrl()->getUrl()\r
258         ));\r
259 \r
260         // set up redirects\r
261         if (!$this->request->getConfig('follow_redirects')) {\r
262             curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);\r
263         } else {\r
264             if (!@curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true)) {\r
265                 throw new HTTP_Request2_LogicException(\r
266                     'Redirect support in curl is unavailable due to open_basedir or safe_mode setting',\r
267                     HTTP_Request2_Exception::MISCONFIGURATION\r
268                 );\r
269             }\r
270             curl_setopt($ch, CURLOPT_MAXREDIRS, $this->request->getConfig('max_redirects'));\r
271             // limit redirects to http(s), works in 5.2.10+\r
272             if (defined('CURLOPT_REDIR_PROTOCOLS')) {\r
273                 curl_setopt($ch, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);\r
274             }\r
275             // works in 5.3.2+, http://bugs.php.net/bug.php?id=49571\r
276             if ($this->request->getConfig('strict_redirects') && defined('CURLOPT_POSTREDIR')) {\r
277                 curl_setopt($ch, CURLOPT_POSTREDIR, 3);\r
278             }\r
279         }\r
280 \r
281         // request timeout\r
282         if ($timeout = $this->request->getConfig('timeout')) {\r
283             curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);\r
284         }\r
285 \r
286         // set HTTP version\r
287         switch ($this->request->getConfig('protocol_version')) {\r
288         case '1.0':\r
289             curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);\r
290             break;\r
291         case '1.1':\r
292             curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);\r
293         }\r
294 \r
295         // set request method\r
296         switch ($this->request->getMethod()) {\r
297         case HTTP_Request2::METHOD_GET:\r
298             curl_setopt($ch, CURLOPT_HTTPGET, true);\r
299             break;\r
300         case HTTP_Request2::METHOD_POST:\r
301             curl_setopt($ch, CURLOPT_POST, true);\r
302             break;\r
303         case HTTP_Request2::METHOD_HEAD:\r
304             curl_setopt($ch, CURLOPT_NOBODY, true);\r
305             break;\r
306         case HTTP_Request2::METHOD_PUT:\r
307             curl_setopt($ch, CURLOPT_UPLOAD, true);\r
308             break;\r
309         default:\r
310             curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->request->getMethod());\r
311         }\r
312 \r
313         // set proxy, if needed\r
314         if ($host = $this->request->getConfig('proxy_host')) {\r
315             if (!($port = $this->request->getConfig('proxy_port'))) {\r
316                 throw new HTTP_Request2_LogicException(\r
317                     'Proxy port not provided', HTTP_Request2_Exception::MISSING_VALUE\r
318                 );\r
319             }\r
320             curl_setopt($ch, CURLOPT_PROXY, $host . ':' . $port);\r
321             if ($user = $this->request->getConfig('proxy_user')) {\r
322                 curl_setopt(\r
323                     $ch, CURLOPT_PROXYUSERPWD,\r
324                     $user . ':' . $this->request->getConfig('proxy_password')\r
325                 );\r
326                 switch ($this->request->getConfig('proxy_auth_scheme')) {\r
327                 case HTTP_Request2::AUTH_BASIC:\r
328                     curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);\r
329                     break;\r
330                 case HTTP_Request2::AUTH_DIGEST:\r
331                     curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST);\r
332                 }\r
333             }\r
334             if ($type = $this->request->getConfig('proxy_type')) {\r
335                 switch ($type) {\r
336                 case 'http':\r
337                     curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);\r
338                     break;\r
339                 case 'socks5':\r
340                     curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);\r
341                     break;\r
342                 default:\r
343                     throw new HTTP_Request2_NotImplementedException(\r
344                         "Proxy type '{$type}' is not supported"\r
345                     );\r
346                 }\r
347             }\r
348         }\r
349 \r
350         // set authentication data\r
351         if ($auth = $this->request->getAuth()) {\r
352             curl_setopt($ch, CURLOPT_USERPWD, $auth['user'] . ':' . $auth['password']);\r
353             switch ($auth['scheme']) {\r
354             case HTTP_Request2::AUTH_BASIC:\r
355                 curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);\r
356                 break;\r
357             case HTTP_Request2::AUTH_DIGEST:\r
358                 curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);\r
359             }\r
360         }\r
361 \r
362         // set SSL options\r
363         foreach ($this->request->getConfig() as $name => $value) {\r
364             if ('ssl_verify_host' == $name && null !== $value) {\r
365                 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $value? 2: 0);\r
366             } elseif (isset(self::$sslContextMap[$name]) && null !== $value) {\r
367                 curl_setopt($ch, self::$sslContextMap[$name], $value);\r
368             }\r
369         }\r
370 \r
371         $headers = $this->request->getHeaders();\r
372         // make cURL automagically send proper header\r
373         if (!isset($headers['accept-encoding'])) {\r
374             $headers['accept-encoding'] = '';\r
375         }\r
376 \r
377         if (($jar = $this->request->getCookieJar())\r
378             && ($cookies = $jar->getMatching($this->request->getUrl(), true))\r
379         ) {\r
380             $headers['cookie'] = (empty($headers['cookie'])? '': $headers['cookie'] . '; ') . $cookies;\r
381         }\r
382 \r
383         // set headers having special cURL keys\r
384         foreach (self::$headerMap as $name => $option) {\r
385             if (isset($headers[$name])) {\r
386                 curl_setopt($ch, $option, $headers[$name]);\r
387                 unset($headers[$name]);\r
388             }\r
389         }\r
390 \r
391         $this->calculateRequestLength($headers);\r
392         if (isset($headers['content-length'])) {\r
393             $this->workaroundPhpBug47204($ch, $headers);\r
394         }\r
395 \r
396         // set headers not having special keys\r
397         $headersFmt = array();\r
398         foreach ($headers as $name => $value) {\r
399             $canonicalName = implode('-', array_map('ucfirst', explode('-', $name)));\r
400             $headersFmt[]  = $canonicalName . ': ' . $value;\r
401         }\r
402         curl_setopt($ch, CURLOPT_HTTPHEADER, $headersFmt);\r
403 \r
404         return $ch;\r
405     }\r
406 \r
407     /**\r
408      * Workaround for PHP bug #47204 that prevents rewinding request body\r
409      *\r
410      * The workaround consists of reading the entire request body into memory\r
411      * and setting it as CURLOPT_POSTFIELDS, so it isn't recommended for large\r
412      * file uploads, use Socket adapter instead.\r
413      *\r
414      * @param resource $ch       cURL handle\r
415      * @param array    &$headers Request headers\r
416      */\r
417     protected function workaroundPhpBug47204($ch, &$headers)\r
418     {\r
419         // no redirects, no digest auth -> probably no rewind needed\r
420         if (!$this->request->getConfig('follow_redirects')\r
421             && (!($auth = $this->request->getAuth())\r
422                 || HTTP_Request2::AUTH_DIGEST != $auth['scheme'])\r
423         ) {\r
424             curl_setopt($ch, CURLOPT_READFUNCTION, array($this, 'callbackReadBody'));\r
425 \r
426         } else {\r
427             // rewind may be needed, read the whole body into memory\r
428             if ($this->requestBody instanceof HTTP_Request2_MultipartBody) {\r
429                 $this->requestBody = $this->requestBody->__toString();\r
430 \r
431             } elseif (is_resource($this->requestBody)) {\r
432                 $fp = $this->requestBody;\r
433                 $this->requestBody = '';\r
434                 while (!feof($fp)) {\r
435                     $this->requestBody .= fread($fp, 16384);\r
436                 }\r
437             }\r
438             // curl hangs up if content-length is present\r
439             unset($headers['content-length']);\r
440             curl_setopt($ch, CURLOPT_POSTFIELDS, $this->requestBody);\r
441         }\r
442     }\r
443 \r
444     /**\r
445      * Callback function called by cURL for reading the request body\r
446      *\r
447      * @param resource $ch     cURL handle\r
448      * @param resource $fd     file descriptor (not used)\r
449      * @param integer  $length maximum length of data to return\r
450      *\r
451      * @return   string      part of the request body, up to $length bytes\r
452      */\r
453     protected function callbackReadBody($ch, $fd, $length)\r
454     {\r
455         if (!$this->eventSentHeaders) {\r
456             $this->request->setLastEvent(\r
457                 'sentHeaders', curl_getinfo($ch, CURLINFO_HEADER_OUT)\r
458             );\r
459             $this->eventSentHeaders = true;\r
460         }\r
461         if (in_array($this->request->getMethod(), self::$bodyDisallowed)\r
462             || 0 == $this->contentLength || $this->position >= $this->contentLength\r
463         ) {\r
464             return '';\r
465         }\r
466         if (is_string($this->requestBody)) {\r
467             $string = substr($this->requestBody, $this->position, $length);\r
468         } elseif (is_resource($this->requestBody)) {\r
469             $string = fread($this->requestBody, $length);\r
470         } else {\r
471             $string = $this->requestBody->read($length);\r
472         }\r
473         $this->request->setLastEvent('sentBodyPart', strlen($string));\r
474         $this->position += strlen($string);\r
475         return $string;\r
476     }\r
477 \r
478     /**\r
479      * Callback function called by cURL for saving the response headers\r
480      *\r
481      * @param resource $ch     cURL handle\r
482      * @param string   $string response header (with trailing CRLF)\r
483      *\r
484      * @return   integer     number of bytes saved\r
485      * @see      HTTP_Request2_Response::parseHeaderLine()\r
486      */\r
487     protected function callbackWriteHeader($ch, $string)\r
488     {\r
489         // we may receive a second set of headers if doing e.g. digest auth\r
490         if ($this->eventReceivedHeaders || !$this->eventSentHeaders) {\r
491             // don't bother with 100-Continue responses (bug #15785)\r
492             if (!$this->eventSentHeaders\r
493                 || $this->response->getStatus() >= 200\r
494             ) {\r
495                 $this->request->setLastEvent(\r
496                     'sentHeaders', curl_getinfo($ch, CURLINFO_HEADER_OUT)\r
497                 );\r
498             }\r
499             $upload = curl_getinfo($ch, CURLINFO_SIZE_UPLOAD);\r
500             // if body wasn't read by a callback, send event with total body size\r
501             if ($upload > $this->position) {\r
502                 $this->request->setLastEvent(\r
503                     'sentBodyPart', $upload - $this->position\r
504                 );\r
505                 $this->position = $upload;\r
506             }\r
507             if ($upload && (!$this->eventSentHeaders\r
508                             || $this->response->getStatus() >= 200)\r
509             ) {\r
510                 $this->request->setLastEvent('sentBody', $upload);\r
511             }\r
512             $this->eventSentHeaders = true;\r
513             // we'll need a new response object\r
514             if ($this->eventReceivedHeaders) {\r
515                 $this->eventReceivedHeaders = false;\r
516                 $this->response             = null;\r
517             }\r
518         }\r
519         if (empty($this->response)) {\r
520             $this->response = new HTTP_Request2_Response(\r
521                 $string, false, curl_getinfo($ch, CURLINFO_EFFECTIVE_URL)\r
522             );\r
523         } else {\r
524             $this->response->parseHeaderLine($string);\r
525             if ('' == trim($string)) {\r
526                 // don't bother with 100-Continue responses (bug #15785)\r
527                 if (200 <= $this->response->getStatus()) {\r
528                     $this->request->setLastEvent('receivedHeaders', $this->response);\r
529                 }\r
530 \r
531                 if ($this->request->getConfig('follow_redirects') && $this->response->isRedirect()) {\r
532                     $redirectUrl = new Net_URL2($this->response->getHeader('location'));\r
533 \r
534                     // for versions lower than 5.2.10, check the redirection URL protocol\r
535                     if (!defined('CURLOPT_REDIR_PROTOCOLS') && $redirectUrl->isAbsolute()\r
536                         && !in_array($redirectUrl->getScheme(), array('http', 'https'))\r
537                     ) {\r
538                         return -1;\r
539                     }\r
540 \r
541                     if ($jar = $this->request->getCookieJar()) {\r
542                         $jar->addCookiesFromResponse($this->response, $this->request->getUrl());\r
543                         if (!$redirectUrl->isAbsolute()) {\r
544                             $redirectUrl = $this->request->getUrl()->resolve($redirectUrl);\r
545                         }\r
546                         if ($cookies = $jar->getMatching($redirectUrl, true)) {\r
547                             curl_setopt($ch, CURLOPT_COOKIE, $cookies);\r
548                         }\r
549                     }\r
550                 }\r
551                 $this->eventReceivedHeaders = true;\r
552             }\r
553         }\r
554         return strlen($string);\r
555     }\r
556 \r
557     /**\r
558      * Callback function called by cURL for saving the response body\r
559      *\r
560      * @param resource $ch     cURL handle (not used)\r
561      * @param string   $string part of the response body\r
562      *\r
563      * @return   integer     number of bytes saved\r
564      * @throws   HTTP_Request2_MessageException\r
565      * @see      HTTP_Request2_Response::appendBody()\r
566      */\r
567     protected function callbackWriteBody($ch, $string)\r
568     {\r
569         // cURL calls WRITEFUNCTION callback without calling HEADERFUNCTION if\r
570         // response doesn't start with proper HTTP status line (see bug #15716)\r
571         if (empty($this->response)) {\r
572             throw new HTTP_Request2_MessageException(\r
573                 "Malformed response: {$string}",\r
574                 HTTP_Request2_Exception::MALFORMED_RESPONSE\r
575             );\r
576         }\r
577         if ($this->request->getConfig('store_body')) {\r
578             $this->response->appendBody($string);\r
579         }\r
580         $this->request->setLastEvent('receivedBodyPart', $string);\r
581         return strlen($string);\r
582     }\r
583 }\r
584 ?>\r