[PEAR-BUG] Bug #14469 [Opn->Fbk]: POST Multipart Content Not Included in Request Object

[email protected]
Newsgroups php.pear.bugs
Message-ID <[email protected]>
Edit report at http://pear.php.net/bugs/bug.php?id=14469&edit=1

 ID:               14469
 Updated by:       [email protected]
 Reported By:      andy dot christianson dot 2 at gmail dot com
 Summary:          POST Multipart Content Not Included in Request Object
-Status:           Open
+Status:           Feedback
 Type:             Bug
 Package:          HTTP_Server
 Operating System: Mac OSX 10.5.4
 Package Version:  0.4.0
 PHP Version:      5.2.6
 Roadmap Versions: 
 New Comment:

-Status: Open
+Status: Feedback
Andy/Oleg; can I get either of you to do the updated patches as actual
diffs? It's a 
bit tricky for me to read them otherwise.


Previous Comments:
------------------------------------------------------------------------

[2008-11-14 12:02:31] mrsol

Here is variant under which stright not it is necessary to use the
function Driver reading from socket. 
Plus is added restriction of the request.
Single that here else it is necessary to correct here is this bug
http://pear.php.net/bugs/bug.php?id=15030

-----------------------
		var $ClientsInfo = array();
		var $MaximalLenRequest = 51200;
		var $readBuferDataSize = 256;
		
	 	function __construct($hostname, $port, $driver = 'Fork'){
		        $this->_driver = &Net_Server::create($driver, $hostname,
$port);
		        $this->_driver->readEndCharacter  = "\n";
		        $this->_driver->readBufferSize = 1;
        
		        $this->_driver->setCallbackObject($this);
    }
    
		function onReceiveData($clientId, $data){
        
				if(empty($this->ClientsInfo[$clientId])){
        		$this->ClientsInfo[$clientId]['data'] = $data;
        		$this->ClientsInfo[$clientId]['end'] = false;
        		$this->ClientsInfo[$clientId]['last_content'] = 0;
        		$this->ClientsInfo[$clientId]['found_content_len'] = false;
        		$this->ClientsInfo[$clientId]['full_len'] = strlen($data);
        		$this->ClientsInfo[$clientId]['content_len'] = NULL;
        }else{
        		$this->ClientsInfo[$clientId]['data'] .= $data;
        		if($this->ClientsInfo[$clientId]['content_len']===NULL and
strpos($data, 'Content-Length')!==false){
        			if(preg_match("'([^: ]+): (.+)'", $data, $regs)){
        				$this->ClientsInfo[$clientId]['last_content'] = $regs[2]; 
        				$this->ClientsInfo[$clientId]['content_len'] = $regs[2];
        			}
        		}
        		if($this->ClientsInfo[$clientId]['found_content_len']){
        			$this->ClientsInfo[$clientId]['last_content'] -=
strlen($data);
       
			if($this->ClientsInfo[$clientId]['last_content']>$this->readBuferDataSize){
        				$this->_driver->readBufferSize = $this->readBuferDataSize; 
        			}else{
        				$this->_driver->readBufferSize =
$this->ClientsInfo[$clientId]['last_content'];
        			}
        		}
        		if($this->ClientsInfo[$clientId]['found_content_len']!=true
and $data=="\r\n"){
        			$this->ClientsInfo[$clientId]['found_content_len'] = true;
        			$this->_driver->setEndCharacter(null);
        		}
        		$this->ClientsInfo[$clientId]['last_data'] = $data;
        		$this->ClientsInfo[$clientId]['full_len'] += strlen($data);
        		
        }
        		
        		
        if($this->ClientsInfo[$clientId]['last_content']<=0 and
$this->ClientsInfo[$clientId]['found_content_len']){
        	$this->ClientsInfo[$clientId]['end'] = true;
        }
        		
        if($this->ClientsInfo[$clientId]['end']){
        	$request =
&HTTP_Server_Request::parse($this->ClientsInfo[$clientId]['data']);
        	if($request === false){
            $response = $this->handleBadRequest($clientId,
$this->ClientsInfo[$clientId]['data']);
            $this->_sendResponse($clientId, $response);
        	}else{
        		$this->_serveRequest($clientId, $request);
        	}
        }else{
        	  
if($this->ClientsInfo[$clientId]['full_len']>$this->MaximalLenRequest){
        		$response = $this->handleBadRequestCode($clientId,
$this->ClientsInfo[$clientId]['data'], 413);
        	}else{
        		return true;
        	}
        }
        
        // close the connection
        $this->_driver->setEndCharacter("\n");
    		$this->_driver->readBufferSize = 1;
    		unset($ClientsInfo[$clientId]);
    		$this->_driver->closeConnection($clientId);
    }
    
		function handleBadRequestCode($clientId, $data, $Code){
		        return array(
                        'code' => $Code
                    );
    }

