Request_HTTP: Fixed get_response_body(), added HTTP redirect support, and added support for Content-Length.
authorDan
Sat, 12 Sep 2009 09:56:39 -0400
changeset 1113 000791abdc7e
parent 1112 679916c80599
child 1114 4f4d63a281cd
Request_HTTP: Fixed get_response_body(), added HTTP redirect support, and added support for Content-Length.
includes/http.php
--- a/includes/http.php	Fri Sep 11 09:57:42 2009 -0400
+++ b/includes/http.php	Sat Sep 12 09:56:39 2009 -0400
@@ -448,25 +448,81 @@
       $this->_parse_response_code($buffer);
       $this->response = $buffer;
     }
+    // obey redirects
+    $i = 0;
+    while ( $i < 20 )
+    {
+      $incoming_headers = $this->get_response_headers_array();
+      if ( !$incoming_headers )
+        break;
+      if ( isset($incoming_headers['Location']) )
+      {
+        // we've been redirected...
+        $new_uri = $this->_resolve_uri($incoming_headers['Location']);
+        if ( !$new_uri )
+        {
+          // ... bad URI, ignore Location header.
+          break;
+        }
+        // change location
+        $this->host = $new_uri['host'];
+        $this->port = $new_uri['port'];
+        $this->uri  = $new_uri['uri'];
+        $get = '';
+        
+        // reset
+        $this->sock_close($this->socket);
+        $this->_sock_open($this->socket);
+        $this->_write_request($this->socket, $headers, $cookies, $get, $post);
+        $buffer = $this->_read_until_newlines($this->socket);
+        $this->state = 3;
+        $this->_parse_response_code($buffer);
+        $this->response = $buffer;
+        $i++;
+      }
+      else
+      {
+        break;
+      }
+    }
+    if ( $i == 20 )
+    {
+      throw new Exception(__METHOD__ . ": Redirect trap. Request_HTTP doesn't do cookies, btw.");
+    }
+    
     if ( $this->state == 3 )
     {
       // Determine transfer encoding
       $is_chunked = preg_match("/Transfer-Encoding: (chunked)\r?\n/", $this->response);
-      
-      $buffer = '';
-      while ( !feof($this->socket) )
+      if ( preg_match("/^Content-Length: ([0-9]+)[\s]*$/mi", $this->response, $match) && !$is_chunked )
       {
-        $part = fgets($this->socket, 1024);
-        if ( $is_chunked && preg_match("/^([a-f0-9]+)\x0D\x0A$/", $part, $match) )
+        $size = intval($match[1]);
+        if ( $this->debug )
         {
-          $chunklen = hexdec($match[1]);
-          $part = ( $chunklen > 0 ) ? fread($this->socket, $chunklen) : '';
-          // remove the last newline from $part
-          $part = preg_replace("/\r?\n\$/m", "", $part);
+          echo "Pulling response using fread(), size $size\n";
         }
-        $buffer .= $part;
+        $this->response .= fread($this->socket, $size);
       }
-      $this->response .= $buffer;
+      else
+      {
+        if ( $this->debug )
+          echo "Pulling response using chunked handler\n";
+          
+        $buffer = '';
+        while ( !feof($this->socket) )
+        {
+          $part = fgets($this->socket, 1024);
+          if ( $is_chunked && preg_match("/^([a-f0-9]+)\x0D\x0A$/", $part, $match) )
+          {
+            $chunklen = hexdec($match[1]);
+            $part = ( $chunklen > 0 ) ? fread($this->socket, $chunklen) : '';
+            // remove the last newline from $part
+            $part = preg_replace("/\r?\n\$/", "", $part);
+          }
+          $buffer .= $part;
+        }
+        $this->response .= $buffer;
+      }
     }
     $this->state = 4;
     
@@ -639,6 +695,50 @@
   }
   
   /**
+   * Resolves, based on current settings and URI, a URI string to an array consisting of a host, port, and new URI. Returns false on error.
+   * @param string
+   * @return array
+   */
+  
+  function _resolve_uri($uri)
+  {
+    // long ass regexp w00t
+    if ( !preg_match('#^(?:https?://((?:(?:[a-z0-9-]+\.)*)(?:[a-z0-9-]+)|\[[a-f0-9:]+\])(?::([0-9]+))?)?(/)(.*)$#i', $uri, $match) )
+    {
+      // bad target URI
+      return false;
+    }
+    $hostpart = $match[1];
+    if ( empty($hostpart) )
+    {
+      // use existing host
+      $host = $this->host;
+      $port = $this->port;
+    }
+    else
+    {
+      $host = $match[1];
+      $port = empty($match[2]) ? 80 : intval($match[2]);
+    }
+    // is this an absolute URI, or relative?
+    if ( empty($match[3]) )
+    {
+      // relative
+      $uri = dirname($this->uri) . $match[4];
+    }
+    else
+    {
+      // absolute
+      $uri = '/' . $match[4];
+    }
+    return array(
+        'host' => $host,
+        'port' => $port,
+        'uri'  => $uri
+      );
+  }
+  
+  /**
    * Returns only the response headers.
    * @return string
    */
@@ -652,7 +752,11 @@
     else if ( $this->state == 4 )
     {
       $pos_end = strpos($this->response, "\r\n\r\n");
-      $data = substr($this->response, 0, $pos_start);
+      if ( empty($pos_end) )
+      {
+        $pos_end = strpos($this->response, "\n\n");
+      }
+      $data = substr($this->response, 0, $pos_end);
       return $data;
     }
     else
@@ -688,6 +792,10 @@
   {
     $data = $this->get_response();
     $pos_start = strpos($data, "\r\n\r\n") + 4;
+    if ( $pos_start == 4 )
+    {
+      $pos_start = strpos($data, "\n\n") + 4;
+    }
     $data = substr($data, $pos_start);
     return $data;
   }
@@ -723,6 +831,7 @@
   {
     if ( $this->debug )
       echo htmlspecialchars($data);
+    
     return fputs($socket, $data);
   }
   
@@ -793,4 +902,3 @@
   
 }
 
-?>