diff --git a/httprl.module b/httprl.module index 1053360..bff7d0c 100644 --- a/httprl.module +++ b/httprl.module @@ -51,6 +51,11 @@ define('HTTPRL_URL_INVALID_SCHEMA', -1003); define('HTTPRL_ERROR_INITIALIZING_STREAM', -1004); /** + * Error code indicating that the DNS lookup timed out. + */ +define('HTTPRL_DNS_TIMEOUT', -1005); + +/** * Error code indicating that the request exceeded the specified timeout. * * @see http://msdn.microsoft.com/en-us/library/aa924071.aspx @@ -322,6 +327,18 @@ function httprl_request($url, $options = array()) { $flags = STREAM_CLIENT_CONNECT; } + // hook_httprl_request_alter() + // Allow other programs to alter the connection before it is made. + $data = array($socket, $flags, $uri, $url, $options, $result); + drupal_alter('httprl_request', $data); + list($socket, $flags, $uri, $url, $options, $result) = $data; + + if (isset($result->error)) { + // Add in failed request to the output. + // Exit if an invalid scheme was passed in. + return httprl_send_request(FALSE, $url, $result, $options); + } + // Start the timer. $timer_name = mt_rand(); timer_start($timer_name); @@ -1126,6 +1143,78 @@ function httprl_background_processing($output, $wait = TRUE, $content_type = "te } /** + * Implement hook_httprl_request_alter(). + * + * Use nslookup because it supports timeouts. + * Use static cache for DNS lookups. + */ +function httprl_httprl_request_alter(&$data) { + // Use the advanced drupal_static() pattern. + static $hosts; + if (!isset($hosts) && function_exists('drupal_static')) { + $hosts = &drupal_static(__FUNCTION__); + } + + // Extract parameters passed in. + list($socket, $flags, $uri, $url, $options, $result) = $data; + + // Return if request timed out. + if ($options['timeout'] < 0) { + return; + } + // Return if request already has an error set. + if (isset($result->error)) { + return; + } + + // Parse URL, getting hostname for DNS lookup. + $parsed = @parse_url($socket); + if (empty($parsed) || empty($parsed['host'])) { + return; + } + if (!is_numeric($options['timeout'])) { + return; + } + + + // See if host was already looked up. + if (isset($hosts[$parsed['host']])) { + $parsed['host'] = $hosts[$parsed['host']]; + } + else { + // Start the timer. + $timeout = max(1, floor($options['timeout']-1)); + $timer_name = mt_rand(); + timer_start($timer_name); + + // Execute nslookup. + $query = shell_exec('nslookup -timeout=' . $timeout . ' -retry=1 ' . escapeshellarg($parsed['host'])); + if (preg_match('/\nAddress: (.*)\n/', $query, $matches)) { + $matches[1] = trim($matches[1]); + $hosts[$parsed['host']] = $matches[1]; + $parsed['host'] = $matches[1]; + } + + // Stop the timer. + $options['timeout'] = $options['timeout'] - timer_read($timer_name) / 1000; + timer_stop($timer_name); + + // Set error if we are out of time. + if ($options['timeout'] < 0) { + $result->code = HTTPRL_DNS_TIMEOUT; + $result->error = t('DNS lookup timed out. php_network_getaddresses: getaddrinfo failed: Temporary failure in name resolution.'); + } + } + + // Glue $socket back together. + $socket = httprl_glue_url($parsed); + + // Send it back. + $data = array($socket, $flags, $uri, $url, $options, $result); +} + + +/** * Get the length of a string in bytes. * * @param $string