Parser concept, fixes:

A new concept Parser is introduced with routines to read from a stream
into the parser. This solves a problem with the old read interface where
messages must be default constructible and move assignable.

Parser fixes:

* Fix detect invalid reason-phrase octets
* Fix write_eof to set the 'complete' state on success
* Fix consider parse complete if eof received on empty body

WebSocket:

* Increase coverage
This commit is contained in:
Vinnie Falco
2016-05-12 19:20:57 -04:00
parent 8921da91b8
commit 2a8de0fd6b
28 changed files with 1536 additions and 701 deletions
+74 -36
View File
@@ -9,6 +9,7 @@
#define BEAST_HTTP_IMPL_BASIC_PARSER_V1_IPP
#include <beast/core/buffer_concepts.hpp>
#include <cassert>
namespace beast {
namespace http {
@@ -88,6 +89,11 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
s_ = s_closed;
return used();
};
auto errc = [&]
{
s_ = s_closed;
return used();
};
auto piece = [&]
{
return boost::string_ref{
@@ -113,6 +119,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
switch(s_)
{
case s_closed:
case s_closed_complete:
return err(parse_error::connection_closed);
break;
@@ -126,6 +133,9 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
case s_req_method_start:
if(! is_token(ch))
return err(parse_error::bad_method);
call_on_start(ec);
if(ec)
return errc();
cb_ = &self::call_on_method;
s_ = s_req_method;
break;
@@ -134,7 +144,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
if(! is_token(ch))
{
if(cb(nullptr))
return used();
return errc();
s_ = s_req_space_before_url;
goto redo;
}
@@ -147,21 +157,23 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
break;
case s_req_url_start:
{
if(ch == ' ')
return err(parse_error::bad_uri);
// VFALCO TODO Better checking for valid URL characters
if(! is_text(ch))
return err(parse_error::bad_uri);
if(cb(&self::call_on_uri))
return used();
assert(! cb_);
cb(&self::call_on_uri);
s_ = s_req_url;
break;
}
case s_req_url:
if(ch == ' ')
{
if(cb(nullptr))
return used();
return errc();
s_ = s_req_http_start;
break;
}
@@ -245,7 +257,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
return err(parse_error::bad_crlf);
call_on_request(ec);
if(ec)
return used();
return errc();
s_ = s_header_field_start;
break;
@@ -257,7 +269,14 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
content_length_ = no_content_length;
switch(ch)
{
case 'H': s_ = s_res_H; break;
case 'H':
call_on_start(ec);
if(ec)
return errc();
s_ = s_res_H;
break;
// VFALCO NOTE this allows whitespace at the beginning,
// need to check rfc7230
case '\r':
case '\n':
break;
@@ -365,13 +384,16 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
s_ = s_res_line_almost_done;
break;
}
// VFALCO Is this up to spec?
if(ch == '\n')
{
s_ = s_header_field_start;
break;
}
if(! is_text(ch))
return err(parse_error::bad_status);
if(cb(&self::call_on_reason))
return used();
return errc();
pos_ = 0;
s_ = s_res_status;
break;
@@ -380,17 +402,19 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
if(ch == '\r')
{
if(cb(nullptr))
return used();
return errc();
s_ = s_res_line_almost_done;
break;
}
if(ch == '\n')
{
if(cb(nullptr))
return used();
return errc();
s_ = s_header_field_start;
break;
}
if(! is_text(ch))
return err(parse_error::bad_status);
break;
case s_res_line_almost_done:
@@ -402,7 +426,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
case s_res_line_done:
call_on_response(ec);
if(ec)
return used();
return errc();
s_ = s_header_field_start;
goto redo;
@@ -431,8 +455,8 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
fs_ = h_general;
break;
}
if(cb(&self::call_on_field))
return used();
assert(! cb_);
cb(&self::call_on_field);
s_ = s_header_field;
break;
}
@@ -529,7 +553,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
if(ch == ':')
{
if(cb(nullptr))
return used();
return errc();
s_ = s_header_value_start;
break;
}
@@ -579,7 +603,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
}
call_on_value(ec, boost::string_ref{"", 0});
if(ec)
return used();
return errc();
s_ = s_header_field_start;
goto redo;
@@ -629,7 +653,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
}
pos_ = 0;
if(cb(&self::call_on_value))
return used();
return errc();
s_ = s_header_value_text;
break;
}
@@ -641,7 +665,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
if(ch == '\r')
{
if(cb(nullptr))
return used();
return errc();
s_ = s_header_value_discard_lWs;
break;
}
@@ -775,9 +799,9 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
return err(parse_error::bad_value);
call_on_value(ec, boost::string_ref(" ", 1));
if(ec)
return used();
return errc();
if(cb(&self::call_on_value))
return used();
return errc();
s_ = s_header_value_text;
break;
@@ -811,7 +835,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
return err(parse_error::bad_crlf);
if(flags_ & parse_flag::trailing)
{
//if(cb(&self::call_on_chunk_complete)) return used();
//if(cb(&self::call_on_chunk_complete)) return errc();
s_ = s_complete;
goto redo;
}
@@ -821,7 +845,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
(parse_flag::upgrade | parse_flag::connection_upgrade)) /*|| method == "connect"*/;
auto const maybe_skip = call_on_headers(ec);
if(ec)
return used();
return errc();
switch(maybe_skip)
{
case 0: break;
@@ -839,7 +863,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
assert(! cb_);
call_on_headers(ec);
if(ec)
return used();
return errc();
bool const hasBody =
(flags_ & parse_flag::chunked) || (content_length_ > 0 &&
content_length_ != no_content_length);
@@ -878,8 +902,8 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
}
case s_body_identity0:
if(cb(&self::call_on_body))
return used();
assert(! cb_);
cb(&self::call_on_body);
s_ = s_body_identity;
goto redo; // VFALCO fall through?
@@ -903,8 +927,8 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
}
case s_body_identity_eof0:
if(cb(&self::call_on_body))
return used();
assert(! cb_);
cb(&self::call_on_body);
s_ = s_body_identity_eof;
goto redo; // VFALCO fall through?
@@ -963,13 +987,13 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
s_ = s_header_field_start;
break;
}
//call_chunk_header(ec); if(ec) return used();
//call_chunk_header(ec); if(ec) return errc();
s_ = s_chunk_data_start;
break;
case s_chunk_data_start:
if(cb(&self::call_on_body))
return used();
assert(! cb_);
cb(&self::call_on_body);
s_ = s_chunk_data;
goto redo; // VFALCO fall through?
@@ -991,7 +1015,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
if(ch != '\r')
return err(parse_error::bad_crlf);
if(cb(nullptr))
return used();
return errc();
s_ = s_chunk_data_done;
break;
@@ -1005,10 +1029,10 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
case s_complete:
++p;
if(cb(nullptr))
return used();
return errc();
call_on_complete(ec);
if(ec)
return used();
return errc();
s_ = s_restart;
return used();
@@ -1024,7 +1048,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
{
(this->*cb_)(ec, piece());
if(ec)
return used();
return errc();
}
return used();
}
@@ -1036,17 +1060,31 @@ write_eof(error_code& ec)
{
switch(s_)
{
case s_restart:
s_ = s_closed_complete;
break;
case s_closed:
case s_closed_complete:
break;
case s_body_identity_eof0:
case s_body_identity_eof:
cb_ = nullptr;
call_on_complete(ec);
if(ec)
return;
return;
{
s_ = s_closed;
break;
}
s_ = s_closed_complete;
break;
default:
s_ = s_closed;
ec = parse_error::short_read;
break;
}
ec = parse_error::short_read;
s_ = s_closed;
}
template<bool isRequest, class Derived>