You are here

httprl.install in HTTP Parallel Request & Threading Library 7

Same filename and directory in other branches
  1. 6 httprl.install

Handle HTTP Parallel Request Library installation and upgrade tasks.

File

httprl.install
View source
<?php

/**
 * @file
 * Handle HTTP Parallel Request Library installation and upgrade tasks.
 */

/**
 * Implements hook_uninstall().
 */
function httprl_uninstall() {
  variable_del('httprl_server_addr');
  variable_del('httprl_server_hostname');
  variable_del('httprl_server_port');
  variable_del('httprl_server_schema');
  variable_del('httprl_background_callback');
  variable_del('httprl_dns_timeout');
  variable_del('httprl_connect_timeout');
  variable_del('httprl_ttfb_timeout');
  variable_del('httprl_timeout');
  variable_del('httprl_global_timeout');
  variable_del('httprl_url_inbound_alter');
  variable_del('httprl_non_blocking_fclose_delay');
}

/**
 * Implements hook_requirements().
 */
function httprl_requirements($phase) {
  $requirements = array();

  // Ensure translations don't break at install time.
  $t = get_t();
  if ($phase == 'runtime' || $phase == 'install') {
    $function_list = array(
      'stream_socket_client',
      'stream_select',
      'stream_set_blocking',
      'stream_get_meta_data',
      'stream_socket_get_name',
    );

    // Check each function to make sure it exists.
    foreach ($function_list as $function_name) {
      if (!function_exists($function_name)) {
        $requirements['httprl_function_' . $function_name] = array(
          'title' => $t('HTTPRL'),
          'value' => $phase == 'install' ? FALSE : $function_name,
          'severity' => REQUIREMENT_ERROR,
          'description' => $t('<a href="!url">%name()</a> is disabled on this server. Please contact your hosting provider and see if they can re-enable this function for you.', array(
            '!url' => 'http://php.net/' . str_replace('_', '-', $function_name),
            '%name' => $function_name,
          )),
        );
      }
    }
  }
  if ($phase == 'runtime') {

    // Check that the menu router item is working. If it is not working, the
    // rest of the tests below will be pointless.
    $item = menu_get_item('httprl_async_function_callback');
    if (empty($item['page_callback']) || strpos($item['page_callback'], 'httprl') === FALSE) {
      $requirements['httprl_callback'] = array(
        'title' => $t('HTTPRL - Menu Callback'),
        'severity' => REQUIREMENT_WARNING,
        'value' => $t('Flush your caches'),
        'description' => $t('You need to flush your menu cache. The background callback for httprl is not there.'),
      );
      return $requirements;
    }
    if (defined('VERSION') && substr(VERSION, 0, 1) >= 7 && httprl_variable_get('maintenance_mode', 0) || httprl_variable_get('site_offline', 0)) {
      if (empty($requirements)) {
        $requirements['httprl'] = array(
          'title' => $t('HTTPRL'),
          'severity' => REQUIREMENT_INFO,
          'value' => $phase == 'install' ? TRUE : $t('All the required functions are enabled, but non blocking requests can not be tested while the site is in maintenance mode.'),
        );
      }
      return $requirements;
    }

    // Test the non-blocking url.
    list($success, $msg_non_blocking) = httprl_install_http_test(2, FALSE);
    if (!$success) {

      // Test the blocking url.
      list($success, $msg_blocking) = httprl_install_http_test(2, TRUE);
      if (!$success) {

        // Test that drupal_http_request() works.
        list($success, $msg_core) = httprl_install_http_test(1);
        if (!$success) {
          $requirements['httprl_callback'] = array(
            'title' => $t('HTTPRL - Core'),
            'severity' => REQUIREMENT_ERROR,
            'value' => $t('drupal_http_request()'),
            'description' => $t('Your system or network configuration does not allow Drupal to access web pages. This could be due to your webserver configuration or PHP settings. Debug info: !debug <br />For more info go here: <a href="!link">"HTTP request status Fails" error</a>', array(
              '!link' => 'http://drupal.org/node/588186',
              '!debug' => httprl_pr($GLOBALS['_httprl']['install']['debug'], TRUE),
            )),
          );
          return $requirements;
        }
        $requirements['httprl_blocking'] = array(
          'title' => $t('HTTPRL - Blocking'),
          'severity' => REQUIREMENT_ERROR,
          'value' => $t('Problem with stream_select()'),
          'description' => $t('This server can not issue self http requests with stream_select(). Debug info: !debug <br />', array(
            '!debug' => httprl_pr($GLOBALS['_httprl']['install']['debug'], TRUE),
          )),
        );
        return $requirements;
      }
      $requirements['httprl_nonblocking'] = array(
        'title' => $t('HTTPRL - Non Blocking'),
        'severity' => REQUIREMENT_WARNING,
        'value' => $t('This server does not handle hanging connections.'),
        'description' => $t('Using non blocking mode on this server may not work correctly. Debug info: !debug <br />', array(
          '!debug' => httprl_pr($GLOBALS['_httprl']['install']['debug'], TRUE),
        )),
      );
    }
  }
  if (!empty($msg_non_blocking)) {
    $requirements['httprl_settings_change_a'] = array(
      'title' => $t('HTTPRL - Settings'),
      'severity' => REQUIREMENT_WARNING,
      'value' => $t('The current configuration does not work.'),
      'description' => $msg_non_blocking,
    );
  }
  if (!empty($msg_blocking)) {
    $requirements['httprl_settings_change_b'] = array(
      'title' => $t('HTTPRL - Settings'),
      'severity' => REQUIREMENT_WARNING,
      'value' => $t('The current configuration does not work.'),
      'description' => $msg_blocking,
    );
  }
  if (!empty($msg_core)) {
    $requirements['httprl_settings_change_c'] = array(
      'title' => $t('HTTPRL - Settings'),
      'severity' => REQUIREMENT_WARNING,
      'value' => $t('The current configuration does not work.'),
      'description' => $msg_core,
    );
  }
  if (empty($requirements)) {
    $requirements['httprl'] = array(
      'title' => $t('HTTPRL'),
      'severity' => REQUIREMENT_OK,
      'value' => $phase == 'install' ? TRUE : $t('All the required functions are enabled and non blocking requests are working.'),
    );
  }
  return $requirements;
}

