diff --git a/includes/common.inc b/includes/common.inc
index 3d73784..0ee0af3 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -544,15 +544,41 @@ function drupal_http_request($url, $headers = array(), $method = 'GET', $data =
 
   $result->request = $request;
 
+  // Maximum time that making and reading the request may take.
+  $total_time = variable_get('drupal_http_request_total_time', 30);
+  timer_start(__FUNCTION__);
+  stream_set_timeout($fp, $total_time);
+
   fwrite($fp, $request);
 
-  // Fetch response.
+  // Fetch response. Due to PHP bugs like http://bugs.php.net/bug.php?id=43782
+  // and http://bugs.php.net/bug.php?id=46049 we can't rely on feof(), but
+  // instead must invoke stream_get_meta_data() each iteration.
+  $info = stream_get_meta_data($fp);
+  $alive = !$info['eof'] && !$info['timed_out'];
+
   $response = '';
-  while (!feof($fp) && $chunk = fread($fp, 1024)) {
+
+  while ($alive) {
+    $timeout = $total_time - timer_read(__FUNCTION__) / 1000;
+    if ($timeout <= 0) {
+      $info['timed_out'] = TRUE;
+      break;
+    }
+    stream_set_timeout($fp, floor($timeout), floor(1000000 * fmod($timeout, 1)));
+    $chunk = fread($fp, 1024);
     $response .= $chunk;
+    $info = stream_get_meta_data($fp);
+    $alive = !$info['eof'] && !$info['timed_out'] && $chunk;
   }
   fclose($fp);
 
+  if ($info['timed_out']) {
+    $result->code = 1;
+    $result->error = 'request timed out';
+    return $result;
+  }
+
   // Parse response.
   list($split, $result->data) = explode("\r\n\r\n", $response, 2);
   $split = preg_split("/\r\n|\n|\r/", $split);
