Package home | Report new bug | New search | Development Roadmap Status: Open | Feedback | All | Closed Since Version 2.2.1

Bug #20125 HTTP_Request2_Adapter_Socket class doesn't handle chunked data
Submitted: 2013-11-11 19:55 UTC Modified: 2014-01-11 17:26 UTC
From: rubberman Assigned: avb
Status: Closed Package: HTTP_Request2 (version 2.1.1)
PHP Version: 5.5.3 OS: RHEL 6.x
Roadmaps: (Not assigned)    
Subscription  


 [2013-11-11 19:55 UTC] rubberman (William Boyle)
Description: ------------ The HTTP_Request2_Adapter_Socket::writeBody() method ignores chunked transfer-encoding. The protocol requires a leading hex representation of the chunk length + a CRLF sequence, a trailing CRLF after the chunk, and after the last chunk, a 0 length chunk must be added to the stream. In addition, in the HTTP_Request2_Adapter class, the calculateRequestLength() method needs to be modified to NOT set the 'content-length' header when the transfer-encoding is 'chunked'. They are mutually-exclusive headers. I am attaching modified versions of Request2/Adapter.cpp and Request2/Adapter/Socket.php that resolve these issues. The code changes are bracketed with // TTT:... comments so the changes are easy to find.

Comments

 [2013-11-11 20:04 UTC] rubberman (William Boyle)
I could not attach a source file so here is the HTTP_Request2_Adapter_Socket::writeBody() method as changed: protected function writeBody() { if (in_array($this->request->getMethod(), self::$bodyDisallowed) || 0 == $this->contentLength ) { return; } $position = 0; $bufferSize = $this->request->getConfig('buffer_size'); // TTT: deal with chunked data. $chunked = $this->request->getHeaders()['transfer-encoding']; $ischunked = (!is_null($chunked) && ($chunked == 'chunked')); if ($ischunked) { $buffersize = 671; } while ($position < $this->contentLength) { $tosend = null; if (is_string($this->requestBody)) { $str = substr($this->requestBody, $position, $bufferSize); } elseif (is_resource($this->requestBody)) { $str = fread($this->requestBody, $bufferSize); } else { $str = $this->requestBody->read($bufferSize); } if ($ischunked) { $sndlen = min($buffersize, strlen($str)); $hexval = dechex($sndlen); $tosend = $hexval . rawurldecode('%0d%0a') . substr($str, 0, $sndlen) . rawurldecode('%0d%0a'); } else { $tosend = $str; $sndlen = strlen($str); } $this->socket->write($tosend); // Provide the length of written string to the observer, request #7630 $this->request->setLastEvent('sentBodyPart', $sndlen); $position += $sndlen; } if ($ischunked) { $this->socket->write( '0' . rawurldecode('%0d%0a%0d%0a')); } // TTT: end chunked output changes. $this->request->setLastEvent('sentBody', $this->contentLength); }
 [2013-11-11 20:06 UTC] rubberman (William Boyle)
Here is the modified version HTTP_Request2_Adapter::calculateRequestLength(): protected function calculateRequestLength(&$headers) { $this->requestBody = $this->request->getBody(); if (is_string($this->requestBody)) { $this->contentLength = strlen($this->requestBody); } elseif (is_resource($this->requestBody)) { $stat = fstat($this->requestBody); $this->contentLength = $stat['size']; rewind($this->requestBody); } else { $this->contentLength = $this->requestBody->getLength(); $headers['content-type'] = 'multipart/form-data; boundary=' . $this->requestBody->getBoundary(); $this->requestBody->rewind(); } if (in_array($this->request->getMethod(), self::$bodyDisallowed) || 0 == $this->contentLength ) { // No body: send a Content-Length header nonetheless (request #12900), // but do that only for methods that require a body (bug #14740) if (in_array($this->request->getMethod(), self::$bodyRequired)) { $headers['content-length'] = 0; } else { unset($headers['content-length']); // if the method doesn't require a body and doesn't have a // body, don't send a Content-Type header. (request #16799) unset($headers['content-type']); } } else { if (empty($headers['content-type'])) { $headers['content-type'] = 'application/x-www-form-urlencoded'; } // TTT: don't set content-length if the encoding is 'chunked'. if (empty($headers['transfer-encoding']) || ($headers['transfer-encoding'] != 'chunked')) { $headers['content-length'] = $this->contentLength; } // TTT: end setting content length. } }
 [2014-01-11 17:26 UTC] avb (Alexey Borzov)
-Status: Open +Status: Closed -Assigned To: +Assigned To: avb
Fixed in Git. https://github.com/pear/HTTP_Request2/commit/a3225c368380eb4c75bb672af64dd06280895a20 Your writeBody() is most probably broken due to "$buffersize = 671" and related logic, BTW.