Skip to content

Commit

Permalink
Fixes issue AndreLouisCaron#5 (off-by-one error in 'feed()').
Browse files Browse the repository at this point in the history
'http_parser_execute()' does not mark the last byte of headers as consumed
after calling the 'on_headers_complete()' callback if 'http_parser_pause()'
is called from the callback.

This fix forces the parser to consume the extra byte by unpausing, then calling
'http_parser_execute()' again with the last byte.  Note that this byte is
always available since it had to be there to trigger the callback that pauses
the parser.
  • Loading branch information
AndreLouisCaron committed Feb 28, 2012
1 parent dd7161f commit fbd0422
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 18 deletions.
47 changes: 32 additions & 15 deletions code/Message.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include <cstring>
#include <utility>

#include <iostream>

namespace {

struct Clear {
Expand Down Expand Up @@ -146,24 +148,39 @@ namespace http {

std::size_t Message::feed ( const char * data, ::size_t size )
{
const std::size_t pass =
::http_parser_execute(&myParser, &mySettings, data, size);
if (pass != size)
std::size_t used = ::http_parser_execute
(&myParser, &mySettings, data, size);

const ::http_errno error =
static_cast< ::http_errno >(myParser.http_errno);

// The 'on_message_complete' and 'on_headers_complete' callbacks fail
// on purpose to force the parser to stop between pipelined requests.
// This allows the clients to reliably detect the end of headers and
// the end of the message. Make sure the parser is always unpaused
// for the next call to 'feed'.
if (error == HPE_PAUSED) {
::http_parser_pause(&myParser, 0);
}

if (used < size)
{
const ::http_errno error =
static_cast< ::http_errno >(myParser.http_errno);

// The 'on_message_complete' callback fails on purpose.
// It forces the parser to stop between pipelined
// requests so clients can test the '.complete()' flag.
if (error == HPE_PAUSED) {
::http_parser_pause(&myParser, 0);
return (pass);
if (error == HPE_PAUSED)
{
// Make sure the byte that triggered the pause
// after the headers is properly processed.
if (!myComplete)
{
used += ::http_parser_execute
(&myParser, &mySettings, data+used, 1);
}
}
else {
throw (Error(error));
}

throw (Error(error));
}
return (pass);

return (used);
}

bool Message::complete () const
Expand Down
5 changes: 5 additions & 0 deletions test/data/request-002.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
POST /index.html HTTP/1.1
Host: www.example.com
Content-Length: 13

Hello, world!
27 changes: 24 additions & 3 deletions test/http-extract-request-header.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,35 @@ namespace {
// Parse HTTP request.
http::Request request;
char data[1024];
std::size_t head = 0;
std::size_t body = 0;
do {
stream.read(data, sizeof(data));
std::size_t size = stream.gcount();
std::size_t used = 0;
std::size_t pass = 0;
while ((used < size) && !request.complete()) {
used += request.feed(data+used, size-used);
while ((used < size) && !request.headers_complete())
{
const std::size_t pass = request.feed(data+used, size-used);
used += pass, head += pass;
if (request.headers_complete())
{
std::cerr
<< "Head size: " << head << "."
<< std::endl;
}
}
while ((used < size) && !request.complete())
{
const std::size_t pass = request.feed(data+used, size-used);
used += pass, body += pass;
if (request.complete())
{
std::cerr
<< "Body size: " << body << "."
<< std::endl;
}
}
head += used;
}
while ((stream.gcount() > 0) && !request.complete());

Expand Down

0 comments on commit fbd0422

Please sign in to comment.