CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Reporting Security Issues
    • Privacy Policy
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Get Involved
    • Issues (GitHub)
    • Bakery
    • Featured Resources
    • Training
    • Meetups
    • My CakePHP
    • CakeFest
    • Newsletter
    • Linkedin
    • YouTube
    • Facebook
    • Twitter
    • Mastodon
    • Help & Support
    • Forum
    • Stack Overflow
    • Slack
    • Paid Support
CakePHP

C CakePHP 2.5 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.5
      • 4.2
      • 4.1
      • 4.0
      • 3.9
      • 3.8
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Packages

  • Cake
    • Cache
      • Engine
    • Configure
    • Console
      • Command
        • Task
    • Controller
      • Component
        • Acl
        • Auth
    • Core
    • Error
    • Event
    • I18n
    • Log
      • Engine
    • Model
      • Behavior
      • Datasource
        • Database
        • Session
      • Validator
    • Network
      • Email
      • Http
    • Routing
      • Filter
      • Route
    • TestSuite
      • Coverage
      • Fixture
      • Reporter
    • Utility
    • View
      • Helper

Classes

  • BasicAuthentication
  • DigestAuthentication
  • HttpResponse
  • HttpSocket
  • HttpSocketResponse
   1: <?php
   2: /**
   3:  * HTTP Socket connection class.
   4:  *
   5:  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
   6:  * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
   7:  *
   8:  * Licensed under The MIT License
   9:  * For full copyright and license information, please see the LICENSE.txt
  10:  * Redistributions of files must retain the above copyright notice.
  11:  *
  12:  * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  13:  * @link          http://cakephp.org CakePHP(tm) Project
  14:  * @package       Cake.Network.Http
  15:  * @since         CakePHP(tm) v 1.2.0
  16:  * @license       http://www.opensource.org/licenses/mit-license.php MIT License
  17:  */
  18: 
  19: App::uses('CakeSocket', 'Network');
  20: App::uses('Router', 'Routing');
  21: App::uses('Hash', 'Utility');
  22: 
  23: /**
  24:  * CakePHP network socket connection class.
  25:  *
  26:  * Core base class for HTTP network communication. HttpSocket can be used as an
  27:  * Object Oriented replacement for cURL in many places.
  28:  *
  29:  * @package       Cake.Network.Http
  30:  */
  31: class HttpSocket extends CakeSocket {
  32: 
  33: /**
  34:  * When one activates the $quirksMode by setting it to true, all checks meant to
  35:  * enforce RFC 2616 (HTTP/1.1 specs).
  36:  * will be disabled and additional measures to deal with non-standard responses will be enabled.
  37:  *
  38:  * @var bool
  39:  */
  40:     public $quirksMode = false;
  41: 
  42: /**
  43:  * Contain information about the last request (read only)
  44:  *
  45:  * @var array
  46:  */
  47:     public $request = array(
  48:         'method' => 'GET',
  49:         'uri' => array(
  50:             'scheme' => 'http',
  51:             'host' => null,
  52:             'port' => 80,
  53:             'user' => null,
  54:             'pass' => null,
  55:             'path' => null,
  56:             'query' => null,
  57:             'fragment' => null
  58:         ),
  59:         'version' => '1.1',
  60:         'body' => '',
  61:         'line' => null,
  62:         'header' => array(
  63:             'Connection' => 'close',
  64:             'User-Agent' => 'CakePHP'
  65:         ),
  66:         'raw' => null,
  67:         'redirect' => false,
  68:         'cookies' => array(),
  69:     );
  70: 
  71: /**
  72:  * Contain information about the last response (read only)
  73:  *
  74:  * @var array
  75:  */
  76:     public $response = null;
  77: 
  78: /**
  79:  * Response class name
  80:  *
  81:  * @var string
  82:  */
  83:     public $responseClass = 'HttpSocketResponse';
  84: 
  85: /**
  86:  * Configuration settings for the HttpSocket and the requests
  87:  *
  88:  * @var array
  89:  */
  90:     public $config = array(
  91:         'persistent' => false,
  92:         'host' => 'localhost',
  93:         'protocol' => 'tcp',
  94:         'port' => 80,
  95:         'timeout' => 30,
  96:         'ssl_verify_peer' => true,
  97:         'ssl_allow_self_signed' => false,
  98:         'ssl_verify_depth' => 5,
  99:         'ssl_verify_host' => true,
 100:         'request' => array(
 101:             'uri' => array(
 102:                 'scheme' => array('http', 'https'),
 103:                 'host' => 'localhost',
 104:                 'port' => array(80, 443)
 105:             ),
 106:             'redirect' => false,
 107:             'cookies' => array(),
 108:         )
 109:     );
 110: 
 111: /**
 112:  * Authentication settings
 113:  *
 114:  * @var array
 115:  */
 116:     protected $_auth = array();
 117: 
 118: /**
 119:  * Proxy settings
 120:  *
 121:  * @var array
 122:  */
 123:     protected $_proxy = array();
 124: 
 125: /**
 126:  * Resource to receive the content of request
 127:  *
 128:  * @var mixed
 129:  */
 130:     protected $_contentResource = null;
 131: 
 132: /**
 133:  * Build an HTTP Socket using the specified configuration.
 134:  *
 135:  * You can use a URL string to set the URL and use default configurations for
 136:  * all other options:
 137:  *
 138:  * `$http = new HttpSocket('http://cakephp.org/');`
 139:  *
 140:  * Or use an array to configure multiple options:
 141:  *
 142:  * {{{
 143:  * $http = new HttpSocket(array(
 144:  *    'host' => 'cakephp.org',
 145:  *    'timeout' => 20
 146:  * ));
 147:  * }}}
 148:  *
 149:  * See HttpSocket::$config for options that can be used.
 150:  *
 151:  * @param string|array $config Configuration information, either a string URL or an array of options.
 152:  */
 153:     public function __construct($config = array()) {
 154:         if (is_string($config)) {
 155:             $this->_configUri($config);
 156:         } elseif (is_array($config)) {
 157:             if (isset($config['request']['uri']) && is_string($config['request']['uri'])) {
 158:                 $this->_configUri($config['request']['uri']);
 159:                 unset($config['request']['uri']);
 160:             }
 161:             $this->config = Hash::merge($this->config, $config);
 162:         }
 163:         parent::__construct($this->config);
 164:     }
 165: 
 166: /**
 167:  * Set authentication settings.
 168:  *
 169:  * Accepts two forms of parameters. If all you need is a username + password, as with
 170:  * Basic authentication you can do the following:
 171:  *
 172:  * {{{
 173:  * $http->configAuth('Basic', 'mark', 'secret');
 174:  * }}}
 175:  *
 176:  * If you are using an authentication strategy that requires more inputs, like Digest authentication
 177:  * you can call `configAuth()` with an array of user information.
 178:  *
 179:  * {{{
 180:  * $http->configAuth('Digest', array(
 181:  *      'user' => 'mark',
 182:  *      'pass' => 'secret',
 183:  *      'realm' => 'my-realm',
 184:  *      'nonce' => 1235
 185:  * ));
 186:  * }}}
 187:  *
 188:  * To remove any set authentication strategy, call `configAuth()` with no parameters:
 189:  *
 190:  * `$http->configAuth();`
 191:  *
 192:  * @param string $method Authentication method (ie. Basic, Digest). If empty, disable authentication
 193:  * @param string|array $user Username for authentication. Can be an array with settings to authentication class
 194:  * @param string $pass Password for authentication
 195:  * @return void
 196:  */
 197:     public function configAuth($method, $user = null, $pass = null) {
 198:         if (empty($method)) {
 199:             $this->_auth = array();
 200:             return;
 201:         }
 202:         if (is_array($user)) {
 203:             $this->_auth = array($method => $user);
 204:             return;
 205:         }
 206:         $this->_auth = array($method => compact('user', 'pass'));
 207:     }
 208: 
 209: /**
 210:  * Set proxy settings
 211:  *
 212:  * @param string|array $host Proxy host. Can be an array with settings to authentication class
 213:  * @param int $port Port. Default 3128.
 214:  * @param string $method Proxy method (ie, Basic, Digest). If empty, disable proxy authentication
 215:  * @param string $user Username if your proxy need authentication
 216:  * @param string $pass Password to proxy authentication
 217:  * @return void
 218:  */
 219:     public function configProxy($host, $port = 3128, $method = null, $user = null, $pass = null) {
 220:         if (empty($host)) {
 221:             $this->_proxy = array();
 222:             return;
 223:         }
 224:         if (is_array($host)) {
 225:             $this->_proxy = $host + array('host' => null);
 226:             return;
 227:         }
 228:         $this->_proxy = compact('host', 'port', 'method', 'user', 'pass');
 229:     }
 230: 
 231: /**
 232:  * Set the resource to receive the request content. This resource must support fwrite.
 233:  *
 234:  * @param resource|bool $resource Resource or false to disable the resource use
 235:  * @return void
 236:  * @throws SocketException
 237:  */
 238:     public function setContentResource($resource) {
 239:         if ($resource === false) {
 240:             $this->_contentResource = null;
 241:             return;
 242:         }
 243:         if (!is_resource($resource)) {
 244:             throw new SocketException(__d('cake_dev', 'Invalid resource.'));
 245:         }
 246:         $this->_contentResource = $resource;
 247:     }
 248: 
 249: /**
 250:  * Issue the specified request. HttpSocket::get() and HttpSocket::post() wrap this
 251:  * method and provide a more granular interface.
 252:  *
 253:  * @param string|array $request Either an URI string, or an array defining host/uri
 254:  * @return mixed false on error, HttpSocketResponse on success
 255:  * @throws SocketException
 256:  */
 257:     public function request($request = array()) {
 258:         $this->reset(false);
 259: 
 260:         if (is_string($request)) {
 261:             $request = array('uri' => $request);
 262:         } elseif (!is_array($request)) {
 263:             return false;
 264:         }
 265: 
 266:         if (!isset($request['uri'])) {
 267:             $request['uri'] = null;
 268:         }
 269:         $uri = $this->_parseUri($request['uri']);
 270:         if (!isset($uri['host'])) {
 271:             $host = $this->config['host'];
 272:         }
 273:         if (isset($request['host'])) {
 274:             $host = $request['host'];
 275:             unset($request['host']);
 276:         }
 277:         $request['uri'] = $this->url($request['uri']);
 278:         $request['uri'] = $this->_parseUri($request['uri'], true);
 279:         $this->request = Hash::merge($this->request, array_diff_key($this->config['request'], array('cookies' => true)), $request);
 280: 
 281:         $this->_configUri($this->request['uri']);
 282: 
 283:         $Host = $this->request['uri']['host'];
 284:         if (!empty($this->config['request']['cookies'][$Host])) {
 285:             if (!isset($this->request['cookies'])) {
 286:                 $this->request['cookies'] = array();
 287:             }
 288:             if (!isset($request['cookies'])) {
 289:                 $request['cookies'] = array();
 290:             }
 291:             $this->request['cookies'] = array_merge($this->request['cookies'], $this->config['request']['cookies'][$Host], $request['cookies']);
 292:         }
 293: 
 294:         if (isset($host)) {
 295:             $this->config['host'] = $host;
 296:         }
 297:         $this->_setProxy();
 298:         $this->request['proxy'] = $this->_proxy;
 299: 
 300:         $cookies = null;
 301: 
 302:         if (is_array($this->request['header'])) {
 303:             if (!empty($this->request['cookies'])) {
 304:                 $cookies = $this->buildCookies($this->request['cookies']);
 305:             }
 306:             $scheme = '';
 307:             $port = 0;
 308:             if (isset($this->request['uri']['scheme'])) {
 309:                 $scheme = $this->request['uri']['scheme'];
 310:             }
 311:             if (isset($this->request['uri']['port'])) {
 312:                 $port = $this->request['uri']['port'];
 313:             }
 314:             if (($scheme === 'http' && $port != 80) ||
 315:                 ($scheme === 'https' && $port != 443) ||
 316:                 ($port != 80 && $port != 443)
 317:             ) {
 318:                 $Host .= ':' . $port;
 319:             }
 320:             $this->request['header'] = array_merge(compact('Host'), $this->request['header']);
 321:         }
 322: 
 323:         if (isset($this->request['uri']['user'], $this->request['uri']['pass'])) {
 324:             $this->configAuth('Basic', $this->request['uri']['user'], $this->request['uri']['pass']);
 325:         } elseif (isset($this->request['auth'], $this->request['auth']['method'], $this->request['auth']['user'], $this->request['auth']['pass'])) {
 326:             $this->configAuth($this->request['auth']['method'], $this->request['auth']['user'], $this->request['auth']['pass']);
 327:         }
 328:         $authHeader = Hash::get($this->request, 'header.Authorization');
 329:         if (empty($authHeader)) {
 330:             $this->_setAuth();
 331:             $this->request['auth'] = $this->_auth;
 332:         }
 333: 
 334:         if (is_array($this->request['body'])) {
 335:             $this->request['body'] = http_build_query($this->request['body'], '', '&');
 336:         }
 337: 
 338:         if (!empty($this->request['body']) && !isset($this->request['header']['Content-Type'])) {
 339:             $this->request['header']['Content-Type'] = 'application/x-www-form-urlencoded';
 340:         }
 341: 
 342:         if (!empty($this->request['body']) && !isset($this->request['header']['Content-Length'])) {
 343:             $this->request['header']['Content-Length'] = strlen($this->request['body']);
 344:         }
 345: 
 346:         $connectionType = null;
 347:         if (isset($this->request['header']['Connection'])) {
 348:             $connectionType = $this->request['header']['Connection'];
 349:         }
 350:         $this->request['header'] = $this->_buildHeader($this->request['header']) . $cookies;
 351: 
 352:         if (empty($this->request['line'])) {
 353:             $this->request['line'] = $this->_buildRequestLine($this->request);
 354:         }
 355: 
 356:         if ($this->quirksMode === false && $this->request['line'] === false) {
 357:             return false;
 358:         }
 359: 
 360:         $this->_configContext($this->request['uri']['host']);
 361: 
 362:         $this->request['raw'] = '';
 363:         if ($this->request['line'] !== false) {
 364:             $this->request['raw'] = $this->request['line'];
 365:         }
 366: 
 367:         if ($this->request['header'] !== false) {
 368:             $this->request['raw'] .= $this->request['header'];
 369:         }
 370: 
 371:         $this->request['raw'] .= "\r\n";
 372:         $this->request['raw'] .= $this->request['body'];
 373:         $this->write($this->request['raw']);
 374: 
 375:         $response = null;
 376:         $inHeader = true;
 377:         while ($data = $this->read()) {
 378:             if ($this->_contentResource) {
 379:                 if ($inHeader) {
 380:                     $response .= $data;
 381:                     $pos = strpos($response, "\r\n\r\n");
 382:                     if ($pos !== false) {
 383:                         $pos += 4;
 384:                         $data = substr($response, $pos);
 385:                         fwrite($this->_contentResource, $data);
 386: 
 387:                         $response = substr($response, 0, $pos);
 388:                         $inHeader = false;
 389:                     }
 390:                 } else {
 391:                     fwrite($this->_contentResource, $data);
 392:                     fflush($this->_contentResource);
 393:                 }
 394:             } else {
 395:                 $response .= $data;
 396:             }
 397:         }
 398: 
 399:         if ($connectionType === 'close') {
 400:             $this->disconnect();
 401:         }
 402: 
 403:         list($plugin, $responseClass) = pluginSplit($this->responseClass, true);
 404:         App::uses($responseClass, $plugin . 'Network/Http');
 405:         if (!class_exists($responseClass)) {
 406:             throw new SocketException(__d('cake_dev', 'Class %s not found.', $this->responseClass));
 407:         }
 408:         $this->response = new $responseClass($response);
 409: 
 410:         if (!empty($this->response->cookies)) {
 411:             if (!isset($this->config['request']['cookies'][$Host])) {
 412:                 $this->config['request']['cookies'][$Host] = array();
 413:             }
 414:             $this->config['request']['cookies'][$Host] = array_merge($this->config['request']['cookies'][$Host], $this->response->cookies);
 415:         }
 416: 
 417:         if ($this->request['redirect'] && $this->response->isRedirect()) {
 418:             $location = trim($this->response->getHeader('Location'), '=');
 419:             $request['uri'] = str_replace('%2F', '/', $location);
 420:             $request['redirect'] = is_int($this->request['redirect']) ? $this->request['redirect'] - 1 : $this->request['redirect'];
 421:             $this->response = $this->request($request);
 422:         }
 423: 
 424:         return $this->response;
 425:     }
 426: 
 427: /**
 428:  * Issues a GET request to the specified URI, query, and request.
 429:  *
 430:  * Using a string uri and an array of query string parameters:
 431:  *
 432:  * `$response = $http->get('http://google.com/search', array('q' => 'cakephp', 'client' => 'safari'));`
 433:  *
 434:  * Would do a GET request to `http://google.com/search?q=cakephp&client=safari`
 435:  *
 436:  * You could express the same thing using a uri array and query string parameters:
 437:  *
 438:  * {{{
 439:  * $response = $http->get(
 440:  *     array('host' => 'google.com', 'path' => '/search'),
 441:  *     array('q' => 'cakephp', 'client' => 'safari')
 442:  * );
 443:  * }}}
 444:  *
 445:  * @param string|array $uri URI to request. Either a string uri, or a uri array, see HttpSocket::_parseUri()
 446:  * @param array $query Querystring parameters to append to URI
 447:  * @param array $request An indexed array with indexes such as 'method' or uri
 448:  * @return mixed Result of request, either false on failure or the response to the request.
 449:  */
 450:     public function get($uri = null, $query = array(), $request = array()) {
 451:         if (!empty($query)) {
 452:             $uri = $this->_parseUri($uri, $this->config['request']['uri']);
 453:             if (isset($uri['query'])) {
 454:                 $uri['query'] = array_merge($uri['query'], $query);
 455:             } else {
 456:                 $uri['query'] = $query;
 457:             }
 458:             $uri = $this->_buildUri($uri);
 459:         }
 460: 
 461:         $request = Hash::merge(array('method' => 'GET', 'uri' => $uri), $request);
 462:         return $this->request($request);
 463:     }
 464: 
 465: /**
 466:  * Issues a POST request to the specified URI, query, and request.
 467:  *
 468:  * `post()` can be used to post simple data arrays to a URL:
 469:  *
 470:  * {{{
 471:  * $response = $http->post('http://example.com', array(
 472:  *     'username' => 'batman',
 473:  *     'password' => 'bruce_w4yne'
 474:  * ));
 475:  * }}}
 476:  *
 477:  * @param string|array $uri URI to request. See HttpSocket::_parseUri()
 478:  * @param array $data Array of request body data keys and values.
 479:  * @param array $request An indexed array with indexes such as 'method' or uri
 480:  * @return mixed Result of request, either false on failure or the response to the request.
 481:  */
 482:     public function post($uri = null, $data = array(), $request = array()) {
 483:         $request = Hash::merge(array('method' => 'POST', 'uri' => $uri, 'body' => $data), $request);
 484:         return $this->request($request);
 485:     }
 486: 
 487: /**
 488:  * Issues a PUT request to the specified URI, query, and request.
 489:  *
 490:  * @param string|array $uri URI to request, See HttpSocket::_parseUri()
 491:  * @param array $data Array of request body data keys and values.
 492:  * @param array $request An indexed array with indexes such as 'method' or uri
 493:  * @return mixed Result of request
 494:  */
 495:     public function put($uri = null, $data = array(), $request = array()) {
 496:         $request = Hash::merge(array('method' => 'PUT', 'uri' => $uri, 'body' => $data), $request);
 497:         return $this->request($request);
 498:     }
 499: 
 500: /**
 501:  * Issues a PATCH request to the specified URI, query, and request.
 502:  *
 503:  * @param string|array $uri URI to request, See HttpSocket::_parseUri()
 504:  * @param array $data Array of request body data keys and values.
 505:  * @param array $request An indexed array with indexes such as 'method' or uri
 506:  * @return mixed Result of request
 507:  */
 508:     public function patch($uri = null, $data = array(), $request = array()) {
 509:         $request = Hash::merge(array('method' => 'PATCH', 'uri' => $uri, 'body' => $data), $request);
 510:         return $this->request($request);
 511:     }
 512: 
 513: /**
 514:  * Issues a DELETE request to the specified URI, query, and request.
 515:  *
 516:  * @param string|array $uri URI to request (see {@link _parseUri()})
 517:  * @param array $data Array of request body data keys and values.
 518:  * @param array $request An indexed array with indexes such as 'method' or uri
 519:  * @return mixed Result of request
 520:  */
 521:     public function delete($uri = null, $data = array(), $request = array()) {
 522:         $request = Hash::merge(array('method' => 'DELETE', 'uri' => $uri, 'body' => $data), $request);
 523:         return $this->request($request);
 524:     }
 525: 
 526: /**
 527:  * Normalizes URLs into a $uriTemplate. If no template is provided
 528:  * a default one will be used. Will generate the URL using the
 529:  * current config information.
 530:  *
 531:  * ### Usage:
 532:  *
 533:  * After configuring part of the request parameters, you can use url() to generate
 534:  * URLs.
 535:  *
 536:  * {{{
 537:  * $http = new HttpSocket('http://www.cakephp.org');
 538:  * $url = $http->url('/search?q=bar');
 539:  * }}}
 540:  *
 541:  * Would return `http://www.cakephp.org/search?q=bar`
 542:  *
 543:  * url() can also be used with custom templates:
 544:  *
 545:  * `$url = $http->url('http://www.cakephp/search?q=socket', '/%path?%query');`
 546:  *
 547:  * Would return `/search?q=socket`.
 548:  *
 549:  * @param string|array $url Either a string or array of URL options to create a URL with.
 550:  * @param string $uriTemplate A template string to use for URL formatting.
 551:  * @return mixed Either false on failure or a string containing the composed URL.
 552:  */
 553:     public function url($url = null, $uriTemplate = null) {
 554:         if ($url === null) {
 555:             $url = '/';
 556:         }
 557:         if (is_string($url)) {
 558:             $scheme = $this->config['request']['uri']['scheme'];
 559:             if (is_array($scheme)) {
 560:                 $scheme = $scheme[0];
 561:             }
 562:             $port = $this->config['request']['uri']['port'];
 563:             if (is_array($port)) {
 564:                 $port = $port[0];
 565:             }
 566:             if ($url{0} === '/') {
 567:                 $url = $this->config['request']['uri']['host'] . ':' . $port . $url;
 568:             }
 569:             if (!preg_match('/^.+:\/\/|\*|^\//', $url)) {
 570:                 $url = $scheme . '://' . $url;
 571:             }
 572:         } elseif (!is_array($url) && !empty($url)) {
 573:             return false;
 574:         }
 575: 
 576:         $base = array_merge($this->config['request']['uri'], array('scheme' => array('http', 'https'), 'port' => array(80, 443)));
 577:         $url = $this->_parseUri($url, $base);
 578: 
 579:         if (empty($url)) {
 580:             $url = $this->config['request']['uri'];
 581:         }
 582: 
 583:         if (!empty($uriTemplate)) {
 584:             return $this->_buildUri($url, $uriTemplate);
 585:         }
 586:         return $this->_buildUri($url);
 587:     }
 588: 
 589: /**
 590:  * Set authentication in request
 591:  *
 592:  * @return void
 593:  * @throws SocketException
 594:  */
 595:     protected function _setAuth() {
 596:         if (empty($this->_auth)) {
 597:             return;
 598:         }
 599:         $method = key($this->_auth);
 600:         list($plugin, $authClass) = pluginSplit($method, true);
 601:         $authClass = Inflector::camelize($authClass) . 'Authentication';
 602:         App::uses($authClass, $plugin . 'Network/Http');
 603: 
 604:         if (!class_exists($authClass)) {
 605:             throw new SocketException(__d('cake_dev', 'Unknown authentication method.'));
 606:         }
 607:         if (!method_exists($authClass, 'authentication')) {
 608:             throw new SocketException(__d('cake_dev', 'The %s does not support authentication.', $authClass));
 609:         }
 610:         call_user_func_array("$authClass::authentication", array($this, &$this->_auth[$method]));
 611:     }
 612: 
 613: /**
 614:  * Set the proxy configuration and authentication
 615:  *
 616:  * @return void
 617:  * @throws SocketException
 618:  */
 619:     protected function _setProxy() {
 620:         if (empty($this->_proxy) || !isset($this->_proxy['host'], $this->_proxy['port'])) {
 621:             return;
 622:         }
 623:         $this->config['host'] = $this->_proxy['host'];
 624:         $this->config['port'] = $this->_proxy['port'];
 625: 
 626:         if (empty($this->_proxy['method']) || !isset($this->_proxy['user'], $this->_proxy['pass'])) {
 627:             return;
 628:         }
 629:         list($plugin, $authClass) = pluginSplit($this->_proxy['method'], true);
 630:         $authClass = Inflector::camelize($authClass) . 'Authentication';
 631:         App::uses($authClass, $plugin . 'Network/Http');
 632: 
 633:         if (!class_exists($authClass)) {
 634:             throw new SocketException(__d('cake_dev', 'Unknown authentication method for proxy.'));
 635:         }
 636:         if (!method_exists($authClass, 'proxyAuthentication')) {
 637:             throw new SocketException(__d('cake_dev', 'The %s does not support proxy authentication.', $authClass));
 638:         }
 639:         call_user_func_array("$authClass::proxyAuthentication", array($this, &$this->_proxy));
 640:     }
 641: 
 642: /**
 643:  * Parses and sets the specified URI into current request configuration.
 644:  *
 645:  * @param string|array $uri URI, See HttpSocket::_parseUri()
 646:  * @return bool If uri has merged in config
 647:  */
 648:     protected function _configUri($uri = null) {
 649:         if (empty($uri)) {
 650:             return false;
 651:         }
 652: 
 653:         if (is_array($uri)) {
 654:             $uri = $this->_parseUri($uri);
 655:         } else {
 656:             $uri = $this->_parseUri($uri, true);
 657:         }
 658: 
 659:         if (!isset($uri['host'])) {
 660:             return false;
 661:         }
 662:         $config = array(
 663:             'request' => array(
 664:                 'uri' => array_intersect_key($uri, $this->config['request']['uri'])
 665:             )
 666:         );
 667:         $this->config = Hash::merge($this->config, $config);
 668:         $this->config = Hash::merge($this->config, array_intersect_key($this->config['request']['uri'], $this->config));
 669:         return true;
 670:     }
 671: 
 672: /**
 673:  * Configure the socket's context. Adds in configuration
 674:  * that can not be declared in the class definition.
 675:  *
 676:  * @param string $host The host you're connecting to.
 677:  * @return void
 678:  */
 679:     protected function _configContext($host) {
 680:         foreach ($this->config as $key => $value) {
 681:             if (substr($key, 0, 4) !== 'ssl_') {
 682:                 continue;
 683:             }
 684:             $contextKey = substr($key, 4);
 685:             if (empty($this->config['context']['ssl'][$contextKey])) {
 686:                 $this->config['context']['ssl'][$contextKey] = $value;
 687:             }
 688:             unset($this->config[$key]);
 689:         }
 690:         if (empty($this->config['context']['ssl']['cafile'])) {
 691:             $this->config['context']['ssl']['cafile'] = CAKE . 'Config' . DS . 'cacert.pem';
 692:         }
 693:         if (!empty($this->config['context']['ssl']['verify_host'])) {
 694:             $this->config['context']['ssl']['CN_match'] = $host;
 695:         }
 696:         unset($this->config['context']['ssl']['verify_host']);
 697:     }
 698: 
 699: /**
 700:  * Takes a $uri array and turns it into a fully qualified URL string
 701:  *
 702:  * @param string|array $uri Either A $uri array, or a request string. Will use $this->config if left empty.
 703:  * @param string $uriTemplate The Uri template/format to use.
 704:  * @return mixed A fully qualified URL formatted according to $uriTemplate, or false on failure
 705:  */
 706:     protected function _buildUri($uri = array(), $uriTemplate = '%scheme://%user:%pass@%host:%port/%path?%query#%fragment') {
 707:         if (is_string($uri)) {
 708:             $uri = array('host' => $uri);
 709:         }
 710:         $uri = $this->_parseUri($uri, true);
 711: 
 712:         if (!is_array($uri) || empty($uri)) {
 713:             return false;
 714:         }
 715: 
 716:         $uri['path'] = preg_replace('/^\//', null, $uri['path']);
 717:         $uri['query'] = http_build_query($uri['query'], '', '&');
 718:         $uri['query'] = rtrim($uri['query'], '=');
 719:         $stripIfEmpty = array(
 720:             'query' => '?%query',
 721:             'fragment' => '#%fragment',
 722:             'user' => '%user:%pass@',
 723:             'host' => '%host:%port/'
 724:         );
 725: 
 726:         foreach ($stripIfEmpty as $key => $strip) {
 727:             if (empty($uri[$key])) {
 728:                 $uriTemplate = str_replace($strip, null, $uriTemplate);
 729:             }
 730:         }
 731: 
 732:         $defaultPorts = array('http' => 80, 'https' => 443);
 733:         if (array_key_exists($uri['scheme'], $defaultPorts) && $defaultPorts[$uri['scheme']] == $uri['port']) {
 734:             $uriTemplate = str_replace(':%port', null, $uriTemplate);
 735:         }
 736:         foreach ($uri as $property => $value) {
 737:             $uriTemplate = str_replace('%' . $property, $value, $uriTemplate);
 738:         }
 739: 
 740:         if ($uriTemplate === '/*') {
 741:             $uriTemplate = '*';
 742:         }
 743:         return $uriTemplate;
 744:     }
 745: 
 746: /**
 747:  * Parses the given URI and breaks it down into pieces as an indexed array with elements
 748:  * such as 'scheme', 'port', 'query'.
 749:  *
 750:  * @param string|array $uri URI to parse
 751:  * @param bool|array $base If true use default URI config, otherwise indexed array to set 'scheme', 'host', 'port', etc.
 752:  * @return array Parsed URI
 753:  */
 754:     protected function _parseUri($uri = null, $base = array()) {
 755:         $uriBase = array(
 756:             'scheme' => array('http', 'https'),
 757:             'host' => null,
 758:             'port' => array(80, 443),
 759:             'user' => null,
 760:             'pass' => null,
 761:             'path' => '/',
 762:             'query' => null,
 763:             'fragment' => null
 764:         );
 765: 
 766:         if (is_string($uri)) {
 767:             $uri = parse_url($uri);
 768:         }
 769:         if (!is_array($uri) || empty($uri)) {
 770:             return false;
 771:         }
 772:         if ($base === true) {
 773:             $base = $uriBase;
 774:         }
 775: 
 776:         if (isset($base['port'], $base['scheme']) && is_array($base['port']) && is_array($base['scheme'])) {
 777:             if (isset($uri['scheme']) && !isset($uri['port'])) {
 778:                 $base['port'] = $base['port'][array_search($uri['scheme'], $base['scheme'])];
 779:             } elseif (isset($uri['port']) && !isset($uri['scheme'])) {
 780:                 $base['scheme'] = $base['scheme'][array_search($uri['port'], $base['port'])];
 781:             }
 782:         }
 783: 
 784:         if (is_array($base) && !empty($base)) {
 785:             $uri = array_merge($base, $uri);
 786:         }
 787: 
 788:         if (isset($uri['scheme']) && is_array($uri['scheme'])) {
 789:             $uri['scheme'] = array_shift($uri['scheme']);
 790:         }
 791:         if (isset($uri['port']) && is_array($uri['port'])) {
 792:             $uri['port'] = array_shift($uri['port']);
 793:         }
 794: 
 795:         if (array_key_exists('query', $uri)) {
 796:             $uri['query'] = $this->_parseQuery($uri['query']);
 797:         }
 798: 
 799:         if (!array_intersect_key($uriBase, $uri)) {
 800:             return false;
 801:         }
 802:         return $uri;
 803:     }
 804: 
 805: /**
 806:  * This function can be thought of as a reverse to PHP5's http_build_query(). It takes a given query string and turns it into an array and
 807:  * supports nesting by using the php bracket syntax. So this means you can parse queries like:
 808:  *
 809:  * - ?key[subKey]=value
 810:  * - ?key[]=value1&key[]=value2
 811:  *
 812:  * A leading '?' mark in $query is optional and does not effect the outcome of this function.
 813:  * For the complete capabilities of this implementation take a look at HttpSocketTest::testparseQuery()
 814:  *
 815:  * @param string|array $query A query string to parse into an array or an array to return directly "as is"
 816:  * @return array The $query parsed into a possibly multi-level array. If an empty $query is
 817:  *     given, an empty array is returned.
 818:  */
 819:     protected function _parseQuery($query) {
 820:         if (is_array($query)) {
 821:             return $query;
 822:         }
 823: 
 824:         $parsedQuery = array();
 825: 
 826:         if (is_string($query) && !empty($query)) {
 827:             $query = preg_replace('/^\?/', '', $query);
 828:             $items = explode('&', $query);
 829: 
 830:             foreach ($items as $item) {
 831:                 if (strpos($item, '=') !== false) {
 832:                     list($key, $value) = explode('=', $item, 2);
 833:                 } else {
 834:                     $key = $item;
 835:                     $value = null;
 836:                 }
 837: 
 838:                 $key = urldecode($key);
 839:                 $value = urldecode($value);
 840: 
 841:                 if (preg_match_all('/\[([^\[\]]*)\]/iUs', $key, $matches)) {
 842:                     $subKeys = $matches[1];
 843:                     $rootKey = substr($key, 0, strpos($key, '['));
 844:                     if (!empty($rootKey)) {
 845:                         array_unshift($subKeys, $rootKey);
 846:                     }
 847:                     $queryNode =& $parsedQuery;
 848: 
 849:                     foreach ($subKeys as $subKey) {
 850:                         if (!is_array($queryNode)) {
 851:                             $queryNode = array();
 852:                         }
 853: 
 854:                         if ($subKey === '') {
 855:                             $queryNode[] = array();
 856:                             end($queryNode);
 857:                             $subKey = key($queryNode);
 858:                         }
 859:                         $queryNode =& $queryNode[$subKey];
 860:                     }
 861:                     $queryNode = $value;
 862:                     continue;
 863:                 }
 864:                 if (!isset($parsedQuery[$key])) {
 865:                     $parsedQuery[$key] = $value;
 866:                 } else {
 867:                     $parsedQuery[$key] = (array)$parsedQuery[$key];
 868:                     $parsedQuery[$key][] = $value;
 869:                 }
 870:             }
 871:         }
 872:         return $parsedQuery;
 873:     }
 874: 
 875: /**
 876:  * Builds a request line according to HTTP/1.1 specs. Activate quirks mode to work outside specs.
 877:  *
 878:  * @param array $request Needs to contain a 'uri' key. Should also contain a 'method' key, otherwise defaults to GET.
 879:  * @return string Request line
 880:  * @throws SocketException
 881:  */
 882:     protected function _buildRequestLine($request = array()) {
 883:         $asteriskMethods = array('OPTIONS');
 884: 
 885:         if (is_string($request)) {
 886:             $isValid = preg_match("/(.+) (.+) (.+)\r\n/U", $request, $match);
 887:             if (!$this->quirksMode && (!$isValid || ($match[2] === '*' && !in_array($match[3], $asteriskMethods)))) {
 888:                 throw new SocketException(__d('cake_dev', 'HttpSocket::_buildRequestLine - Passed an invalid request line string. Activate quirks mode to do this.'));
 889:             }
 890:             return $request;
 891:         } elseif (!is_array($request)) {
 892:             return false;
 893:         } elseif (!array_key_exists('uri', $request)) {
 894:             return false;
 895:         }
 896: 
 897:         $request['uri'] = $this->_parseUri($request['uri']);
 898:         $request += array('method' => 'GET');
 899:         if (!empty($this->_proxy['host'])) {
 900:             $request['uri'] = $this->_buildUri($request['uri'], '%scheme://%host:%port/%path?%query');
 901:         } else {
 902:             $request['uri'] = $this->_buildUri($request['uri'], '/%path?%query');
 903:         }
 904: 
 905:         if (!$this->quirksMode && $request['uri'] === '*' && !in_array($request['method'], $asteriskMethods)) {
 906:             throw new SocketException(__d('cake_dev', 'HttpSocket::_buildRequestLine - The "*" asterisk character is only allowed for the following methods: %s. Activate quirks mode to work outside of HTTP/1.1 specs.', implode(',', $asteriskMethods)));
 907:         }
 908:         $version = isset($request['version']) ? $request['version'] : '1.1';
 909:         return $request['method'] . ' ' . $request['uri'] . ' HTTP/' . $version . "\r\n";
 910:     }
 911: 
 912: /**
 913:  * Builds the header.
 914:  *
 915:  * @param array $header Header to build
 916:  * @param string $mode Mode
 917:  * @return string Header built from array
 918:  */
 919:     protected function _buildHeader($header, $mode = 'standard') {
 920:         if (is_string($header)) {
 921:             return $header;
 922:         } elseif (!is_array($header)) {
 923:             return false;
 924:         }
 925: 
 926:         $fieldsInHeader = array();
 927:         foreach ($header as $key => $value) {
 928:             $lowKey = strtolower($key);
 929:             if (array_key_exists($lowKey, $fieldsInHeader)) {
 930:                 $header[$fieldsInHeader[$lowKey]] = $value;
 931:                 unset($header[$key]);
 932:             } else {
 933:                 $fieldsInHeader[$lowKey] = $key;
 934:             }
 935:         }
 936: 
 937:         $returnHeader = '';
 938:         foreach ($header as $field => $contents) {
 939:             if (is_array($contents) && $mode === 'standard') {
 940:                 $contents = implode(',', $contents);
 941:             }
 942:             foreach ((array)$contents as $content) {
 943:                 $contents = preg_replace("/\r\n(?![\t ])/", "\r\n ", $content);
 944:                 $field = $this->_escapeToken($field);
 945: 
 946:                 $returnHeader .= $field . ': ' . $contents . "\r\n";
 947:             }
 948:         }
 949:         return $returnHeader;
 950:     }
 951: 
 952: /**
 953:  * Builds cookie headers for a request.
 954:  *
 955:  * Cookies can either be in the format returned in responses, or
 956:  * a simple key => value pair.
 957:  *
 958:  * @param array $cookies Array of cookies to send with the request.
 959:  * @return string Cookie header string to be sent with the request.
 960:  */
 961:     public function buildCookies($cookies) {
 962:         $header = array();
 963:         foreach ($cookies as $name => $cookie) {
 964:             if (is_array($cookie)) {
 965:                 $value = $this->_escapeToken($cookie['value'], array(';'));
 966:             } else {
 967:                 $value = $this->_escapeToken($cookie, array(';'));
 968:             }
 969:             $header[] = $name . '=' . $value;
 970:         }
 971:         return $this->_buildHeader(array('Cookie' => implode('; ', $header)), 'pragmatic');
 972:     }
 973: 
 974: /**
 975:  * Escapes a given $token according to RFC 2616 (HTTP 1.1 specs)
 976:  *
 977:  * @param string $token Token to escape
 978:  * @param array $chars Characters to escape
 979:  * @return string Escaped token
 980:  */
 981:     protected function _escapeToken($token, $chars = null) {
 982:         $regex = '/([' . implode('', $this->_tokenEscapeChars(true, $chars)) . '])/';
 983:         $token = preg_replace($regex, '"\\1"', $token);
 984:         return $token;
 985:     }
 986: 
 987: /**
 988:  * Gets escape chars according to RFC 2616 (HTTP 1.1 specs).
 989:  *
 990:  * @param bool $hex true to get them as HEX values, false otherwise
 991:  * @param array $chars Characters to escape
 992:  * @return array Escape chars
 993:  */
 994:     protected function _tokenEscapeChars($hex = true, $chars = null) {
 995:         if (!empty($chars)) {
 996:             $escape = $chars;
 997:         } else {
 998:             $escape = array('"', "(", ")", "<", ">", "@", ",", ";", ":", "\\", "/", "[", "]", "?", "=", "{", "}", " ");
 999:             for ($i = 0; $i <= 31; $i++) {
1000:                 $escape[] = chr($i);
1001:             }
1002:             $escape[] = chr(127);
1003:         }
1004: 
1005:         if (!$hex) {
1006:             return $escape;
1007:         }
1008:         foreach ($escape as $key => $char) {
1009:             $escape[$key] = '\\x' . str_pad(dechex(ord($char)), 2, '0', STR_PAD_LEFT);
1010:         }
1011:         return $escape;
1012:     }
1013: 
1014: /**
1015:  * Resets the state of this HttpSocket instance to it's initial state (before Object::__construct got executed) or does
1016:  * the same thing partially for the request and the response property only.
1017:  *
1018:  * @param bool $full If set to false only HttpSocket::response and HttpSocket::request are reset
1019:  * @return bool True on success
1020:  */
1021:     public function reset($full = true) {
1022:         static $initalState = array();
1023:         if (empty($initalState)) {
1024:             $initalState = get_class_vars(__CLASS__);
1025:         }
1026:         if (!$full) {
1027:             $this->request = $initalState['request'];
1028:             $this->response = $initalState['response'];
1029:             return true;
1030:         }
1031:         parent::reset($initalState);
1032:         return true;
1033:     }
1034: 
1035: }
1036: 
OpenHub
Rackspace
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Reporting Security Issues
  • Privacy Policy
  • Logos & Trademarks
  • Community
  • Get Involved
  • Issues (GitHub)
  • Bakery
  • Featured Resources
  • Training
  • Meetups
  • My CakePHP
  • CakeFest
  • Newsletter
  • Linkedin
  • YouTube
  • Facebook
  • Twitter
  • Mastodon
  • Help & Support
  • Forum
  • Stack Overflow
  • Slack
  • Paid Support

Generated using CakePHP API Docs