From:             bret at levycodev dot com
Operating system: win32
PHP version:      4.3.3
PHP Bug Type:     Sockets related
Bug description:  fgets/fread on open socket looks like eof

Description:
------------
I have an "HTTP requestor" class that I wrote many years ago using
fsocketopen, fgets and fread.  It works and has always worked up until I
load PHP 4.3.3.  If I backrev to 4.3.0rc2 (the last rev I was running) the
code works again.

Symptoms are this: I connect fine, I get the request sent fine, I see the
server respond with the correct and full message, but the php client
sometimes, but not always, does not see the whole response.  Sometimes the
fread doesn't return everything requested.

IMHO, this is not the same problem as the other reported socket_xxx
related bugs, but I fully admit that I have not looked at the source!

Included is the class that sends/receives the http message (and yes, there
is probably something in PHP now that does this all for me, but this
always worked before, and still works in PHP <= 4.3.0rc2).  There were a
few recent changes (I think), like the timeout call and the removal of the
call by reference on the fsocketopen call, to make php 4.3 happy.

Thanks,
Bret


Reproduce code:
---------------
<?php

   
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //
    //      http.php
    //      ========
    //
    //      this class supplies a generic http request/response interface
for making requests to server (daemons)
    //
    //      COPYRIGHT (C) 2000, Bret Levy.  All Rights Are Reserved.
    //
    //      ver 1.0.0 2000-01-20 BLevy
    //
   
////////////////////////////////////////////////////////////////////////////////////////////////////////////////

class HTTP {

    // private class properties

  var $m_ReqMethod;
  var $m_ReqURL;
  var $m_ReqProtocol;
  var $m_ReqHeaders;
  var $m_ReqEntity;
  var $m_RspProtocol;
  var $m_RspStatus;
  var $m_RspMessage;
  var $m_RspHeaders;
  var $m_RspEntity;
  var $m_MaxRetries;
  var $m_LastErr;
  var $m_Timeout;

    // public class methods

  function Process ($sHost="127.0.0.1", $nPort=80) {

    $tries = 0;
    
    do {
    
        // init ok to true
        
        $ok = 1;
        
          // open a connection to the server
    
        $sck = fsockopen ($sHost, $nPort, $errnum, $errmsg, 2.0);
        if ($sck === FALSE) {
          $this->m_LastErr = $errmsg;
          $ok = 0;
          }

        // see if we need to set a timeout
        
        if ($this->m_Timeout > 0) {
            $s = (int) ($this->m_Timeout / 1000);
            $m = ($this->m_Timeout % 1000) * 1000;
            stream_set_timeout ($sck, $s, $m);
        }
        
        // do the i/o if we got in
        
        if ($ok) {
            
              // add the request method line
        
            $msg = $this->m_ReqMethod . " " . $this->m_ReqURL . " " .
$this->m_ReqProtocol . "\r\n";

              // add the request headers (supress content-len header(s))
        
            reset ($this->m_ReqHeaders);
            $nCount = sizeof ($this->m_ReqHeaders);
            for ($i=0; $i<$nCount; $i++) {
              list($key,$val) = each ($this->m_ReqHeaders);
              if (strcasecmp($key,"content-length") != 0) {
                $msg .= "$key:$val\r\n";
                }
              }
        
              // ensure a content-length header went out (if we have an
entity to send)
        
            if ($this->m_ReqEntity != "") {
              $msg .= "Content-Length:" . strlen($this->m_ReqEntity) .
"\r\n";
              }
        
              // end the headers section
        
            $msg .= "\r\n";
        
              // write the entity body
        
            if ($this->m_ReqEntity != "") {
              $msg .= $this->m_ReqEntity;
              }

              // write the request
                      
            $size = fwrite ($sck, $msg);
              
              // read the response header line
        
            $methbuf = fgets ($sck, 8192);
            $methinfo = explode (" ", $methbuf, 3);
            $this->m_RspProtocol = $methinfo[0];
            $this->m_RspStatus = (int) $methinfo[1];
            $this->m_RspMessage = $methinfo[2];
        
              // read the response headers
        
            $this->m_RspHeaders = array();
        
            do {
              $buf = fgets ($sck, 8192);
              $hdrinfo = explode (":", $buf);
              $hdrid = trim ($hdrinfo[0]);
              if ($hdrid != "") {
                $hdrval = strstr ($buf, ":");
                $hdrval = trim (substr($hdrval,1));
                $this->m_RspHeaders[strtolower($hdrid)] = $hdrval;
                }
                } while (!feof($sck) && ($hdrid != ""));
        
              // now read the content blob (could be chunked???)
        
            if (strtolower($this->m_RspHeaders["transfer-encoding"]) ==
"chunked") {

                // init the result string buffer

                $data = "";

                // read chunks until we have no more (chunk size of 0)

                do {

                    $chunkid = fgets ($sck, 8192);
                    $chunklen = hexdec (trim($chunkid));
                    if ($chunklen > 0) {
                        $data .= fread ($sck, $chunklen);
                    }

                } while ($chunklen > 0);

                // save the (unchunked) data
                
                $this->m_RspEntity = $data;
                
            }
                
            // the response is NOT chunked
                        
            else {
                $contlen = (int) $this->m_RspHeaders["content-length"];
                if ($contlen > 0) {
                    $this->m_RspEntity = fread ($sck, $contlen);
                }
                else {
                    $this->m_RspEntity = "";
                }
            }
               
              // done with the socket
        
            fclose ($sck);
            
           }
    
        // inc the retry counter
        
        $tries++;
            
    } while (($tries < $this->m_MaxRetries) && ($ok == 0));
    