------------------------------------------------------------------------

[2008-08-12 13:31:11] achristi

...which also means that a few redundant lines can be removed. New 
version (mind the line wrapping):

  function onReceiveData($clientId, $data)
  {
    // read until double \r\n (end of HTTP headers)
    while(false === strpos($data,"\r\n\r\n"))
    {
      $data .= $this->_driver->readFromSocket($clientId);
    }

    // parse request headers
    $request = &HTTP_Server_Request::parse($data);

    if(is_array($request->headers)
        && array_key_exists('content-type',$request->headers)
        && preg_match('/^multipart\/form-
data.*boundary=(\S+).*$/ms',
          $request->headers['content-type'], $matches))
    { 
      $boundary = "--".$matches[1]."--\r\n";

      while(false === strpos($request->content,$boundary))
      {
        $request->content .= $this->_driver-
>readFromSocket($clientId);
      }
    }

    if ($request === false)
    {
      $response = $this->handleBadRequest($clientId, $data);
      $this->_sendResponse($clientId, $response);
    }
    else
    {
      $this->_serveRequest($clientId, $request);
    }

    // close the connection
    $this->_driver->closeConnection($clientId);
  }

------------------------------------------------------------------------

[2008-08-12 13:27:30] achristi

I left out that the code above also relies on the following line in 
HTTP_Server::__construct():

    $this->_driver->readEndCharacter = "\n";

------------------------------------------------------------------------

[2008-08-12 13:26:01] achristi

Here's a very rough test case:

class MyServer extends HTTP_Server
{
    public function POST($clientId, $request)
    {
         var_dump($request->content);
         // expected output: string containing full POST message sent 
from browser
         // actual output: string containing a small amount of
whitespace 
(\r\n?) 
    }
}

Also, here's a thoroughly-tested onReceiveData. I ran into some 
problems with IE/Firefox on a Windows host. The following code works 
perfectly, as far as I can tell, with any combination of Unix/Windows 
hosts and Firefox/Safari/IE clients. The catch here is that this handles

multipart/form-data rather than any POST request. I apologize that it 
is not formatted according to PEAR standards. I haven't got enough 
time to do the formatting and create a proper patch at the moment. 
Hopefully this will help someone and/or make its way into 
HTTP_Server:

  function onReceiveData($clientId, $data)
  {
    // read until double \r\n (end of HTTP headers)
    while(false === strpos($data,"\r\n\r\n"))
    {
      $data .= $this->_driver->readFromSocket($clientId);
    }

    // parse request headers
    $request = &HTTP_Server_Request::parse($data);

    if(is_array($request->headers)
        && array_key_exists('content-type',$request->headers)
        && preg_match('/^multipart\/form-
data.*boundary=(\S+).*$/ms',
          $request->headers['content-type'], $matches))
    {
      $boundary = "--".$matches[1]."--\r\n";
      $originalReadEndCharacter = $this->_driver->readEndCharacter;
      $this->_driver->readEndCharacter = "\n";

      while(false === strpos($request->content,$boundary))
      {
        $request->content .= $this->_driver-
>readFromSocket($clientId);
      }

      $this->_driver->readEndCharacter = $originalReadEndCharacter;
    }

    if ($request === false)
    {
      $response = $this->handleBadRequest($clientId, $data);
      $this->_sendResponse($clientId, $response);
    }
    else
    {
      $this->_serveRequest($clientId, $request);
    }

    // close the connection
    $this->_driver->closeConnection($clientId);
  }

------------------------------------------------------------------------

[2008-08-08 23:05:12] doconnor

Hey thanks for the patch Andy!

I don't suppose you'd feel up to a very small test case to better
demonstrate this? That makes it easier for people who don't know the
package well to evaluate your patch...

------------------------------------------------------------------------

The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at
    http://pear.php.net/bugs/bug.php?id=14469

-- 
Edit this bug report at http://pear.php.net/bugs/bug.php?id=14469&edit=1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.