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.