Overview

Namespaces

  • None
  • OpenCloud
    • Autoscale
      • Resource
    • CloudMonitoring
      • Exception
      • Resource
    • Common
      • Exceptions
      • Log
      • Request
        • Response
    • Compute
    • Database
    • DNS
    • LoadBalancer
      • Resources
    • ObjectStore
      • Resource
    • Orchestration
    • Volume
  • PHP

Classes

  • Curl

Interfaces

  • HttpRequestInterface
  • Overview
  • Namespace
  • Class
  • Tree
  • Download
  1: <?php
  2: 
  3: namespace OpenCloud\Common\Request;
  4: 
  5: use OpenCloud\Common\Base;
  6: use OpenCloud\Common\Lang;
  7: use OpenCloud\Common\Exceptions\HttpRetryError;
  8: use OpenCloud\Common\Exceptions\HttpUrlError;
  9: use OpenCloud\Common\Exceptions\HttpTimeoutError;
 10: use OpenCloud\Common\Exceptions\HttpError;
 11: 
 12: /**
 13:  * The CurlRequest class is a simple wrapper to CURL functions. Not only does
 14:  * this permit stubbing of the interface as described under the HttpRequest
 15:  * interface, it could potentially allow us to replace the interface methods
 16:  * with other function calls in the future.
 17:  *
 18:  * @api
 19:  * @author Glen Campbell <glen.campbell@rackspace.com>
 20:  */
 21: class Curl extends Base implements HttpRequestInterface
 22: {
 23: 
 24:     private $url;
 25:     private $method;
 26:     private $handle;
 27:     private $retries = 0;
 28:     private $headers = array();
 29:     private $returnheaders = array();
 30: 
 31:     /**
 32:      * Initializes the CURL handle and HTTP method
 33:      *
 34:      * The constructor also sets a number of default values for options.
 35:      *
 36:      * @param string $url the URL to connect to
 37:      * @param string $method the HTTP method (default "GET")
 38:      * @param array $options optional hashed array of options => value pairs
 39:      */
 40:     public function __construct($url, $method = 'GET', array $options = array())
 41:     {
 42:         $this->url = $url;
 43:         $this->method = $method;
 44:         $this->handle = curl_init($url);
 45: 
 46:         // set our options
 47:         $this->setOption(CURLOPT_CUSTOMREQUEST, $method);
 48: 
 49:         foreach($options as $opt => $value) {
 50:             $this->getLogger()->info(Lang::translate('Setting option {key}={val}'), array(
 51:                 'key' => $opt, 
 52:                 'val' => $value
 53:             ));
 54:             $this->setOption($opt, $value);
 55:         }
 56: 
 57:         // @codeCoverageIgnoreStart
 58:         if (RAXSDK_SSL_VERIFYHOST != 2) {
 59:             $this->getLogger()->warning("WARNING: RAXSDK_SSL_VERIFYHOST has reduced security, value [{value}]", array(
 60:                 'value' => RAXSDK_SSL_VERIFYHOST
 61:             ));
 62:         }
 63: 
 64:         if (RAXSDK_SSL_VERIFYPEER !== true) {
 65:             $this->getLogger()->warning("WARNING: RAXSDK_SSL_VERIFYPEER has reduced security");
 66:         }
 67:         // @codeCoverageIgnoreEnd
 68: 
 69:         $this->setOption(CURLOPT_SSL_VERIFYHOST, RAXSDK_SSL_VERIFYHOST);
 70:         $this->setOption(CURLOPT_SSL_VERIFYPEER, RAXSDK_SSL_VERIFYPEER);
 71: 
 72:         if (defined('RAXSDK_CACERTPEM') && file_exists(RAXSDK_CACERTPEM)) {
 73:             $this->setOption(CURLOPT_CAINFO, RAXSDK_CACERTPEM);
 74:         }
 75: 
 76:         //  curl code [18]
 77:         //  message [transfer closed with x bytes remaining to read]
 78:         if ($method === 'HEAD') {
 79:             $this->setOption(CURLOPT_NOBODY, true);
 80:         }
 81: 
 82:         // follow redirects
 83:         $this->setOption(CURLOPT_FOLLOWLOCATION, true);
 84: 
 85:         // don't return the headers in the request
 86:         $this->setOption(CURLOPT_HEADER, false);
 87: 
 88:         // retrieve headers via callback
 89:         $this->setOption(CURLOPT_HEADERFUNCTION, array($this, '_get_header_cb'));
 90: 
 91:         // return the entire request on curl_exec()
 92:         $this->setOption(CURLOPT_RETURNTRANSFER, true);
 93: 
 94:         // set default timeouts
 95:         $this->setConnectTimeout(RAXSDK_CONNECTTIMEOUT);
 96:         $this->setHttpTimeout(RAXSDK_TIMEOUT);
 97:     }
 98: 
 99:     /**
100:      * Sets a CURL option
101:      *
102:      * @param const $name - a CURL named constant; e.g. CURLOPT_TIMEOUT
103:      * @param mixed $value - the value for the option
104:      */
105:     public function setOption($name, $value)
106:     {
107:         return curl_setopt($this->handle, $name, $value);
108:     }
109: 
110:     /**
111:      * Explicit method for setting the connect timeout
112:      *
113:      * The connect timeout is the time it takes for the initial connection
114:      * request to be established. It is different than the HTTP timeout, which
115:      * is the time for the entire request to be serviced.
116:      *
117:      * @param integer $value The connection timeout in seconds.
118:      *      Use 0 to wait indefinitely (NOT recommended)
119:      */
120:     public function setConnectTimeout($value)
121:     {
122:         $this->setOption(CURLOPT_CONNECTTIMEOUT, $value);
123:     }
124: 
125:     /**
126:      * Explicit method for setting the HTTP timeout
127:      *
128:      * The HTTP timeout is the time it takes for the HTTP request to be
129:      * serviced. This value is usually larger than the connect timeout
130:      * value.
131:      *
132:      * @param integer $value - the number of seconds to wait before timing out
133:      *      the HTTP request.
134:      */
135:     public function setHttpTimeout($value)
136:     {
137:         $this->setOption(CURLOPT_TIMEOUT, $value);
138:     }
139: 
140:     /**
141:      * Sets the number of retries
142:      *
143:      * If you set this to a non-zero value, then it will repeat the request
144:      * up to that number.
145:      */
146:     public function setRetries($value)
147:     {
148:         $this->retries = $value;
149:     }
150: 
151:     /**
152:      * Simplified method for setting lots of headers at once
153:      *
154:      * This method takes an associative array of header/value pairs and calls
155:      * the setheader() method on each of them.
156:      *
157:      * @param array $arr an associative array of headers
158:      */
159:     public function setheaders($array)
160:     {
161:         if (!is_array($array)) {
162:             throw new HttpError(Lang::translate(
163:                 'Value passed to CurlRequest::setheaders() must be array'
164:             ));
165:         }
166: 
167:         foreach ($array as $name => $value) {
168:             $this->setHeader($name, $value);
169:         }
170:     }
171: 
172:     /**
173:      * Sets a single header
174:      *
175:      * For example, to set the content type to JSON:
176:      * `$request->SetHeader('Content-Type','application/json');`
177:      *
178:      * @param string $name The name of the header
179:      * @param mixed $value The value of the header
180:      */
181:     public function setHeader($name, $value)
182:     {
183:         $this->headers[$name] = $value;
184:     }
185: 
186:     /**
187:      * Executes the current request
188:      *
189:      * This method actually performs the request using the values set
190:      * previously. It throws a OpenCloud\HttpError exception on
191:      * any CURL error.
192:      *
193:      * @return OpenCloud\HttpResponse
194:      * @throws OpenCloud\HttpError
195:      * 
196:      * @codeCoverageIgnore
197:      */
198:     public function execute()
199:     {
200:         // set all the headers
201:         $headarr = array();
202: 
203:         foreach ($this->headers as $name => $value) {
204:             $headarr[] = $name.': '.$value;
205:         }
206: 
207:         $this->setOption(CURLOPT_HTTPHEADER, $headarr);
208: 
209:         // set up to retry if necessary
210:         $try_counter = 0;
211: 
212:         do {
213:             $data = curl_exec($this->handle);
214:             if (curl_errno($this->handle) && ($try_counter<$this->retries)) {
215:                 $this->getLogger()->info(Lang::translate('Curl error [%d]; retrying [%s]'), array(
216:                     'error' => curl_errno($this->handle), 
217:                     'url'   => $this->url
218:                 ));
219:             }
220: 
221:         } while((++$try_counter <= $this->retries) && (curl_errno($this->handle) != 0));
222: 
223:         // log retries error
224:         if ($this->retries && curl_errno($this->handle)) {
225:             throw new HttpRetryError(sprintf(
226:                 Lang::translate('No more retries available, last error [%d]'), 
227:                 curl_errno($this->handle)
228:             ));
229:         }
230: 
231:         // check for CURL errors
232:         switch(curl_errno($this->handle)) {
233:             case 0:
234:                 // everything's ok
235:                 break;
236:             case 3:
237:                 throw new HttpUrlError(sprintf(Lang::translate('Malformed URL [%s]'), $this->url));
238:                 break;
239:             case 28:
240:                 // timeout
241:                 throw new HttpTimeoutError(Lang::translate('Operation timed out; check RAXSDK_TIMEOUT value'));
242:                 break;
243:             default:
244:                 throw new HttpError(sprintf(
245:                     Lang::translate('HTTP error on [%s], curl code [%d] message [%s]'),
246:                     $this->url,
247:                     curl_errno($this->handle),
248:                     curl_error($this->handle)
249:                 ));
250:         }
251: 
252:         // otherwise, return the HttpResponse
253:         return new Response\Http($this, $data);
254:     }
255: 
256:     /**
257:      * returns an array of information about the request
258:      */
259:     public function info()
260:     {
261:         return curl_getinfo($this->handle);
262:     }
263: 
264:     /**
265:      * returns the most recent CURL error number
266:      */
267:     public function errno()
268:     {
269:         return curl_errno($this->handle);
270:     }
271: 
272:     /**
273:      * returns the most recent CURL error string
274:      */
275:     public function error()
276:     {
277:         return curl_error($this->handle);
278:     }
279: 
280:     /**
281:      * Closes the HTTP request
282:      */
283:     public function close()
284:     {
285:         return curl_close($this->handle);
286:     }
287: 
288:     /**
289:      * Returns the headers as an array
290:      */
291:     public function returnHeaders()
292:     {
293:         return $this->returnheaders;
294:     }
295: 
296:     /**
297:      * This is a callback method used to handle the returned HTTP headers
298:      *
299:      * @param mixed $ch a CURL handle
300:      * @param string $header the header string in its entirety
301:      */
302:     public function _get_header_cb($ch, $header)
303:     {
304:         $this->returnheaders[] = $header;
305:         return strlen($header);
306:     }
307: 
308: }
309: 
PHP OpenCloud API API documentation generated by ApiGen 2.8.0