/**
 * Issue a HTTP request to admin/httprl-test, verifying that the server got it.
 *
 * @param int $mode
 *   1: use drupal_http_request()
 *   2: use httprl_request()
 * @param bool $blocking
 *   (Optional) HTTPRL blocking mode.
 *
 * @return array
 *   (bool, msg).
 */
function httprl_install_http_test($mode, $blocking = FALSE, $depth = 0) {
  $t = get_t();
  $depth++;
  set_time_limit(0);

  // 512 bits = 64 bytes.
  if (function_exists('drupal_random_bytes')) {
    $id = 'httprl_' . hash('sha512', drupal_random_bytes(64));
  }
  elseif (function_exists('openssl_random_pseudo_bytes')) {
    $id = 'httprl_' . hash('sha512', openssl_random_pseudo_bytes(64));
  }
  else {
    $id = 'httprl_' . hash('sha512', mt_rand() . microtime(TRUE) . serialize($_SERVER));
  }
  $msg = '';
  $hostname = httprl_get_hostname();

  // Set the headers to point to this hostname.
  $headers = array(
    'Host' => $hostname,
    'Connection' => 'closed',
  );

  // Setup the arguments for releasing the lock.
  $timing = array(
    httprl_variable_get('httprl_install_lock_time', 7),
    httprl_variable_get('httprl_install_connection_time', 5),
  );
  $args = array(
    array(
      'function' => 'httprl_lock_release',
      // Setup options array.
      'options' => array(
        'blocking' => $blocking,
        'timeout' => $timing[0],
        'max_redirects' => 0,
        'headers' => $headers,
      ),
    ),
    $id,
  );

  // Get a lock & start the timer.
  lock_acquire($id, $args[0]['options']['timeout']);
  timer_start($id);
  if ($mode == 2) {

    // Queue up the request.
    if ($blocking) {
      $args[0]['return'] = '';
      $args[0]['printed'] = '';
    }

    // Enable background callbacks even if disabled.
    $old_var = httprl_variable_get('httprl_background_callback', HTTPRL_BACKGROUND_CALLBACK);
    $GLOBALS['conf']['httprl_background_callback'] = HTTPRL_BACKGROUND_CALLBACK;
    $url = httprl_queue_background_callback($args);
    if (empty($url)) {
      return array(
        FALSE,
        $t('The background callbacks setting is disabled.'),
      );
    }
    else {
      $url = array_keys($url);
      $url = array_pop($url);

      // Execute request.
      $output = httprl_send_request();
    }

    // Restore the background callbacks setting.
    $GLOBALS['conf']['httprl_background_callback'] = $old_var;
  }
  else {

    // Get options.
    $callback_options = array_shift($args);

    // Build URL to point to httprl_async_function_callback on this server.
    $url = httprl_build_url_self('httprl_async_function_callback?count=0', TRUE);

    // Create lock name for this run.
    $available = FALSE;
    $lock_counter = 0;
    while (!$available && $lock_counter < 20) {

      // 512 bits = 64 bytes.
      if (function_exists('drupal_random_bytes')) {
        $name = 'httprl_' . hash('sha512', drupal_random_bytes(64));
      }
      elseif (function_exists('openssl_random_pseudo_bytes')) {
        $name = 'httprl_' . hash('sha512', openssl_random_pseudo_bytes(64));
      }
      else {
        $name = 'httprl_' . hash('sha512', mt_rand() . microtime(TRUE) . serialize($_SERVER));
      }
      $available = lock_may_be_available($name);
      $lock_counter++;
    }
    $callback_options['options']['lock_name'] = $name;
    lock_acquire($name, $callback_options['options']['timeout']);

    // Create data array and options for request.
    $options = array(
      'data' => array(
        'master_key' => hash('sha512', httprl_drupal_get_private_key()),
        'temp_key' => $name,
        'mode' => TRUE,
        'php_timeout' => $callback_options['options']['timeout'],
        'function' => $callback_options['function'],
        // Follow rfc4648 for base64url
        // @see http://tools.ietf.org/html/rfc4648#page-7
        'args' => strtr(base64_encode(serialize($args)), array(
          '+' => '-',
          '/' => '_',
        )),
      ),
      'method' => 'POST',
      'headers' => $headers,
      'timeout' => $callback_options['options']['timeout'],
      'max_redirects' => $callback_options['options']['max_redirects'],
    );
    httprl_handle_data($options);

    // Execute the request using core.
    if (defined('VERSION') && substr(VERSION, 0, 1) >= 7) {
      $output = drupal_http_request($url, $options);
    }
    else {
      $output = drupal_http_request($url, $options['headers'], $options['method'], $options['data'], $options['max_redirects'], $options['timeout']);
    }
  }

  // Wait for the lock and stop the timer.
  while (lock_wait($id)) {
    usleep(25000);
  }
  $time = timer_stop($id);

  // Add in debugging info.
  $time['mode'] = $mode;
  $time['blocking'] = $blocking;
  $time['url'] = $url;
  $time['request'] = $output;
  $GLOBALS['_httprl']['install']['debug'][] = $time;

  // See if the request came back in under 5 seconds, or if it timed out.
  if ($time['time'] / 1000 > $timing[1]) {
    if ($depth <= 1) {
      list($success, $msg) = httprl_install_try_different_settings_checker($mode, $blocking, $depth);
      if ($success) {
        return array(
          $success,
          $msg,
        );
      }
    }
  }
  else {
    $hostname = httprl_get_hostname();

    // Check if the httprl_server_hostname needs to be set to HTTP_HOST or
    // SERVER_NAME.
    if (!empty($hostname) && $hostname !== 'default' && ip2long($hostname) === FALSE && httprl_variable_get('httprl_server_hostname', FALSE) != $hostname) {
      $msg = $t('The "Host name of the server to send all self server requests to" setting needs to be changed to @hostname on the <a href="@url">configuration page</a>,', array(
        '@url' => url('admin/config/development/httprl'),
        '@hostname' => $hostname,
      ));
    }
    return array(
      TRUE,
      $msg,
    );
  }
}
function httprl_install_fclose_delay_check($mode, $blocking, $depth) {
  $t = get_t();
  $msg = '';
  if (httprl_variable_get('httprl_non_blocking_fclose_delay', HTTPRL_NON_BLOCKING_FCLOSE_DELAY) != 0 || $blocking != FALSE) {
    return;
  }

  // Try again with a fclose_delay of 25ms.
  $GLOBALS['conf']['httprl_non_blocking_fclose_delay'] = 25;
  list($success, $msg) = httprl_install_http_test($mode, $blocking, $depth);
  if ($success) {

    // Try again with a fclose_delay of 5ms.
    $GLOBALS['conf']['httprl_non_blocking_fclose_delay'] = 5;
    list($success, $msg) = httprl_install_http_test($mode, $blocking, $depth);
    if ($success) {
      $msg = $t('The Millisecond Delay For Non-Blocking Requests setting needs to be changed to 5 on the <a href="@url">configuration page</a>,', array(
        '@url' => url('admin/config/development/httprl'),
      ));
    }
    else {
      $msg = $t('The Millisecond Delay For Non-Blocking Requests setting needs to be changed to 25 on the <a href="@url">configuration page</a>,', array(
        '@url' => url('admin/config/development/httprl'),
      ));
    }
  }
  $GLOBALS['conf']['httprl_non_blocking_fclose_delay'] = 0;
  return array(
    $success,
    $msg,
  );
}
function httprl_install_try_different_settings_checker($mode, $blocking, $depth) {
  $t = get_t();
  list($success, $msg) = httprl_install_fclose_delay_check($mode, $blocking, $depth);
  if ($success) {
    return array(
      $success,
      $msg,
    );
  }
  $msg = '';
  $ip = httprl_variable_get('httprl_server_addr', FALSE);

  // Try with it empty.
  $GLOBALS['conf']['httprl_server_addr'] = FALSE;
  httprl_build_url_self('', FALSE, TRUE);
  list($success, $msg) = httprl_install_http_test($mode, $blocking, $depth);
  if ($success) {
    $msg = $t('The "IP Address to send all self server requests to" setting needs to be empty on the <a href="@url">configuration page</a>,', array(
      '@url' => url('admin/config/development/httprl'),
    ));
  }
  else {
    list($success, $msg) = httprl_install_fclose_delay_check($mode, $blocking, $depth);
    if ($success) {
      $msg .= ' ' . $t('The "IP Address to send all self server requests to" setting needs to be empty on the <a href="@url">configuration page</a>,', array(
        '@url' => url('admin/config/development/httprl'),
      ));
    }

    // Try again with -1.
    $GLOBALS['conf']['httprl_server_addr'] = -1;
    httprl_build_url_self('', FALSE, TRUE);
    list($success, $msg) = httprl_install_http_test($mode, $blocking, $depth);
    if ($success) {
      $msg = $t('The "IP Address to send all self server requests to" setting needs to be changed to -1 on the <a href="@url">configuration page</a>,', array(
        '@url' => url('admin/config/development/httprl'),
      ));
    }
    else {
      list($success, $msg) = httprl_install_fclose_delay_check($mode, $blocking, $depth);
      if ($success) {
        $msg .= ' ' . $t('The "IP Address to send all self server requests to" setting needs to be changed to -1 on the <a href="@url">configuration page</a>,', array(
          '@url' => url('admin/config/development/httprl'),
        ));
      }
    }
  }
  $GLOBALS['conf']['httprl_server_addr'] = $ip;
  httprl_build_url_self('', FALSE, TRUE);
  return array(
    $success,
    $msg,
  );
}

Functions

Namesort descending Description
httprl_install_fclose_delay_check
httprl_install_http_test Issue a HTTP request to admin/httprl-test, verifying that the server got it.
httprl_install_try_different_settings_checker
httprl_requirements Implements hook_requirements().
httprl_uninstall Implements hook_uninstall().