4 * HTTP Parallel Request Library module.
8 * Default maximum number of seconds a single request call may take.
10 define('HTTPRL_TIMEOUT', 30.0);
13 * Default maximum number of seconds a function call may take.
15 define('HTTPRL_GLOBAL_TIMEOUT', 120.0);
18 * Error code indicating that the request made by httprl_request() exceeded
19 * the maximum allowed redirects without reaching the final target.
21 define('HTTPRL_REQUEST_ALLOWED_REDIRECTS_EXHAUSTED', -2);
24 * Error code indicating that the call to fwrite() failed.
26 define('HTTPRL_REQUEST_FWRITE_FAIL', -3);
29 * Error code indicating that all requests made by httprl_send_request
30 * exceeded the specified timeout.
32 define('HTTPRL_FUNCTION_TIMEOUT', -4);
35 * Error code indicating that this request made by stream_select() couldn't
36 * open a read and/or write steam after a minimum of ~2.5 seconds.
38 define('HTTPRL_STREAM_SELECT_TIMEOUT', -5);
41 * parse_url() was unable to parse the given url.
43 define('HTTPRL_URL_PARSE_ERROR', -1001);
46 * Given URL is missing a schema (http, https, feed).
48 define('HTTPRL_URL_MISSING_SCHEMA', -1002);
51 * Invalid schema. Only http, feed, and https allowed currently.
53 define('HTTPRL_URL_INVALID_SCHEMA', -1003);
56 * An error occurred before the system connect() call. This is most likely due
57 * to a problem initializing the stream.
59 define('HTTPRL_ERROR_INITIALIZING_STREAM', -1004);
62 * Error code indicating that software caused the connection to be aborted.
64 * @see http://msdn.microsoft.com/en-us/library/aa924071.aspx
66 define('HTTPRL_REQUEST_ABORTED', -10053);
69 * Error code indicating that the connection was forcibly closed by the remote
72 * @see http://msdn.microsoft.com/en-us/library/aa924071.aspx
74 define('HTTPRL_CONNECTION_RESET', -10054);
77 * Error code indicating that the request exceeded the specified timeout.
79 * @see http://msdn.microsoft.com/en-us/library/aa924071.aspx
81 define('HTTPRL_REQUEST_TIMEOUT', -10060);
84 * Error code indicating that the endpoint server has refused or dropped the
87 * @see http://msdn.microsoft.com/en-us/library/aa924071.aspx
89 define('HTTPRL_CONNECTION_REFUSED', -10061);
92 * Error code indicating that the host is unknown or can not be found.
94 * @see http://msdn.microsoft.com/en-us/library/aa924071.aspx
96 define('HTTPRL_HOST_NOT_FOUND', -11001);
99 * HTTP encapsulation boundary string.
101 define('HTTPRL_MULTIPART_BOUNDARY', '---------------------------' .
str_replace('.', '', microtime(TRUE
)));
104 * Implement hook_menu().
106 function httprl_menu() {
109 // Non blocking test URL.
110 $items['admin/httprl-test'] = array(
112 'page callback' => 'httprl_nonblockingtest_page',
113 'access callback' => TRUE
,
114 'description' => 'Test URL to make sure non blocking requests work.',
115 'type' => MENU_CALLBACK
,
116 'file' => 'httprl.nonblocktest.inc',
120 if (defined('VERSION') && substr(VERSION
, 0, 1) >= 7) {
121 $config_url = 'admin/config/development/httprl';
124 $config_url = 'admin/settings/httprl';
126 $items[$config_url] = array(
128 'description' => 'Configure HTTPRL settings.',
129 'access arguments' => array('administer site configuration'),
130 'page callback' => 'drupal_get_form',
131 'page arguments' => array('httprl_admin_settings_form'),
132 'type' => MENU_NORMAL_ITEM
,
133 'file' => 'httprl.admin.inc',
136 // Async Function Callback.
137 $items['httprl_async_function_callback'] = array(
139 'page callback' => 'httprl_async_page',
140 'access callback' => TRUE
,
141 'description' => 'URL for async function workers.',
142 'type' => MENU_CALLBACK
,
143 'file' => 'httprl.async.inc',
150 * Implement hook_cron().
152 * This hook should be ran about once a day to once an hour.
154 function httprl_cron() {
155 // Let expiration times vary by 30 seconds or so.
158 // Remove expired locks from the semaphore database table.
159 if (defined('VERSION') && substr(VERSION
, 0, 1) >= 7) {
160 db_delete('semaphore')
161 ->condition('value', 'httprl')
162 ->condition('expire', REQUEST_TIME
- $fuzz_factor, '<')
166 db_query("DELETE FROM {semaphore} WHERE value = 'httprl' AND expire < %f", time() - $fuzz_factor);
171 * Helper function to build an URL for asynchronous requests to self.
174 * Path to a URI excluding everything to the left and including the base path.
175 * @param $detect_schema
176 * If TRUE it will see if this request is https; if so, it will set the full
177 * url to be https as well.
179 function httprl_build_url_self($path = '', $detect_schema = FALSE
) {
180 global $base_path, $conf;
181 static
$variable_loaded = FALSE
;
183 if (!empty($base_path)) {
184 $root_path = $base_path;
187 // $_SERVER['SCRIPT_NAME'] can, in contrast to $_SERVER['PHP_SELF'], not
188 // be modified by a visitor.
189 if ($dir = trim(dirname($_SERVER['SCRIPT_NAME']), '\,/')) {
190 $base_path = "/$dir/";
199 if (isset($_SERVER['AUTH_TYPE']) && $_SERVER['AUTH_TYPE'] == 'Basic') {
200 $auth = $_SERVER['PHP_AUTH_USER'] .
':' .
$_SERVER['PHP_AUTH_PW'] .
'@';
204 $ip = httprl_variable_get('httprl_server_addr', FALSE
);
206 $ip = $_SERVER['HTTP_HOST'];
208 elseif (empty($ip)) {
209 $ip = empty($_SERVER['SERVER_ADDR']) ?
'127.0.0.1' : $_SERVER['SERVER_ADDR'];
210 // Check for IPv6. If IPv6 convert to IPv4 if possible.
211 if (strpos($ip, ':') !== FALSE
) {
212 if ($_SERVER['SERVER_ADDR'] == '::1') {
215 elseif (preg_match('/^::\d+.\d+.\d+.\d+$/', $ip)) {
216 $ip = substr($ip, 2);
218 elseif (!empty($_SERVER['HTTP_HOST'])) {
219 // Last option is to use the IP from the host name.
220 $ip = gethostbyname($_SERVER['HTTP_HOST']);
227 // if ( isset($_SERVER['SERVER_PORT'])
228 // && is_numeric($_SERVER['SERVER_PORT'])
229 // && ($_SERVER['SERVER_PORT'] != 80 || $_SERVER['SERVER_PORT'] != 443)
231 // $port = ':' . $_SERVER['SERVER_PORT'];
235 $schema = httprl_get_server_schema() .
'://';
237 return $schema .
$auth .
$ip .
$port .
$root_path .
$path;
241 * Run parse_url and handle any errors.
244 * String containing the URL to be parsed by parse_url().
246 * Result object; used only for error handling in this function.
249 * Array from parse_url().
251 function httprl_parse_url($url, &$result) {
252 // Parse the URL and make sure we can handle the schema.
253 $uri = @
parse_url($url);
255 // If the t function is not available use httprl_pr.
256 if (function_exists('t')) {
264 // Set error code for failed request.
265 $result->error
= $t('Unable to parse URL.');
266 $result->code
= HTTPRL_URL_PARSE_ERROR
;
268 elseif (!isset($uri['scheme'])) {
269 // Set error code for failed request.
270 $result->error
= $t('Missing schema.');
271 $result->code
= HTTPRL_URL_MISSING_SCHEMA
;
278 * Set the default options in the $options array.
281 * Array containing options.
283 function httprl_set_default_options(&$options) {
286 // Merge the default options.
288 'headers' => array(),
291 'max_redirects' => 3,
292 'timeout' => HTTPRL_TIMEOUT
,
297 'domain_connections' => 2,
298 'global_connections' => 128,
299 'global_timeout' => HTTPRL_GLOBAL_TIMEOUT
,
300 'chunk_size_read' => 32768,
301 'chunk_size_write' => 1024,
302 'async_connect' => TRUE
,
305 // Merge the default headers.
306 // Set user agent to drupal.
307 // Set connection to closed to prevent keep-alive from causing a timeout.
308 $options['headers'] += array(
309 'User-Agent' => 'Drupal (+http://drupal.org/)',
310 'Connection' => 'close',
313 // Set referrer to current page.
314 if (!isset($options['headers']['Referer']) && !empty($options['referrer'])) {
315 if (function_exists('request_uri')) {
316 $options['headers']['Referer'] = $base_root .
request_uri();
320 // stream_socket_client() requires timeout to be a float.
321 $options['timeout'] = (float) $options['timeout'];
325 * If server uses a proxy, change the request to utilize said proxy.
328 * Array from parse_url().
330 * Array containing options.
332 * String containing the URL.
334 * @return $proxy_server
335 * String containing the proxy servers host name if one is to be used.
337 function httprl_setup_proxy(&$uri, &$options, $url) {
339 $proxy_server = httprl_variable_get('proxy_server', '');
340 // Use a proxy if one is defined and the host is not on the excluded list.
341 if ($proxy_server && _httprl_use_proxy($uri['host'])) {
342 // Set the scheme so we open a socket to the proxy server.
343 $uri['scheme'] = 'proxy';
344 // Set the path to be the full URL.
346 // Since the full URL is passed as the path, we won't use the parsed query.
347 unset($uri['query']);
349 // Add in username and password to Proxy-Authorization header if needed.
350 if ($proxy_username = httprl_variable_get('proxy_username', '')) {
351 $proxy_password = httprl_variable_get('proxy_password', '');
352 $options['headers']['Proxy-Authorization'] = 'Basic ' .
base64_encode($proxy_username .
(!empty($proxy_password) ?
":" .
$proxy_password : ''));
354 // Some proxies reject requests with any User-Agent headers, while others
355 // require a specific one.
356 $proxy_user_agent = httprl_variable_get('proxy_user_agent', '');
357 // The default value matches neither condition.
358 if (is_null($proxy_user_agent)) {
359 unset($options['headers']['User-Agent']);
361 elseif ($proxy_user_agent) {
362 $options['headers']['User-Agent'] = $proxy_user_agent;
365 return $proxy_server;
369 * Create the TCP/SSL socket connection string.
372 * Array from parse_url().
374 * Array containing options.
375 * @param $proxy_server
376 * String containing the proxy servers host name if one is to be used.
378 * Result object; used only for error handling in this function.
381 * String containing the TCP/SSL socket connection URI.
383 function httprl_set_socket($uri, &$options, $proxy_server, &$result) {
385 switch ($uri['scheme']) {
387 // Make the socket connection to a proxy server.
388 $socket = 'tcp://' .
$proxy_server .
':' .
httprl_variable_get('proxy_port', 8080);
389 // The Host header still needs to match the real request.
390 $options['headers']['Host'] = $uri['host'];
391 $options['headers']['Host'] .
= isset($uri['port']) && $uri['port'] != 80 ?
':' .
$uri['port'] : '';
396 $port = isset($uri['port']) ?
$uri['port'] : 80;
397 $socket = 'tcp://' .
$uri['host'] .
':' .
$port;
398 // RFC 2616: "non-standard ports MUST, default ports MAY be included".
399 // We don't add the standard port to prevent from breaking rewrite rules
400 // checking the host that do not take into account the port number.
401 if (empty($options['headers']['Host'])) {
402 $options['headers']['Host'] = $uri['host'];
405 $options['headers']['Host'] .
= ':' .
$port;
410 // Note: Only works when PHP is compiled with OpenSSL support.
411 $port = isset($uri['port']) ?
$uri['port'] : 443;
412 $socket = 'ssl://' .
$uri['host'] .
':' .
$port;
413 if (empty($options['headers']['Host'])) {
414 $options['headers']['Host'] = $uri['host'];
417 $options['headers']['Host'] .
= ':' .
$port;
422 // If the t function is not available use httprl_pr.
423 if (function_exists('t')) {
430 $result->error
= $t('Invalid schema @scheme.', array('@scheme' => $uri['scheme']));
431 $result->code
= HTTPRL_URL_INVALID_SCHEMA
;
439 * Select which connect flags to use in stream_socket_client().
442 * Array containing options.
444 * Array from parse_url().
447 * STREAM_CLIENT_CONNECT or STREAM_CLIENT_ASYNC_CONNECT|STREAM_CLIENT_CONNECT.
449 function httprl_set_connection_flag(&$options, $uri) {
450 // Set connection flag.
451 if ($options['async_connect']) {
452 // Workaround for PHP bug with STREAM_CLIENT_ASYNC_CONNECT and SSL
453 // https://bugs.php.net/bug.php?id=48182 - Fixed in PHP 5.2.11 and 5.3.1
454 if ($uri['scheme'] == 'https' && (version_compare(PHP_VERSION
, '5.2.11', '<') || version_compare(PHP_VERSION
, '5.3.0', '='))) {
455 $flags = STREAM_CLIENT_CONNECT
;
456 $options['async_connect'] = FALSE
;
459 $flags = STREAM_CLIENT_ASYNC_CONNECT
|STREAM_CLIENT_CONNECT
;
463 $flags = STREAM_CLIENT_CONNECT
;
469 * If data is being sent out in this request, handle it correctly.
471 * If $options['data'] is not a string, convert it to a string using
472 * http_build_query(). Set the Content-Length header correctly. Set the
473 * Content-Type to application/x-www-form-urlencoded if not already set and
474 * using method is POST.
477 * Proper mime support.
480 * Array containing options.
482 function httprl_handle_data(&$options) {
483 // Encode data if not already done.
484 if (!empty($options['data']) && !is_string($options['data'])) {
485 // No files passed in, url-encode the data.
486 if (empty($options['data']['files']) || !is_array($options['data']['files'])) {
487 $options['data'] = http_build_query($options['data'], '', '&');
489 // Set the Content-Type to application/x-www-form-urlencoded if the data
490 // is not empty, the Content-Type is not set, and the method is POST or
492 if (!empty($options['data']) && !isset($options['headers']['Content-Type']) && ($options['method'] == 'POST' || $options['method'] == 'PUT')) {
493 $options['headers']['Content-Type'] = 'application/x-www-form-urlencoded';
498 // Add files to the request.
499 foreach ($options['data']['files'] as
$field_name => $info) {
501 // Convert $info into an array if it's a string.
502 // This makes for one code path (the foreach loop).
503 if (is_string($info)) {
509 foreach ($info as
$fullpath) {
510 // Strip '@' from the start of the path (cURL requirement).
511 if (substr($fullpath, 0, 1) == "@") {
512 $fullpath = substr($fullpath, 1);
514 $filename = basename($fullpath);
515 // TODO: mime detection.
516 $mimetype = 'application/octet-stream';
518 // Build the datastream for this file.
519 $data_stream .
= '--' . HTTPRL_MULTIPART_BOUNDARY .
"\r\n";
520 $data_stream .
= 'Content-Disposition: form-data; name="files[' .
$field_name .
']' .
$multi_field .
'"; filename="' .
$filename .
"\"\r\n";
521 $data_stream .
= 'Content-Transfer-Encoding: binary' .
"\r\n";
522 $data_stream .
= 'Content-Type: ' .
$mimetype .
"\r\n\r\n";
523 $data_stream .
= file_get_contents($fullpath) .
"\r\n";
526 // Remove files from the data array as they have already been added.
527 $data_array = $options['data'];
528 unset($data_array['files']);
529 // Add fields to the request too: $_POST['foo'] = 'bar'.
530 httprl_multipart_encoder($data_stream, $data_array);
532 // Signal end of request (note the trailing "--").
533 $data_stream .
= '--' . HTTPRL_MULTIPART_BOUNDARY .
"--\r\n";
534 $options['data'] = $data_stream;
536 // Set the Content-Type to multipart/form-data if the data is not empty,
537 // the Content-Type is not set, and the method is POST or PUT.
538 if (!empty($options['data']) && !isset($options['headers']['Content-Type']) && ($options['method'] == 'POST' || $options['method'] == 'PUT')) {
539 $options['headers']['Content-Type'] = 'multipart/form-data; boundary=' . HTTPRL_MULTIPART_BOUNDARY
;
544 // Only add Content-Length if we actually have any content or if it is a POST
545 // or PUT request. Some non-standard servers get confused by Content-Length in
546 // at least HEAD/GET requests, and Squid always requires Content-Length in
547 // POST/PUT requests.
548 $content_length = httprl_strlen($options['data']);
549 if ($content_length > 0 || $options['method'] == 'POST' || $options['method'] == 'PUT') {
550 $options['headers']['Content-Length'] = $content_length;
555 * Multipart encode a data array.
557 * PHP has http_build_query() which will url-encode data. There is no built in
558 * function to multipart encode data thus we have this function below.
561 * Array from parse_url().
563 * Array containing options.
565 function httprl_multipart_encoder(&$data_stream, $data_array, $prepend = array()) {
566 foreach ($data_array as
$key => $value) {
567 $key_array = $prepend;
569 if (is_array($value)) {
570 httprl_multipart_encoder($data_stream, $value, $key_array);
572 elseif (is_scalar($value)) {
573 $key_string = array_shift($key_array);
574 if (!empty($key_array)) {
575 $key_string .
= '[' .
implode('][', $key_array) .
']';
577 $data_stream .
= '--' . HTTPRL_MULTIPART_BOUNDARY .
"\r\n";
578 $data_stream .
= 'Content-Disposition: form-data; name="' .
$key_string .
"\"\r\n\r\n";
579 $data_stream .
= $value .
"\r\n";
585 * Set the Authorization header if a user is set in the URI.
588 * Array from parse_url().
590 * Array containing options.
592 function httprl_basic_auth($uri, &$options) {
593 // If the server URL has a user then attempt to use basic authentication.
594 if (isset($uri['user'])) {
595 $options['headers']['Authorization'] = 'Basic ' .
base64_encode($uri['user'] .
(!empty($uri['pass']) ?
":" .
$uri['pass'] : ''));
600 * Build the request string.
602 * This string is what gets sent to the server once a connection has been made.
605 * Array from parse_url().
607 * Array containing options.
610 * String containing the data that will be written to the server.
612 function httprl_build_request_string($uri, $options) {
613 // Construct the path to act on.
614 $path = isset($uri['path']) ?
$uri['path'] : '/';
615 if (isset($uri['query'])) {
616 $path .
= '?' .
$uri['query'];
619 // Assemble the request together. HTTP version requires to be a float.
620 $request = $options['method'] .
' ' .
$path .
' HTTP/' .
sprintf("%.1F", $options['version']) .
"\r\n";
621 foreach ($options['headers'] as
$name => $value) {
622 $request .
= $name .
': ' .
trim($value) .
"\r\n";
624 $request .
= "\r\n" .
$options['data'];
631 * Read the error number & string and give a nice looking error in the output.
633 * This is a flexible and powerful HTTP client implementation. Correctly
634 * handles GET, POST, PUT or any other HTTP requests.
637 * Error number from stream_socket_client().
639 * Error string from stream_socket_client().
641 * An integer holding the stream timeout value.
643 * An object for httprl_send_request.
645 * An object for httprl_send_request.
647 function httprl_stream_connection_error_formatter($errno, $errstr, &$result) {
648 // If the t function is not available use httprl_pr.
649 if (function_exists('t')) {
651 // Make sure drupal_convert_to_utf8() is available.
652 if (defined('VERSION') && substr(VERSION
, 0, 1) >= 7) {
653 require_once DRUPAL_ROOT .
'/includes/unicode.inc';
656 require_once
'./includes/unicode.inc';
659 // Convert error message to utf-8. Using ISO-8859-1 (Latin-1) as source
660 // encoding could be wrong; it is a simple workaround :)
661 $errstr = trim(drupal_convert_to_utf8($errstr, 'ISO-8859-1'));
668 // If $errno is 0, it is an indication that the error occurred
669 // before the connect() call.
670 if (empty($errstr)) {
671 // If the error string is empty as well, this is most likely due to a
672 // problem initializing the stream.
673 $result->code
= HTTPRL_ERROR_INITIALIZING_STREAM
;
674 $result->error
= $t('Error initializing socket @socket.', array('@socket' => $result->socket));
676 elseif (stripos($errstr, 'network_getaddresses: getaddrinfo failed:') !== FALSE
) {
677 // Host not found. No such host is known. The name is not an official host
679 $result->code
= HTTPRL_HOST_NOT_FOUND
;
680 $result->error
= $errstr;
684 // When a network error occurs, we use a negative number so it does not
685 // clash with the HTTP status codes.
686 $result->code
= (int) -$errno;
687 $result->error
= !empty($errstr) ?
$errstr : $t('Error opening socket @socket.', array('@socket' => $result->socket));
692 * Use stream_socket_client() to create a connection to the server.
695 * An integer holding the stream timeout value.
697 * STREAM_CLIENT_CONNECT or STREAM_CLIENT_ASYNC_CONNECT|STREAM_CLIENT_CONNECT.
699 * Array from parse_url().
701 * Array containing options. Used for timeout and async_connect here.
703 * array($fp, $options, $errno, $errstr).
705 function httprl_establish_stream_connection(&$result) {
706 // Record start time.
707 $start_time = microtime(TRUE
);
710 // Try to make a connection, 3 max tries in loop.
712 while (!$result->fp
&& $count < 3) {
713 // Try the connection again not using async if in https mode.
715 if ($result->flags
=== STREAM_CLIENT_ASYNC_CONNECT
|STREAM_CLIENT_CONNECT
&& $result->uri
['scheme'] == 'https') {
716 $result->flags
= STREAM_CLIENT_CONNECT
;
717 $result->options
['async_connect'] = FALSE
;
720 // Break out of while loop if we can't connect.
725 // Open the connection.
726 if (empty($result->options
['context'])) {
727 $result->fp
= @
stream_socket_client($result->socket, $errno, $errstr, $result->options
['timeout'], $result->flags
);
730 // Create a stream with context. Context allows for the verification of
731 // a SSL certificate.
732 $result->fp
= @
stream_socket_client($result->socket, $errno, $errstr, $result->options
['timeout'], $result->flags
, $result->options
['context']);
737 // Make sure the stream opened properly. This check doesn't work if
738 // async_connect is used, so only check it if async_connect is FALSE. Making
739 // sure that stream_socket_get_name returns a "TRUE" value.
741 && !$result->options
['async_connect']
742 && !stream_socket_get_name($result->fp
, TRUE
)
744 $errno = HTTPRL_CONNECTION_REFUSED
;
745 $errstr = 'Connection refused. No connection could be made because the target machine actively refused it.';
749 // Report any errors or set the steram to non blocking mode.
751 httprl_stream_connection_error_formatter($errno, $errstr, $result);
754 stream_set_blocking($result->fp
, 0);
758 $end_time = microtime(TRUE
);
760 if (isset($result->options
['internal_states']['running_time'])) {
761 $extra = $result->options
['internal_states']['running_time'];
762 unset($result->options
['internal_states']['running_time']);
764 $result->running_time
= $end_time - $start_time + $extra;
768 * Queue up a HTTP request in httprl_send_request.
770 * @see drupal_http_request()
772 * This is a flexible and powerful HTTP client implementation. Correctly
773 * handles GET, POST, PUT or any other HTTP requests.
776 * A string or an array containing a fully qualified URI(s).
777 * @param array $options
778 * (optional) An array that can have one or more of the following elements:
779 * - headers: An array containing request headers to send as name/value pairs.
780 * Some of the more useful headers:
781 * - For POST: 'Content-Type' => 'application/x-www-form-urlencoded',
782 * - Limit number of bytes server sends back: 'Range' => 'bytes=0-1024',
783 * - Compression: 'Accept-Encoding' => 'gzip, deflate',
784 * - Let server know where request came from: 'Referer' => 'example.com',
785 * - Content-Types that are acceptable: 'Accept' => 'text/plain',
786 * - Send Cookies: 'Cookie' => 'key1=value1; key2=value2;',
787 * - Skip the cache: 'Cache-Control' => 'no-cache',
788 * - Skip the cache: 'Pragma' => 'no-cache',
789 * List of headers: http://en.wikipedia.org/wiki/List_of_HTTP_header_fields
790 * - method: A string containing the request method. Defaults to 'GET'.
791 * - data: A string containing the request body, formatted as
792 * 'param=value¶m=value&...'. Defaults to NULL.
793 * - max_redirects: An integer representing how many times a redirect
794 * may be followed. Defaults to 3.
795 * - timeout: A float representing the maximum number of seconds a connection
796 * may take. The default is 30 seconds. If a timeout occurs, the error code
797 * is set to the HTTPRL_REQUEST_TIMEOUT constant.
798 * - context: A context resource created with stream_context_create().
799 * - blocking: set to FALSE to make this not care about the returned data.
800 * - version: HTTP Version 1.0 or 1.1. Default is 1.0 for a good reason.
801 * - referrer: TRUE - send current page; FALSE - do not send current
802 * page. Default is FALSE.
803 * - domain_connections: Maximum number of simultaneous connections to a given
804 * domain name. Default is 8.
805 * - global_connections: Maximum number of simultaneous connections that can
806 * be open on the server. Default is 128.
807 * - global_timeout: A float representing the maximum number of seconds the
808 * function call may take. If a timeout occurs,the error code is set to the
809 * HTTPRL_FUNCTION_TIMEOUT constant. Default is 120 seconds.
810 * - chunk_size_write: max size of what will be written in fwrite().
811 * - chunk_size_read: max size of what will be read from fread().
812 * - async_connect: default is TRUE. FALSE may give more info on errors but is
814 * - callback: Array where the first value is an array of options; the result
815 * is passed to the callback function as the first argument, the other
816 * options passed in this array are passed in after the result. The options
817 * array needs to contain the function name and the target variable for the
818 * result of the function.
819 * - background_callback: Array where the first value is an array of options;
820 * the result is passed to the callback function as the first argument, the
821 * other options passed in this array are passed in after the result. The
822 * options array needs to contain the function name. If the return or
823 * printed keys are not defined this function will run in non blocking mode
824 * and the parent will not be able to get the result; if the return or
825 * printed keys defined then this function will run in blocking mode and the
826 * returned and printed data as well as any variables passed by reference
827 * will be available to the parent.
828 * - alter_all_streams_function: Function name. This function runs at the end
829 * of httprl_post_processing() so that one can alter the $responses and
830 * $output variables inside of httprl_send_request. Defined function
831 * should have the following parameters:
832 * ($id, &$responses).
835 * Array where key is the URL and the value is the return value from
836 * httprl_send_request.
838 function httprl_request($urls, $options = array()) {
839 // See if a full bootstrap has been done.
840 $full_bootstrap = httprl_drupal_full_bootstrap();
842 // Transform string to an array.
843 if (!is_array($urls)) {
846 $urls = array($temp);
850 if ($full_bootstrap) {
851 // Allow other modules to alter things before we get started.
852 // Run hook_pre_httprl_request_alter().
853 $data = array($urls, $options);
854 drupal_alter('pre_httprl_request', $data);
855 list($urls, $options) = $data;
858 $connections = array();
860 // Set things up; but do not perform any IO.
861 foreach ($urls as
$url) {
862 $result = new
stdClass();
864 $result->status
= 'in progress';
866 $result->chunk_size
= 1024;
870 $these_options = $options;
872 // Setup the default options.
873 httprl_set_default_options($these_options);
875 // Parse the given URL and skip if an error occurred.
876 $uri = httprl_parse_url($url, $result);
877 if (isset($result->error
)) {
878 // Put all variables into an array for easy alterations.
879 $connections[] = array(NULL
, NULL
, $uri, $url, $these_options, $result, NULL
);
880 $return[$url] = FALSE
;
881 // Stop processing this request as we have encountered an error.
885 // Set the proxy server if one is required.
886 $proxy_server = httprl_setup_proxy($uri, $these_options, $url);
887 // Create the socket string and skip if an error occurred.
888 $socket = httprl_set_socket($uri, $these_options, $proxy_server, $result, $return, $url);
889 if (isset($result->error
)) {
890 // Put all variables into an array for easy alterations.
891 $connections[] = array($socket, NULL
, $uri, $url, $these_options, $result, NULL
);
892 $return[$url] = FALSE
;
893 // Stop processing this request as we have encountered an error.
897 // Use a sync of async connection.
898 $flags = httprl_set_connection_flag($these_options, $uri);
899 // If any data is given, do the right things to this request so it works.
900 httprl_handle_data($these_options);
901 // Build the request string.
902 $request = httprl_build_request_string($uri, $these_options);
904 // Put all variables into an array for easy alterations.
905 $connections[] = array($socket, $flags, $uri, $url, $these_options, $result, $request);
906 $return[$url] = TRUE
;
909 if ($full_bootstrap) {
910 // Allow other programs to alter the connections before they are made.
911 // run hook_httprl_request_alter().
912 drupal_alter('httprl_request', $connections);
916 foreach ($connections as
$connection) {
917 list($socket, $flags, $uri, $url, $options, $result, $request) = $connection;
918 $result->request
= $request;
919 $result->options
= $options;
920 $result->socket = $socket;
921 $result->flags
= $flags;
923 $result->running_time
= 0;
924 $results[] = $result;
927 httprl_send_request($results);
932 * Perform many HTTP requests.
934 * @see drupal_http_request()
936 * This is a flexible and powerful HTTP client implementation. Correctly
937 * handles GET, POST, PUT or any other HTTP requests.
939 * @param $connections
940 * (optional) A file pointer.
942 * TRUE if function worked as planed.
944 function httprl_send_request($results = NULL
) {
945 static
$responses = array();
948 if (!is_null($results)) {
949 // Put the connection information into the responses array.
950 foreach ($results as
$result) {
951 $responses[$counter] = $result;
957 // Exit if there is nothing to do.
958 if (empty($responses)) {
962 // If the t function is not available use httprl_pr.
963 if (function_exists('t')) {
970 // Create output array.
972 // Remove errors from responses array and set the global timeout.
974 $global_connection_limit = 1;
975 foreach ($responses as
$id => &$result) {
976 if (!empty($result->error
)) {
977 $result->status
= 'Connection not made.';
978 // Do post processing on the stream.
979 httprl_post_processing($id, $responses, $output);
983 // Get connection limits.
984 $global_connection_limit = max($global_connection_limit, $result->options
['global_connections']);
985 if (!isset($domain_connection_limit[$result->options
['headers']['Host']])) {
986 $domain_connection_limit[$result->options
['headers']['Host']] = max(1, $result->options
['domain_connections']);
989 $domain_connection_limit[$result->options
['headers']['Host']] = max($domain_connection_limit[$result->options
['headers']['Host']], $result->options
['domain_connections']);
992 $global_timeout = max($global_timeout, $result->options
['global_timeout']);
995 // Record start time.
996 $start_time_this_run = $start_time_global = microtime(TRUE
);
998 // Run the loop as long as we have a stream to read/write to.
1000 $stream_select_timeout = 1;
1001 $stream_write_count = 0;
1003 while (!empty($responses)) {
1005 // Initialize connection limits.
1006 $this_run = array();
1007 $global_connection_count = 0;
1008 $domain_connection_count = array();
1009 $restart_timers = FALSE
;
1012 $now = microtime(TRUE
);
1015 $elapsed_time = $now - $start_time_this_run;
1016 $start_time_this_run = $now;
1017 $global_time = $global_timeout - ($start_time_this_run - $start_time_global);
1019 $reset_empty_runs = FALSE
;
1020 // Inspect each stream, checking for timeouts and connection limits.
1021 foreach ($responses as
$id => &$result) {
1022 // See if function timed out.
1023 if ($global_time <= 0) {
1024 // Function timed out & the request is not done.
1025 if ($result->status
== 'in progress') {
1026 $result->error
= $t('Function timed out. Write.');
1027 // If stream is not done writing, then remove one from the write count.
1028 if (isset($result->fp
)) {
1029 $stream_write_count--;
1033 $result->error
= $t('Function timed out.');
1035 $result->code
= HTTPRL_FUNCTION_TIMEOUT
;
1036 $result->status
= 'Done.';
1038 // Do post processing on the stream and close it.
1039 httprl_post_processing($id, $responses, $output, $global_time);
1042 // Do not calculate local timeout if a file pointer doesn't exist.
1043 if (isset($result->fp
)) {
1044 // Add the elapsed time to this stream.
1045 $result->running_time
+= $elapsed_time;
1046 // Calculate how much time is left of the original timeout value.
1047 $timeout = $result->options
['timeout'] - $result->running_time
;
1048 // No streams are ready from stream_select, See if end server has
1049 // dropped the connection, or has failed to make the connection.
1050 $socket_name = 'Not empty.';
1051 if ($empty_runs > 32) {
1052 // If nothing has happened after 32 runs, see if the connection has
1054 $socket_name = stream_socket_get_name($result->fp
, TRUE
);
1057 // Connection was dropped or connection timed out.
1058 if ($timeout <= 0 || empty($socket_name)) {
1059 $result->error
= $t('Connection timed out. If you believe this is a false error, turn off async_connect in the httprl options array and try again.');
1060 // Stream timed out & the request is not done.
1061 if ($result->status
== 'in progress') {
1062 $result->error .
= $t(' Write.');
1063 // If stream is not done writing, then remove one from the write count.
1064 $stream_write_count--;
1067 $result->error .
= $t(' Read.');
1069 $result->code
= HTTPRL_REQUEST_TIMEOUT
;
1070 $result->status
= 'Done.';
1072 // Do post processing on the stream.
1073 httprl_post_processing($id, $responses, $output, $timeout);
1074 $reset_empty_runs = TRUE
;
1079 // Connection was handled elsewhere.
1080 if (!isset($result->fp
) && $result->status
!= 'in progress') {
1081 // Do post processing on the stream.
1082 httprl_post_processing($id, $responses, $output);
1083 $reset_empty_runs = TRUE
;
1087 // Set the connection limits for this run.
1088 // Get the host name.
1089 $host = $result->options
['headers']['Host'];
1090 // Set the domain connection limit if none has been defined yet.
1091 if (!isset($domain_connection_limit[$host])) {
1092 $domain_connection_limit[$host] = max(1, $result->options
['domain_connections']);
1094 // Count up the number of connections.
1095 $global_connection_count++;
1096 if (empty($domain_connection_count[$host])) {
1097 $domain_connection_count[$host] = 1;
1100 $domain_connection_count[$host]++;
1102 // If the conditions are correct, let the stream be ran in this loop.
1103 if ($global_connection_limit >= $global_connection_count && $domain_connection_limit[$host] >= $domain_connection_count[$host]) {
1104 // Establish a new connection.
1105 if (!isset($result->fp
) && $result->status
== 'in progress') {
1106 // Establish a connection to the server.
1107 httprl_establish_stream_connection($result);
1110 $restart_timers = TRUE
;
1112 // If connection can not be established bail out here.
1114 // Do post processing on the stream.
1115 httprl_post_processing($id, $responses, $output);
1116 $domain_connection_count[$host]--;
1117 $global_connection_count--;
1120 $stream_write_count++;
1123 if (!empty($result->fp
)) {
1124 $this_run[$id] = $result->fp
;
1129 // All streams removed; exit loop.
1130 if (empty($responses)) {
1134 if ($restart_timers) {
1135 $start_time_this_run = microtime(TRUE
);
1137 // No streams selected; restart loop from the top.
1138 if (empty($this_run)) {
1141 if ($reset_empty_runs) {
1143 $reset_empty_runs = FALSE
;
1148 // Set the read and write vars to the streams var.
1149 $read = $write = $this_run;
1151 // Do some voodoo and open all streams at once. Wait 25ms for streams to
1153 $n = stream_select($read, $write, $except, $stream_select_timeout, 25000);
1154 $stream_select_timeout = 0;
1156 // We have some streams to read/write to.
1161 // Readable sockets either have data for us, or are failed connection
1163 foreach ($read as
$r) {
1164 $id = array_search($r, $this_run);
1165 // Make sure ID is in the streams.
1166 if ($id === FALSE
) {
1170 // Do not read from the non blocking sockets.
1171 if (empty($responses[$id]->options
['blocking'])) {
1172 // Do post processing on the stream and close it.
1173 httprl_post_processing($id, $responses, $output);
1178 $chunk = fread($r, $responses[$id]->chunk_size
);
1179 if (httprl_strlen($chunk) > 0) {
1182 $responses[$id]->data .
= $chunk;
1184 // Process the headers if we have some data.
1185 if (!empty($responses[$id]->data
) && empty($responses[$id]->headers
) &&
1186 ( strpos($responses[$id]->data
, "\r\n\r\n")
1187 || strpos($responses[$id]->data
, "\n\n")
1188 || strpos($responses[$id]->data
, "\r\r")
1191 // See if the headers are in the data stream.
1192 httprl_parse_data($responses[$id]);
1193 if (!empty($responses[$id]->headers
)) {
1194 // Stream was a redirect, kill & close this connection; redirect is
1195 // being followed now.
1196 if (!empty($responses[$id]->options
['internal_states']['kill'])) {
1198 unset($responses[$id]);
1202 // Now that we have the headers, increase the chunk size.
1203 $responses[$id]->chunk_size
= $responses[$id]->options
['chunk_size_read'];
1205 // If a range header is set, 200 was returned, and method is GET
1206 // calculate how many bytes need to be downloaded.
1207 if (!empty($responses[$id]->options
['headers']['Range']) && $responses[$id]->code
== 200 && $responses[$id]->method
== 'GET') {
1208 $responses[$id]->ranges
= httprl_get_ranges($responses[$id]->options
['headers']['Range']);
1209 $responses[$id]->options
['max_data_size'] = httprl_get_last_byte_from_range($responses[$id]->ranges
);
1214 // Close the connection if a Range request was made and the currently
1215 // downloaded data size is larger than the Range request.
1216 if ( !empty($responses[$id]->options
['max_data_size'])
1217 && !is_null($responses[$id]->options
['max_data_size'])
1218 && $responses[$id]->options
['max_data_size'] < httprl_strlen($responses[$id]->data
)
1220 $responses[$id]->status
= 'Done.';
1221 $responses[$id]->code
= 206;
1223 // Make the data conform to the range request.
1224 $new_data = array();
1225 foreach ($responses[$id]->ranges as
$range) {
1226 $new_data[] = substr($responses[$id]->data
, $range['start'], ($range['end']+1) - $range['start']);
1228 $responses[$id]->data
= implode('', $new_data);
1230 // Do post processing on the stream.
1231 httprl_post_processing($id, $responses, $output);
1236 $info = stream_get_meta_data($r);
1237 $alive = !$info['eof'] && !feof($r) && !$info['timed_out'] && httprl_strlen($chunk);
1239 if ($responses[$id]->status
== 'in progress') {
1240 $responses[$id]->error
= $t('Connection refused by destination. Write.');
1241 $responses[$id]->code
= HTTPRL_CONNECTION_REFUSED
;
1243 $responses[$id]->status
= 'Done.';
1245 // Do post processing on the stream.
1246 httprl_post_processing($id, $responses, $output);
1250 $responses[$id]->status
= 'Reading data';
1254 // Write to each stream if it is available.
1255 if ($stream_write_count > 0) {
1256 foreach ($write as
$w) {
1257 $id = array_search($w, $this_run);
1258 // Make sure ID is in the streams & status is for writing.
1259 if ($id === FALSE
|| empty($responses[$id]->status
) || $responses[$id]->status
!= 'in progress') {
1263 // Calculate the number of bytes we need to write to the stream.
1264 if (!empty($responses[$id]->request_left
)) {
1265 $data_to_send = $responses[$id]->request_left
;
1268 $data_to_send = $responses[$id]->request
;
1270 $len = httprl_strlen($data_to_send);
1272 // Write to the stream.
1273 $bytes = fwrite($w, $data_to_send, min($responses[$id]->options
['chunk_size_write'], $len));
1276 // Nothing to write.
1280 // See if we are done with writing.
1281 if ($bytes === FALSE
) {
1283 $responses[$id]->error
= $t('fwrite() failed.');
1284 $responses[$id]->code
= HTTPRL_REQUEST_FWRITE_FAIL
;
1285 $responses[$id]->status
= 'Done.';
1286 $stream_write_count--;
1288 // Do post processing on the stream.
1289 httprl_post_processing($id, $responses, $output);
1292 elseif ($bytes >= $len) {
1293 $stream_write_count--;
1295 // Clear out the request_left variable.
1296 if (isset($responses[$id]->request_left
)) {
1297 unset($responses[$id]->request_left
);
1300 // If this is a non blocking request then close the connection and destroy the stream.
1301 if (empty($responses[$id]->options
['blocking'])) {
1302 $responses[$id]->status
= 'Non-Blocking request sent out. Not waiting for the response.';
1303 // Do post processing on the stream.
1304 httprl_post_processing($id, $responses, $output);
1308 // All data has been written to the socket. We are read only from here on out.
1309 $responses[$id]->status
= "Request sent, waiting for response.";
1314 // There is more data to write to this socket. Cut what was sent
1315 // across the stream and resend whats left next time in the loop.
1316 $responses[$id]->request_left
= substr($data_to_send, $bytes);
1325 if ($empty_runs > 400) {
1326 // If stream_select hasn't returned a valid read or write stream after
1327 // 10+ seconds, error out.
1328 foreach ($this_run as
$id => $fp) {
1329 // stream_select timed out & the request is not done.
1330 $responses[$id]->error
= $t('stream_select() timed out.');
1331 $responses[$id]->code
= HTTPRL_STREAM_SELECT_TIMEOUT
;
1332 $responses[$id]->status
= 'Done.';
1334 // Do post processing on the stream.
1335 httprl_post_processing($id, $responses, $output);
1340 // Wait 5ms for data buffers.
1345 // Free memory/reset static variables.
1346 $responses = array();
1353 * Extract the header and meta data from the http data stream.
1355 * @see drupal_http_request()
1358 * An object from httprl_send_request.
1360 function httprl_parse_data(&$result) {
1361 // If in non blocking mode, skip.
1362 if (empty($result->options
['blocking'])) {
1366 // If the headers are already parsed, skip.
1367 if (!empty($result->headers
)) {
1371 // If the t function is not available use httprl_pr.
1372 if (function_exists('t')) {
1379 // Parse response headers from the response body.
1380 // Be tolerant of malformed HTTP responses that separate header and body with
1381 // \n\n or \r\r instead of \r\n\r\n.
1382 $response = $result->data
;
1383 list($response, $result->data
) = preg_split("/\r\n\r\n|\n\n|\r\r/", $response, 2);
1384 $response = preg_split("/\r\n|\n|\r/", $response);
1386 // Parse the response status line.
1387 $protocol_code_array = explode(' ', trim(array_shift($response)), 3);
1388 $result->protocol
= $protocol_code_array[0];
1389 $code = (int) $protocol_code_array[1];
1390 // If the response does not include a description, don't try to process it.
1391 $result->status_message
= isset($protocol_code_array[2]) ?
$protocol_code_array[2] : '';
1392 unset($protocol_code_array);
1394 $result->headers
= array();
1396 // Parse the response headers.
1397 $cookie_primary_counter = 0;
1398 while ($line = trim(array_shift($response))) {
1399 list($name, $value) = explode(':', $line, 2);
1400 $name = strtolower($name);
1402 // Parse cookies before they get added to the header.
1403 if ($name == 'set-cookie') {
1404 // Extract the key value pairs for this cookie.
1405 foreach (explode(';', $value) as
$cookie_name_value) {
1406 $temp = explode('=', trim($cookie_name_value));
1407 $cookie_key = trim($temp[0]);
1408 $cookie_value = isset($temp[1]) ?
trim($temp[1]) : '';
1410 // The cookie name-value pair always comes first (RFC 2109 4.2.2).
1411 if (!isset($result->cookies
[$cookie_primary_counter])) {
1412 $result->cookies
[$cookie_primary_counter] = array(
1413 'name' => $cookie_key,
1414 'value' => $cookie_value,
1417 // Extract the rest of the attribute-value pairs.
1419 $result->cookies
[$cookie_primary_counter] += array(
1420 $cookie_key => $cookie_value,
1424 $cookie_primary_counter++;
1427 // Add key value pairs to the header; including cookies.
1428 if (isset($result->headers
[$name]) && $name == 'set-cookie') {
1429 // RFC 2109: the Set-Cookie response header comprises the token Set-
1430 // Cookie:, followed by a comma-separated list of one or more cookies.
1431 $result->headers
[$name] .
= ',' .
trim($value);
1434 $result->headers
[$name] = trim($value);
1440 101 => 'Switching Protocols',
1444 203 => 'Non-Authoritative Information',
1445 204 => 'No Content',
1446 205 => 'Reset Content',
1447 206 => 'Partial Content',
1448 300 => 'Multiple Choices',
1449 301 => 'Moved Permanently',
1452 304 => 'Not Modified',
1454 307 => 'Temporary Redirect',
1455 400 => 'Bad Request',
1456 401 => 'Unauthorized',
1457 402 => 'Payment Required',
1460 405 => 'Method Not Allowed',
1461 406 => 'Not Acceptable',
1462 407 => 'Proxy Authentication Required',
1463 408 => 'Request Time-out',
1466 411 => 'Length Required',
1467 412 => 'Precondition Failed',
1468 413 => 'Request Entity Too Large',
1469 414 => 'Request-URI Too Large',
1470 415 => 'Unsupported Media Type',
1471 416 => 'Requested range not satisfiable',
1472 417 => 'Expectation Failed',
1473 500 => 'Internal Server Error',
1474 501 => 'Not Implemented',
1475 502 => 'Bad Gateway',
1476 503 => 'Service Unavailable',
1477 504 => 'Gateway Time-out',
1478 505 => 'HTTP Version not supported',
1480 // RFC 2616 states that all unknown HTTP codes must be treated the same as the
1481 // base code in their class.
1482 if (!isset($responses[$code])) {
1483 $code = floor($code / 100) * 100;
1485 $result->code
= $code;
1489 case
206: // Partial Content
1490 case
304: // Not modified
1493 case
301: // Moved permanently
1494 case
302: // Moved temporarily
1495 case
307: // Moved temporarily
1496 $location = @
parse_url($result->headers
['location']);
1498 // If location isn't fully qualified URL (as per W3 RFC2616), build one.
1499 if (empty($location['scheme']) || empty($location['host'])) {
1500 // Get the important parts from the original request.
1501 $original_location = @
parse_url($result->url
);
1502 // Assume request is to self if none of this was setup correctly.
1503 $location['scheme'] = !empty($location['scheme']) ?
$location['scheme'] : $original_location['scheme'];
1504 $location['host'] = !empty($location['host']) ?
$location['host'] : !empty($original_location['host']) ?
$original_location['host'] : $_SERVER['HTTP_HOST'];
1505 $location['port'] = !empty($location['port']) ?
$location['port'] : !empty($original_location['port']) ?
$original_location['port'] : '';
1506 $location = httprl_glue_url($location);
1509 $location = $result->headers
['location'];
1512 // Set internal redirect states.
1513 $result->options
['internal_states']['redirect_code_array'][] = $code;
1514 $result->options
['internal_states']['redirect_url_array'][] = $location;
1515 if (!isset($result->options
['internal_states']['original_url'])) {
1516 $result->options
['internal_states']['original_url'] = $result->url
;
1519 // Error out if we hit the max redirect.
1520 if ($result->options
['max_redirects'] <= 0) {
1521 $result->code
= HTTPRL_REQUEST_ALLOWED_REDIRECTS_EXHAUSTED
;
1522 $result->error
= $t('Maximum allowed redirects exhausted.');
1525 // Redirect to the new location.
1526 // TODO: Send cookies in the redirect request if domain/path match.
1527 $result->options
['max_redirects']--;
1528 if (isset($result->options
['headers']['Referer'])) {
1529 $result->options
['headers']['Referer'] = $result->url
;
1531 // Remove the host from the header.
1532 unset($result->options
['headers']['Host']);
1534 // Pass along running time.
1535 $result->options
['internal_states']['running_time'] = $result->running_time
;
1537 // Send new request.
1538 httprl_request($location, $result->options
);
1539 // Kill this request.
1540 $result->options
['internal_states']['kill'] = TRUE
;
1546 $result->error
= $result->status_message
;
1551 * Parse a range header into start and end byte ranges.
1554 * String in the form of bytes=0-1024 or bytes=0-1024,2048-4096
1556 * Keyed arrays containing start and end values for the byte ranges.
1557 * Empty array if the string can not be parsed.
1559 function httprl_get_ranges($input) {
1561 // Make sure the input string matches the correct format.
1562 $string = preg_match('/^bytes=((\d*-\d*,? ?)+)$/', $input, $matches) ?
$matches[1] : FALSE
;
1563 if (!empty($string)) {
1564 // Handle mutiple ranges
1565 foreach (explode(',', $string) as
$range) {
1566 // Get the start and end byte values for this range.
1567 $values = explode('-', $range);
1568 if (count($values) != 2) {
1571 $ranges[] = array('start' => $values[0], 'end' => $values[1]);
1578 * Given an array of ranges, get the last byte we need to download.
1581 * Multi dimentional array
1582 * @return int or NULL
1583 * NULL: Get all values; int: last byte to download.
1585 function httprl_get_last_byte_from_range($ranges) {
1587 if (empty($ranges)) {
1590 foreach ($ranges as
$range) {
1591 if (!is_numeric($range['start']) || !is_numeric($range['end'])) {
1594 $max = max($range['end']+1, $max);
1600 * Run post processing on the request if we are done reading.
1602 * Decode transfer-encoding and content-encoding.
1603 * Reconstruct the internal redirect arrays.
1606 * An object from httprl_send_request.
1608 function httprl_post_processing($id, &$responses, &$output, $time_left = NULL
) {
1609 // Create the result reference.
1610 $result = &$responses[$id];
1613 if (isset($result->fp
)) {
1614 @
fclose($result->fp
);
1618 if (is_null($time_left)) {
1619 $time_left = $result->options
['timeout'] - $result->running_time
;
1621 $result->options
['timeout'] = $time_left;
1623 // Assemble redirects.
1624 httprl_reconstruct_redirects($result);
1626 // Decode chunked transfer-encoding and gzip/deflate content-encoding if we
1627 // have a successful read; code is greater than 0.
1628 if ($result->code
> 0) {
1629 httprl_decode_data($result);
1632 // If this is a background callback request, extract the data and return.
1633 if (isset($result->options
['internal_states']['background_function_return']) && isset($result->headers
['content-type']) && $result->headers
['content-type'] == 'application/x-www-form-urlencoded') {
1634 httprl_extract_background_callback_data($result);
1635 unset($responses[$id]);
1639 // See if a full bootstrap has been done.
1640 $full_bootstrap = httprl_drupal_full_bootstrap();
1642 // Allow other modules to alter the result.
1643 if ($full_bootstrap) {
1644 // Call hook_httprl_post_processing_alter().
1645 drupal_alter('httprl_post_processing', $result);
1648 // Run callback so other modules can do stuff in the event loop.
1649 if ( $full_bootstrap
1650 && !empty($result->options
['callback'])
1651 && is_array($result->options
['callback'])
1653 httprl_run_callback($result);
1656 // Run background_callback.
1657 if ( !empty($result->options
['background_callback'])
1658 && is_array($result->options
['background_callback'])
1660 httprl_queue_background_callback($result->options
['background_callback'], $result);
1663 // Allow a user defined function to alter all $responses.
1664 if ($full_bootstrap && !empty($result->options
['alter_all_streams_function']) && function_exists($result->options
['alter_all_streams_function'])) {
1665 $result->options
['alter_all_streams_function']($id, $responses);
1668 // Copy the result to the output array.
1669 if (isset($result->url
)) {
1670 $output[$result->url
] = $result;
1672 unset($responses[$id]);
1676 * Set the return and printed values & any pass by reference values from a
1677 * background callback operation.
1680 * An object from httprl_send_request.
1682 function httprl_extract_background_callback_data(&$result) {
1683 // Extract data from string.
1685 parse_str($result->data
, $data);
1686 $data = unserialize(current($data));
1688 // Set return and printed values.
1689 if (isset($data['return'])) {
1690 $result->options
['internal_states']['background_function_return'] = $data['return'];
1692 if (isset($data['printed'])) {
1693 $result->options
['internal_states']['background_function_printed'] = $data['printed'];
1696 // Set any pass by reference values.
1697 httprl_recursive_array_reference_extract($result->options
['internal_states']['background_function_args'], $data['args']);
1701 * Replace data in place so pass by reference sill works.
1704 * An array containing the references if any.
1706 * An array that has the new values to copy into $array.
1708 * Only go 10 levels deep. Prevent infinite loops.
1710 function httprl_recursive_array_reference_extract(&$array, $data, $depth = 0) {
1712 foreach ($array as
$key => &$value) {
1713 if (isset($data[$key])) {
1714 if (is_array($data[$key]) && is_array($value) && $depth < 10) {
1715 $value = httprl_recursive_array_reference_extract($value, $data[$key], $depth);
1718 $value = $data[$key];
1725 // Copy new keys into the data structure.
1726 foreach ($data as
$key => $value) {
1727 if (isset($array[$key])) {
1730 $array[$key] = $value;
1735 * Will run the given callback returning values and what might have been
1736 * printed by that function, as well as respecting any pass by reference values.
1739 * An object from httprl_send_request.
1741 function httprl_run_callback(&$result) {
1743 $callback_options = $result->options
['callback'][0];
1744 // Merge in values by reference.
1745 $result->options
['callback'][0] = &$result;
1747 // Capture anything printed out.
1748 if (array_key_exists('printed', $callback_options)) {
1752 $callback_options['return'] = call_user_func_array($callback_options['function'], $result->options
['callback']);
1753 if (array_key_exists('printed', $callback_options)) {
1754 $callback_options['printed'] = ob_get_contents();
1758 // Add options back into the callback array.
1759 if (isset($result->options
['callback'])) {
1760 array_unshift($result->options
['callback'], $callback_options);
1765 * Will run the given callback returning values and what might have been
1766 * printed by that function, as well as respecting any pass by reference values.
1769 * An array of arguments, first key value pair is used to control the
1770 * callback function. The rest of the key value pairs will be arguments for
1771 * the callback function.
1773 * (optional) An object from httprl_send_request. If this is set, this will
1774 * be the first argument of the function.
1776 function httprl_queue_background_callback(&$args, &$result = NULL
) {
1777 // Use a counter to prevent key collisions in httprl_send_request.
1779 if (!isset($counter)) {
1785 $callback_options = $args[0];
1787 if (is_null($result)) {
1791 // Merge in this request by reference.
1792 $args[0] = &$result;
1795 // Set blocking mode.
1796 if (isset($callback_options['return']) || isset($callback_options['printed'])) {
1802 // Make sure some array keys exist.
1803 if (!isset($callback_options['return'])) {
1804 $callback_options['return'] = '';
1806 if (!isset($callback_options['function'])) {
1807 $callback_options['function'] = '';
1810 // Get the maximum amount of time this could take.
1811 $times = array(HTTPRL_TIMEOUT
, HTTPRL_GLOBAL_TIMEOUT
);
1812 if (isset($callback_options['options']['timeout'])) {
1813 $times[] = $callback_options['options']['timeout'];
1815 if (isset($callback_options['options']['global_timeout'])) {
1816 $times[] = $callback_options['options']['global_timeout'];
1819 // Acquire lock for this run.
1822 while (!$locked && $lock_counter < 20) {
1823 $id = 'httprl_' .
hash('sha512', mt_rand() .
time());
1824 // Set lock to maximum amount of time.
1825 $locked = lock_acquire($id, max($times));
1829 // Make sure lock exists after this process is dead.
1831 // Remove from the global locks variable.
1835 // Remove the lock_id reference in the database.
1836 if (httprl_variable_get('lock_inc', './includes/lock.inc') === './includes/lock.inc') {
1837 if (defined('VERSION') && substr(VERSION
, 0, 1) >= 7) {
1838 db_update('semaphore')
1839 ->fields(array('value' => 'httprl'))
1840 ->condition('name', $id)
1841 ->condition('value', _lock_id())
1845 db_query("UPDATE {semaphore} SET value = '%s' WHERE name = '%s' AND value = '%s'", 'httprl', $id, _lock_id());
1850 // Get URL to call function in background.
1851 if (empty($callback_options['url'])) {
1852 $url = httprl_build_url_self('httprl_async_function_callback?count=' .
$counter);
1855 $url = $callback_options['url'];
1857 // Create data array and options for request.
1860 'master_key' => hash('sha512', httprl_drupal_get_private_key()),
1863 'function' => $callback_options['function'],
1864 'args' => serialize($args),
1866 'internal_states' => array(
1867 'background_function_return' => &$callback_options['return'],
1868 'background_function_args' => &$args,
1870 'blocking' => $mode,
1873 if (isset($callback_options['printed'])) {
1874 $options['internal_states']['background_function_printed'] = &$callback_options['printed'];
1876 if (isset($callback_options['options']) && is_array($callback_options['options'])) {
1877 $options += $callback_options['options'];
1880 return httprl_request($url, $options);
1884 * Will decode chunked transfer-encoding and gzip/deflate content-encoding.
1887 * An object from httprl_send_request.
1889 function httprl_decode_data(&$result) {
1890 if (isset($result->headers
['transfer-encoding']) && $result->headers
['transfer-encoding'] == 'chunked') {
1891 $stream_position = 0;
1893 $data = $result->data
;
1894 while ($stream_position < httprl_strlen($data)) {
1895 // Get the number of bytes to read for this chunk.
1896 $rawnum = substr($data, $stream_position, strpos(substr($data, $stream_position), "\r\n") + 2);
1897 $num = hexdec(trim($rawnum));
1898 // Get the position to read from.
1899 $stream_position += httprl_strlen($rawnum);
1900 // Extract the chunk.
1901 $chunk = substr($data, $stream_position, $num);
1902 // Decompress if compressed.
1903 if (isset($result->headers
['content-encoding'])) {
1904 if ($result->headers
['content-encoding'] == 'gzip') {
1905 $chunk = gzinflate(substr($chunk, 10));
1907 elseif ($result->headers
['content-encoding'] == 'deflate') {
1908 $chunk = gzinflate($chunk);
1911 // Glue the chunks together.
1913 $stream_position += httprl_strlen($chunk);
1915 $result->data
= $output;
1917 // Decompress if compressed.
1918 elseif (isset($result->headers
['content-encoding'])) {
1919 if ($result->headers
['content-encoding'] == 'gzip') {
1920 $result->data
= gzinflate(substr($result->data
, 10));
1922 elseif ($result->headers
['content-encoding'] == 'deflate') {
1923 $result->data
= gzinflate($result->data
);
1929 * Reconstruct the internal redirect arrays.
1932 * An object from httprl_send_request.
1934 function httprl_reconstruct_redirects(&$result) {
1935 // Return if original_url is not set.
1936 if (empty($result->options
['internal_states']['original_url'])) {
1939 // Set the original url.
1940 $result->url
= $result->options
['internal_states']['original_url'];
1942 // Set the redirect code.
1943 $result->redirect_code_array
= $result->options
['internal_states']['redirect_code_array'];
1944 $result->redirect_code
= array_pop($result->options
['internal_states']['redirect_code_array']);
1946 // Set the redirect url.
1947 $result->redirect_url_array
= $result->options
['internal_states']['redirect_url_array'];
1948 $result->redirect_url
= array_pop($result->options
['internal_states']['redirect_url_array']);
1951 unset($result->options
['internal_states']['original_url'], $result->options
['internal_states']['redirect_code_array'], $result->options
['internal_states']['redirect_url_array']);
1952 if (empty($result->options
['internal_states'])) {
1953 unset($result->options
['internal_states']);
1958 * Output text, close connection, continue processing in the background.
1961 * string - Text to output to open connection.
1963 * bool - Wait 1 second?
1964 * @param $content_type
1965 * string - Content type header.
1967 * int - Content length.
1970 * Returns TRUE if operation worked, FALSE if it failed.
1972 function httprl_background_processing($output, $wait = TRUE
, $content_type = "text/html; charset=utf-8", $length = 0) {
1973 // Can't do background processing if headers are already sent.
1974 if (headers_sent()) {
1978 // Prime php for background operations.
1979 // Remove any output buffers.
1982 while (ob_get_level() && $loop < 25) {
1987 // Ignore user aborts.
1988 ignore_user_abort(TRUE
);
1990 // Output headers & data.
1992 header("HTTP/1.0 200 OK");
1993 header("Content-type: " .
$content_type);
1994 header("Expires: Sun, 19 Nov 1978 05:00:00 GMT");
1995 header("Cache-Control: no-cache");
1996 header("Cache-Control: must-revalidate");
1997 header("Connection: close");
1998 header('Etag: "' .
microtime(TRUE
) .
'"');
2000 $size = ob_get_length();
2001 header("Content-Length: " .
$size);
2006 if (function_exists('fastcgi_finish_request')) {
2007 fastcgi_finish_request();
2010 // wait for 1 second
2015 // text returned and connection closed.
2016 // Do background processing. Time taken after should not effect page load times.
2021 * Get the length of a string in bytes.
2026 function httprl_strlen($string) {
2028 if (!isset($mb_strlen)) {
2029 $mb_strlen = function_exists('mb_strlen');
2032 return mb_strlen($string, '8bit');
2035 return strlen($string);
2040 * Alt to http_build_url().
2042 * @see http://php.net/parse-url#85963
2045 * array from parse_url()
2049 function httprl_glue_url($parsed) {
2050 if (!is_array($parsed)) {
2054 $uri = isset($parsed['scheme']) ?
$parsed['scheme'] .
':' .
((strtolower($parsed['scheme']) == 'mailto') ?
'' : '//') : '';
2055 $uri .
= isset($parsed['user']) ?
$parsed['user'] .
(isset($parsed['pass']) ?
':' .
$parsed['pass'] : '') .
'@' : '';
2056 $uri .
= isset($parsed['host']) ?
$parsed['host'] : '';
2057 $uri .
= !empty($parsed['port']) ?
':' .
$parsed['port'] : '';
2059 if (isset($parsed['path'])) {
2060 $uri .
= (substr($parsed['path'], 0, 1) == '/') ?
$parsed['path'] : ((!empty($uri) ?
'/' : '' ) .
$parsed['path']);
2063 $uri .
= isset($parsed['query']) ?
'?' .
$parsed['query'] : '';
2064 $uri .
= isset($parsed['fragment']) ?
'#' .
$parsed['fragment'] : '';
2070 * Return the server schema (http or https).
2075 function httprl_get_server_schema() {
2076 return ( (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on')
2077 || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
2078 || (isset($_SERVER['HTTP_HTTPS']) && $_SERVER['HTTP_HTTPS'] == 'on')
2079 ) ?
'https' : 'http';
2083 * Send out a fast 403 and exit.
2085 function httprl_fast403() {
2089 if (!headers_sent()) {
2090 header($_SERVER['SERVER_PROTOCOL'] .
' 403 Forbidden');
2091 header('X-HTTPRL: Forbidden.');
2094 // Print simple 403 page.
2095 print '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' .
"\n";
2097 print '<head><title>403 Forbidden</title></head>';
2098 print '<body><h1>Forbidden</h1>';
2099 print '<p>You are not authorized to access this page.</p>';
2100 print '<p><a href="' .
$base_path .
'">Home</a></p>';
2101 print '<!-- httprl_fast403 -->';
2102 print '</body></html>';
2109 * Release a lock previously acquired by lock_acquire().
2111 * This will release the named lock.
2114 * The name of the lock.
2116 function httprl_lock_release($name) {
2117 if (httprl_variable_get('lock_inc', './includes/lock.inc') !== './includes/lock.inc') {
2118 lock_release($name);
2123 unset($locks[$name]);
2124 if (defined('VERSION') && substr(VERSION
, 0, 1) >= 7) {
2125 db_delete('semaphore')
2126 ->condition('name', $name)
2130 db_query("DELETE FROM {semaphore} WHERE name = '%s'", $name);
2136 * Pretty print data.
2141 * Human readable HTML version of the data.
2143 function httprl_pr($data) {
2144 // Get extra arguments passed in.
2145 $data = func_get_args();
2147 // If empty print out the dump of that variable.
2148 foreach ($data as
$key => &$value) {
2149 if (strlen(print_r($value, TRUE
)) == 0) {
2150 $value = strtoupper(var_export($value, TRUE
));
2154 // Merge into base array if only one argument passed in.
2155 if (count($data) == 1) {
2156 $data = array_pop($data);
2159 // Remove non UTF-8 Characters, escape HTML markup, remove extra new lines.
2160 $output = array_filter(explode("\n", htmlentities(iconv('utf-8', 'utf-8//IGNORE', print_r($data, TRUE
)), ENT_QUOTES
, 'UTF-8')));
2162 // Whitespace compression.
2163 foreach ($output as
$key => $value) {
2164 if (str_replace(' ', '', $value) == "(") {
2165 $output[$key-1] .
= ' (';
2166 unset($output[$key]);
2170 // Replace whitespace with html markup.
2171 $output = str_replace(' ', ' ', nl2br(implode("\n", $output))) .
'<br />';
2176 * Helper function for determining hosts excluded from needing a proxy.
2179 * TRUE if a proxy should be used for this host.
2181 function _httprl_use_proxy($host) {
2182 $proxy_exceptions = httprl_variable_get('proxy_exceptions', array('localhost', '127.0.0.1'));
2183 return !in_array(strtolower($host), $proxy_exceptions, TRUE
);
2187 * Returns a persistent variable.
2189 * This version ignores the $conf global and reads directly from the database.
2191 * Case-sensitivity of the variable_* functions depends on the database
2192 * collation used. To avoid problems, always use lower case for persistent
2196 * The name of the variable to return.
2198 * The default value to use if this variable has never been set.
2200 * The value of the variable.
2202 * @see variable_del(), variable_set()
2204 function httprl_variable_get($name, $default = NULL
) {
2205 // Try global configuration variable first.
2207 if (isset($conf[$name])) {
2208 return $conf[$name];
2211 // Try database next if not at a full bootstrap level.
2212 if (function_exists('db_query') && !httprl_drupal_full_bootstrap()) {
2213 if (defined('VERSION') && substr(VERSION
, 0, 1) >= 7) {
2214 $variables = array_map('unserialize', db_query('SELECT name, value FROM {variable} WHERE name = :name', array(':name' => $name))->fetchAllKeyed());
2216 // Use the default if need be.
2217 return isset($variables[$name]) ?
$variables[$name] : $default;
2220 $result = db_query("SELECT value FROM {variable} WHERE name = '%s'", $name);
2221 if (!empty($result)) {
2222 $result = db_result($result);
2223 if (!empty($result)) {
2224 $value = unserialize($result);
2228 // Use the default if need be.
2229 return isset($value) ?
$value : $default;
2233 // Return default if database is not available or if at a full bootstrap.
2239 * Run multiple functions or methods independently or chained.
2241 * Example for running a Drupal 6 Database query.
2243 * // Run 2 queries and get it's result.
2244 * $max = db_result(db_query('SELECT MAX(wid) FROM {watchdog}'));
2245 * $min = db_result(db_query('SELECT MIN(wid) FROM {watchdog}'));
2246 * echo $max . ' ' . $min;
2248 * // Doing the same thing as above but with a set of arrays.
2253 * 'type' => 'function',
2254 * 'call' => 'db_query',
2255 * 'args' => array('SELECT MAX(wid) FROM {watchdog}'),
2258 * 'type' => 'function',
2259 * 'call' => 'db_result',
2260 * 'args' => array('last' => NULL),
2261 * 'return' => &$max,
2264 * 'type' => 'function',
2265 * 'call' => 'db_query',
2266 * 'args' => array('SELECT MIN(wid) FROM {watchdog}'),
2269 * 'type' => 'function',
2270 * 'call' => 'db_result',
2271 * 'args' => array('last' => NULL),
2272 * 'return' => &$min,
2275 * httprl_run_array($args);
2276 * echo $max . ' ' . $min;
2279 * Example for running a Drupal 7 Database query.
2281 * // Run a query and get it's result.
2282 * $min = db_select('watchdog', 'w')
2283 * ->fields('w', array('wid'))
2284 * ->orderBy('wid', 'DESC')
2290 * // Doing the same thing as above but with a set of arrays.
2294 * 'type' => 'function',
2295 * 'call' => 'db_select',
2296 * 'args' => array('watchdog', 'w',),
2299 * 'type' => 'method',
2300 * 'call' => 'fields',
2301 * 'args' => array('w', array('wid')),
2304 * 'type' => 'method',
2305 * 'call' => 'orderBy',
2306 * 'args' => array('wid', 'DESC'),
2309 * 'type' => 'method',
2310 * 'call' => 'range',
2311 * 'args' => array(999, 1),
2314 * 'type' => 'method',
2315 * 'call' => 'execute',
2316 * 'args' => array(),
2319 * 'type' => 'method',
2320 * 'call' => 'fetchField',
2321 * 'args' => array(),
2322 * 'return' => &$min,
2325 * httprl_run_array($args);
2330 * 2 dimensional array
2333 * 'type' => function or method
2334 * 'call' => function name or name of object method
2336 * List of arguments to pass in. If you set the key to last, the return
2337 * value of the last thing ran will be put in this place.
2340 * 'return' => what was returned from this call.
2341 * 'printed' => what was printed from this call.
2342 * 'error' => any errors that might have occurred.
2343 * 'last' => set the last variable to anything.
2347 function httprl_run_array(&$array) {
2349 foreach ($array as
&$data) {
2350 // Skip if no type is set.
2351 if (!isset($data['type'])) {
2355 // Set the last variable if so desired.
2356 if (isset($data['last'])) {
2357 $last = $data['last'];
2360 // Replace the last key with the last thing that has been returned.
2361 if (isset($data['args']) && array_key_exists('last', $data['args'])) {
2362 $data['args']['last'] = $last;
2363 $data['args'] = array_values($data['args']);
2366 // Capture output if requested.
2367 if (array_key_exists('printed', $data)) {
2371 // Pass by reference trick for call_user_func_array().
2373 if (isset($data['args']) && is_array($data['args'])) {
2374 foreach ($data['args'] as
&$arg) {
2379 // Start to capture errors.
2380 $track_errors = ini_set('track_errors', '1');
2383 // Call a function or a method.
2384 switch ($data['type']) {
2386 if (function_exists($data['call'])) {
2387 $last = call_user_func_array($data['call'], $args);
2390 $php_errormsg = 'Recoverable Fatal error: Call to undefined function ' .
$data['call'] .
'()';
2395 if (method_exists($last, $data['call'])) {
2396 $last = call_user_func_array(array($last, $data['call']), $args);
2399 $php_errormsg = 'Recoverable Fatal error: Call to undefined method ' .
get_class($last) .
'::' .
$data['call'] .
'()';
2405 // Set any errors if any where thrown.
2406 if (!empty($php_errormsg)) {
2407 $data['error'] = $php_errormsg;
2408 ini_set('track_errors', $track_errors);
2409 watchdog('httprl', 'Error thrown in httprl_run_array(). <br /> @error', array('@error' => $php_errormsg), WATCHDOG_ERROR
);
2413 if (array_key_exists('printed', $data)) {
2414 $data['printed'] = ob_get_contents();
2418 // Set what was returned from each call.
2419 if (array_key_exists('return', $data)) {
2420 $data['return'] = $last;
2424 return array('args' => array($array));
2428 * Run a single function.
2431 * Name of function to run.
2432 * @param $input_args
2433 * list of arguments to pass along to the function.
2435 function httprl_run_function($function, &$input_args) {
2436 // Pass by reference trick for call_user_func_array().
2438 foreach ($input_args as
&$arg) {
2442 // Capture anything printed out.
2445 // Start to capture errors.
2446 $track_errors = ini_set('track_errors', '1');
2450 if (function_exists($function)) {
2451 $return = call_user_func_array($function, $args);
2454 $php_errormsg = 'Recoverable Fatal error: Call to undefined function ' .
$function .
'()';
2457 $printed = ob_get_contents();
2460 // Create data array.
2461 $data = array('return' => $return, 'args' => $args, 'printed' => $printed);
2463 // Set any errors if any where thrown.
2464 if (!empty($php_errormsg)) {
2465 $data['error'] = $php_errormsg;
2466 ini_set('track_errors', $track_errors);
2467 watchdog('httprl', 'Error thrown in httprl_run_function(). <br /> @error', array('@error' => $php_errormsg), WATCHDOG_ERROR
);
2473 function httprl_boot() {
2475 $full_url = $base_root .
request_uri();
2477 // Return if this is not a httprl_async_function_callback request.
2478 if ( strpos($full_url, '/httprl_async_function_callback') === FALSE
2479 || $_SERVER['REQUEST_METHOD'] !== 'POST'
2480 || empty($_POST['master_key'])
2481 || empty($_POST['temp_key'])
2482 || strpos($_POST['temp_key'], 'httprl_') !== 0
2483 || !empty($_POST['function'])
2488 // Load httprl.async.inc.
2489 if (defined('DRUPAL_ROOT')) {
2490 require_once DRUPAL_ROOT .
'/' .
dirname(drupal_get_filename('module', 'httprl')) .
'/httprl.async.inc';
2493 require_once
'./' .
dirname(drupal_get_filename('module', 'httprl')) .
'/httprl.async.inc';
2495 httprl_async_page();
2499 * Gets the private key variable.
2504 function httprl_drupal_get_private_key() {
2505 $full_bootstrap = httprl_drupal_full_bootstrap();
2507 $private_key = $full_bootstrap ?
drupal_get_private_key() : httprl_variable_get('drupal_private_key', 0);
2508 return $private_key;
2512 * Performs end-of-request tasks and/or call exit directly.
2514 function httprl_call_exit() {
2515 if (defined('VERSION') && substr(VERSION
, 0, 1) >= 7 && drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL
) {
2524 * Sees if Drupal has been fully booted.
2527 * TRUE if DRUPAL_BOOTSTRAP_FULL.
2528 * FALse if not DRUPAL_BOOTSTRAP_FULL.
2530 function httprl_drupal_full_bootstrap() {
2531 static
$full_bootstrap;
2532 if (!isset($full_bootstrap)) {
2533 // See if a full bootstrap has been done given the Drupal version.
2534 if (defined('VERSION') && substr(VERSION
, 0, 1) >= 7) {
2535 $level = drupal_bootstrap();
2536 $full_bootstrap = ($level == DRUPAL_BOOTSTRAP_FULL
) ? TRUE
: FALSE
;
2539 $full_bootstrap = isset($GLOBALS['multibyte']) ? TRUE
: FALSE
;
2542 return $full_bootstrap;
2546 * Sets the global user to the given user ID.
2549 * Integer specifying the user ID to load.
2551 function httprl_set_user($uid) {
2553 $account = user_load($uid);
2554 if (!empty($account)) {
2561 * Sets the global $_GET['q'] parameter.
2566 function httprl_set_q($q) {