      // return the status code

    return $this->m_RspStatus;
    }

  function SetRequestMethod ($method) {
    $this->m_ReqMethod = strtoupper($method);
    }

  function GetRequestMethod () {
    return $this->m_ReqMethod;
    }

  function SetRequestURL ($url) {
    $this->m_ReqURL = $url;
    }

  function GetRequestURL () {
    return $this->m_ReqURL;
    }

  function SetRequestProtocol ($proto) {
    $this->m_ReqProtocol = strtoupper($proto);
    }

  function GetRequestProtocol () {
    return $this->m_ReqProtocol;
    }

  function SetRequestHeader ($hdr, $val) {
    $this->m_ReqHeaders[strtolower($hdr)] = $val;
    }

  function GetRequestHeader ($hdr) {
    return $this->m_ReqHeaders[strtolower($hdr)];
    }

  function SetRequestEntity ($entity) {
    $this->m_ReqEntity = $entity;
    }

  function GetRequestEntity () {
    return $this->m_ReqEntity;
    }

  function GetResponseProtocol () {
    return $this->m_RspProtocol;
    }

  function GetResponseStatus () {
    return $this->m_RspStatus;
    }

  function GetResponseMessage () {
    return $this->m_RspMessage;
    }

  function GetResponseHeaderCount () {
    return sizeof ($this->m_RspHeaders);
    }

  function GetResponseHeaderInfo ($index, &$key, &$val) {

    $count = sizeof ($this->m_RspHeaders);
    if (($index < 0) || ($index >= $count)) {
      $key = "";
      $val = "";
      return;
      }

    $i = 0;
    reset ($this->m_RspHeaders);
    do {
      list($key,$val) = each ($this->m_RspHeaders);
      $i++;
      } while ($i <= $index);
    }

  function SetResponseHeader ($hdr, $val) {
    $this->m_RspHeaders[strtolower($hdr)] = $val;
    }

  function GetResponseHeader ($hdr) {
    return $this->m_RspHeaders[strtolower($hdr)];
    }

  function SetResponseEntity ($entity) {
    $this->m_RspEntity = $entity;
    }

  function GetResponseEntity () {
    return $this->m_RspEntity;
    }

  function SetRetries ($count) {
    $this->m_MaxRetries = $count;
    }
    
  function GetRetries () {
    return ($this->m_MaxRetries);
    }
    
  function SetTimeout ($value) {
    $this->m_Timeout = $value;
    }
    
  function GetTimeout () {
    return ($this->m_Timeout);
    }
    
  function GetLastError () {
      return ($this->m_LastErr);
  }        

  function Clear () {
    $this->m_ReqMethod = "GET";
    $this->m_ReqURL = "/";
    $this->m_ReqProtocol = "HTTP/1.0";
    $this->m_ReqHeaders = array();
    $this->m_ReqEntity = "";
    $this->m_RspProtocol = "";
    $this->m_RspStatus = 0;
    $this->m_RspMessage = "";
    $this->m_RspHeaders = array();
    $this->m_RspEntity = "";
    $this->m_Timeout = 0;
    }

  function HTTP () {
    $this->Clear();
    $this->m_MaxRetries = 5;
    }

  }

?>


-- 
Edit bug report at http://bugs.php.net/?id=25440&edit=1
-- 
Try a CVS snapshot (php4):  http://bugs.php.net/fix.php?id=25440&r=trysnapshot4
Try a CVS snapshot (php5):  http://bugs.php.net/fix.php?id=25440&r=trysnapshot5
Fixed in CVS:               http://bugs.php.net/fix.php?id=25440&r=fixedcvs
Fixed in release:           http://bugs.php.net/fix.php?id=25440&r=alreadyfixed
Need backtrace:             http://bugs.php.net/fix.php?id=25440&r=needtrace
Try newer version:          http://bugs.php.net/fix.php?id=25440&r=oldversion
Not developer issue:        http://bugs.php.net/fix.php?id=25440&r=support
Expected behavior:          http://bugs.php.net/fix.php?id=25440&r=notwrong
Not enough info:            http://bugs.php.net/fix.php?id=25440&r=notenoughinfo
Submitted twice:            http://bugs.php.net/fix.php?id=25440&r=submittedtwice
register_globals:           http://bugs.php.net/fix.php?id=25440&r=globals
PHP 3 support discontinued: http://bugs.php.net/fix.php?id=25440&r=php3
Daylight Savings:           http://bugs.php.net/fix.php?id=25440&r=dst
IIS Stability:              http://bugs.php.net/fix.php?id=25440&r=isapi
Install GNU Sed:            http://bugs.php.net/fix.php?id=25440&r=gnused

Reply via email to