Fixes, fail testing:

Core:

* Test buffer_cat iterator move members

HTTP:

* Fixed yield / resume in writer
* Fixed message serialization with chunked encoding

* Test yield / resume in writer
* Test all conditional branches during message serialization
* Test chunked encoding
* Increase coverage on parse_error
* Add parse_error::general

WebSocket:

* Add error::general
* Increase coverage in error
This commit is contained in:
Vinnie Falco
2017-07-20 08:12:06 -07:00
parent c49cde844b
commit b62d1b2164
44 changed files with 1064 additions and 688 deletions
-2
View File
@@ -197,7 +197,6 @@ private:
void
move(C<sizeof...(Bs)>, const_iterator&&)
{
return;
}
template<std::size_t I>
@@ -216,7 +215,6 @@ private:
void
copy(C<sizeof...(Bs)>, const_iterator const&)
{
return;
}
template<std::size_t I>
@@ -110,7 +110,7 @@ read_some_op<MutableBufferSequence, Handler>::operator()(
if(d.srs.sb_.size() == 0)
{
d.state =
d.srs.size_ > 0 ? 2 : 1;
d.srs.capacity_ > 0 ? 2 : 1;
break;
}
d.state = 4;
@@ -129,7 +129,7 @@ read_some_op<MutableBufferSequence, Handler>::operator()(
// read
d.state = 3;
d.srs.next_layer_.async_read_some(
d.srs.sb_.prepare(d.srs.size_),
d.srs.sb_.prepare(d.srs.capacity_),
std::move(*this));
return;
@@ -217,14 +217,12 @@ read_some(MutableBufferSequence const& buffers,
"MutableBufferSequence requirements not met");
using boost::asio::buffer_size;
using boost::asio::buffer_copy;
if(buffer_size(buffers) == 0)
return 0;
if(size_ == 0)
return next_layer_.read_some(buffers, ec);
if(sb_.size() == 0)
{
if(capacity_ == 0)
return next_layer_.read_some(buffers, ec);
sb_.commit(next_layer_.read_some(
sb_.prepare(size_), ec));
sb_.prepare(capacity_), ec));
if(ec)
return 0;
}
+6 -18
View File
@@ -1,21 +1,9 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_WEBSOCKET_STATIC_STRING_HPP
#define BEAST_WEBSOCKET_STATIC_STRING_HPP
+3 -3
View File
@@ -99,7 +99,7 @@ class streambuf_readstream
class read_some_op;
Streambuf sb_;
std::size_t size_ = 0;
std::size_t capacity_ = 0;
Stream next_layer_;
public:
@@ -211,9 +211,9 @@ public:
than the amount of data in the buffer, no bytes are discarded.
*/
void
reserve(std::size_t size)
capacity(std::size_t size)
{
size_ = size;
capacity_ = size;
}
/// Write the given data to the stream. Returns the number of bytes written.
+57 -205
View File
@@ -8,7 +8,9 @@
#ifndef BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP
#define BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP
#include <beast/core/buffer_cat.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/logic/tribool.hpp>
#include <algorithm>
#include <array>
#include <cassert>
@@ -20,244 +22,94 @@ namespace beast {
namespace http {
namespace detail {
template <class Buffers>
class chunk_encoded_buffers
class chunk_encode_text
{
private:
using const_buffer = boost::asio::const_buffer;
Buffers buffers_;
const_buffer head_;
const_buffer tail_;
boost::asio::const_buffer cb_;
// Storage for the longest hex string we might need, plus delimiters.
std::array<char, 2 * sizeof(std::size_t) + 2> data_;
std::array<char, 2 * sizeof(std::size_t) + 2> buf_;
template <class OutIter>
static
OutIter
to_hex(OutIter last, std::size_t n)
{
if(n == 0)
{
*--last = '0';
return last;
}
while(n)
{
*--last = "0123456789abcdef"[n&0xf];
n>>=4;
}
return last;
}
public:
using value_type = boost::asio::const_buffer;
class const_iterator;
using const_iterator = value_type const*;
chunk_encoded_buffers() = delete;
chunk_encoded_buffers (chunk_encoded_buffers const&) = default;
chunk_encoded_buffers& operator= (chunk_encoded_buffers const&) = default;
chunk_encode_text(chunk_encode_text const& other)
{
auto const n =
boost::asio::buffer_size(other.cb_);
buf_ = other.buf_;
cb_ = boost::asio::const_buffer(
&buf_[buf_.size() - n], n);
}
chunk_encoded_buffers (Buffers const& buffers, bool final_chunk);
explicit
chunk_encode_text(std::size_t n)
{
buf_[buf_.size() - 2] = '\r';
buf_[buf_.size() - 1] = '\n';
auto it = to_hex(buf_.end() - 2, n);
cb_ = boost::asio::const_buffer{&*it,
static_cast<std::size_t>(
std::distance(it, buf_.end()))};
}
const_iterator
begin() const
{
return const_iterator(*this, false);
return &cb_;
}
const_iterator
end() const
{
return const_iterator(*this, true);
return begin() + 1;
}
private:
// Unchecked conversion of unsigned to hex string
template<class OutIter, class Unsigned>
static
typename std::enable_if<
std::is_unsigned<Unsigned>::value, OutIter>::type
to_hex(OutIter const first, OutIter const last, Unsigned n);
};
template <class Buffers>
class chunk_encoded_buffers<Buffers>::const_iterator
: public std::iterator<std::bidirectional_iterator_tag, const_buffer>
{
private:
using iterator = typename Buffers::const_iterator;
enum class Where { head, input, end };
chunk_encoded_buffers const* buffers_;
Where where_;
iterator iter_;
/** Returns a chunk-encoded ConstBufferSequence.
public:
const_iterator();
const_iterator (const_iterator const&) = default;
const_iterator& operator= (const_iterator const&) = default;
bool operator== (const_iterator const& other) const;
bool operator!= (const_iterator const& other) const;
const_iterator& operator++();
const_iterator& operator--();
const_iterator operator++(int) const;
const_iterator operator--(int) const;
const_buffer operator*() const;
private:
friend class chunk_encoded_buffers;
const_iterator(chunk_encoded_buffers const& buffers, bool past_the_end);
};
//------------------------------------------------------------------------------
template <class Buffers>
chunk_encoded_buffers<Buffers>::chunk_encoded_buffers (
Buffers const& buffers, bool final_chunk)
: buffers_(buffers)
{
auto const size = boost::asio::buffer_size(buffers);
data_[data_.size() - 2] = '\r';
data_[data_.size() - 1] = '\n';
auto pos = to_hex(data_.begin(), data_.end() - 2, size);
head_ = const_buffer(&*pos,
std::distance(pos, data_.end()));
if (size > 0 && final_chunk)
tail_ = const_buffer("\r\n0\r\n\r\n", 7);
else
tail_ = const_buffer("\r\n", 2);
}
template <class Buffers>
template <class OutIter, class Unsigned>
typename std::enable_if<
std::is_unsigned<Unsigned>::value, OutIter>::type
chunk_encoded_buffers<Buffers>::to_hex(
OutIter const first, OutIter const last, Unsigned n)
{
assert(first != last);
OutIter iter = last;
if(n == 0)
{
*--iter = '0';
return iter;
}
while(n)
{
assert(iter != first);
*--iter = "0123456789abcdef"[n&0xf];
n>>=4;
}
return iter;
}
template <class Buffers>
chunk_encoded_buffers<Buffers>::const_iterator::const_iterator()
: buffers_(nullptr)
, where_(Where::end)
{
}
template <class Buffers>
bool
chunk_encoded_buffers<Buffers>::const_iterator::operator==(
const_iterator const& other) const
{
return buffers_ == other.buffers_ &&
where_ == other.where_ && iter_ == other.iter_;
}
template <class Buffers>
bool
chunk_encoded_buffers<Buffers>::const_iterator::operator!=(
const_iterator const& other) const
{
return buffers_ != other.buffers_ ||
where_ != other.where_ || iter_ != other.iter_;
}
template <class Buffers>
auto
chunk_encoded_buffers<Buffers>::const_iterator::operator++() ->
const_iterator&
{
assert(buffers_);
assert(where_ != Where::end);
if (where_ == Where::head)
where_ = Where::input;
else if (iter_ != buffers_->buffers_.end())
++iter_;
else
where_ = Where::end;
return *this;
}
template <class Buffers>
auto
chunk_encoded_buffers<Buffers>::const_iterator::operator--() ->
const_iterator&
{
assert(buffers_);
assert(where_ != Where::head);
if (where_ == Where::end)
where_ = Where::input;
else if (iter_ != buffers_->buffers_.begin())
--iter_;
else
where_ = Where::head;
return *this;
}
template <class Buffers>
auto
chunk_encoded_buffers<Buffers>::const_iterator::operator++(int) const ->
const_iterator
{
auto iter = *this;
++iter;
return iter;
}
template <class Buffers>
auto
chunk_encoded_buffers<Buffers>::const_iterator::operator--(int) const ->
const_iterator
{
auto iter = *this;
--iter;
return iter;
}
template <class Buffers>
auto
chunk_encoded_buffers<Buffers>::const_iterator::operator*() const ->
const_buffer
{
assert(buffers_);
assert(where_ != Where::end);
if (where_ == Where::head)
return buffers_->head_;
if (iter_ != buffers_->buffers_.end())
return *iter_;
return buffers_->tail_;
}
template <class Buffers>
chunk_encoded_buffers<Buffers>::const_iterator::const_iterator(
chunk_encoded_buffers const& buffers, bool past_the_end)
: buffers_(&buffers)
, where_(past_the_end ? Where::end : Where::head)
, iter_(past_the_end ? buffers_->buffers_.end() :
buffers_->buffers_.begin())
{
}
/* Returns a chunk-encoded BufferSequence.
See:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1
This returns a buffer sequence representing the
first chunk of a chunked transfer coded body.
@param buffers The input buffer sequence.
@param final_chunk `true` If this should include a final-chunk.
@return A chunk-encoded ConstBufferSequence representing the input.
@see <a href=https://tools.ietf.org/html/rfc7230#section-4.1.3>rfc7230 section 4.1.3</a>
*/
template<class ConstBufferSequence>
#if GENERATING_DOCS
implementation_defined
#else
detail::chunk_encoded_buffers<ConstBufferSequence>
beast::detail::buffer_cat_helper<boost::asio::const_buffer,
chunk_encode_text, ConstBufferSequence, boost::asio::const_buffers_1>
#endif
chunk_encode(ConstBufferSequence const& buffers,
bool final_chunk = false)
chunk_encode(ConstBufferSequence const& buffers)
{
return detail::chunk_encoded_buffers<
ConstBufferSequence>{buffers, final_chunk};
using boost::asio::buffer_size;
return buffer_cat(
chunk_encode_text{buffer_size(buffers)},
buffers,
boost::asio::const_buffers_1{"\r\n", 2});
}
/// Returns a chunked encoding final chunk.
+37 -39
View File
@@ -148,13 +148,13 @@ class write_op
}
};
class writef0
class writef0_lambda
{
write_op& self_;
public:
explicit
writef0(write_op& self)
writef0_lambda(write_op& self)
: self_(self)
{
}
@@ -176,13 +176,13 @@ class write_op
}
};
class writef
class writef_lambda
{
write_op& self_;
public:
explicit
writef(write_op& self)
writef_lambda(write_op& self)
: self_(self)
{
}
@@ -300,7 +300,7 @@ operator()(error_code ec, std::size_t, bool again)
case 1:
{
auto const result = d.wp.w(
std::move(d.copy), ec, writef0{*this});
std::move(d.copy), ec, writef0_lambda{*this});
if(ec)
{
// call handler
@@ -331,7 +331,7 @@ operator()(error_code ec, std::size_t, bool again)
case 3:
{
auto const result = d.wp.w(
std::move(d.copy), ec, writef{*this});
std::move(d.copy), ec, writef_lambda{*this});
if(ec)
{
// call handler
@@ -378,7 +378,7 @@ operator()(error_code ec, std::size_t, bool again)
}
template<class SyncWriteStream, class Streambuf>
class writef0_write
class writef0_lambda
{
Streambuf const& sb_;
SyncWriteStream& stream_;
@@ -386,7 +386,7 @@ class writef0_write
error_code& ec_;
public:
writef0_write(SyncWriteStream& stream,
writef0_lambda(SyncWriteStream& stream,
Streambuf const& sb, bool chunked, error_code& ec)
: sb_(sb)
, stream_(stream)
@@ -409,14 +409,14 @@ public:
};
template<class SyncWriteStream>
class writef_write
class writef_lambda
{
SyncWriteStream& stream_;
bool chunked_;
error_code& ec_;
public:
writef_write(SyncWriteStream& stream,
writef_lambda(SyncWriteStream& stream,
bool chunked, error_code& ec)
: stream_(stream)
, chunked_(chunked)
@@ -478,45 +478,43 @@ write(SyncWriteStream& stream,
cv.notify_one();
}};
auto copy = resume;
for(;;)
boost::tribool result;
result = wp.w(std::move(copy), ec,
detail::writef0_lambda<SyncWriteStream, decltype(wp.sb)>{
stream, wp.sb, wp.chunked, ec});
if(ec)
return;
if(boost::indeterminate(result))
{
copy = resume;
{
auto result = wp.w(std::move(copy), ec,
detail::writef0_write<SyncWriteStream, decltype(wp.sb)>{
stream, wp.sb, wp.chunked, ec});
if(ec)
return;
if(result)
break;
if(boost::indeterminate(result))
{
boost::asio::write(stream, wp.sb.data(), ec);
if(ec)
return;
wp.sb.consume(wp.sb.size());
copy = resume;
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&]{ return ready; });
ready = false;
}
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&]{ return ready; });
ready = false;
}
wp.sb.consume(wp.sb.size());
boost::asio::write(stream, wp.sb.data(), ec);
if(ec)
return;
result = false;
}
wp.sb.consume(wp.sb.size());
if(! result)
{
for(;;)
{
auto result = wp.w(std::move(copy), ec,
detail::writef_write<SyncWriteStream>{
result = wp.w(std::move(copy), ec,
detail::writef_lambda<SyncWriteStream>{
stream, wp.chunked, ec});
if(ec)
return;
if(result)
break;
if(boost::indeterminate(result))
{
copy = resume;
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&]{ return ready; });
ready = false;
}
if(! result)
continue;
copy = resume;
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&]{ return ready; });
ready = false;
}
}
if(wp.chunked)
+4 -2
View File
@@ -34,7 +34,9 @@ enum class parse_error
invalid_chunk_size,
short_read
short_read,
general
};
class parse_error_category : public boost::system::error_category
@@ -97,7 +99,7 @@ public:
return "unexpected end of data";
default:
return "beast::http::parser error";
return "parse error";
}
}
+6 -18
View File
@@ -1,21 +1,9 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_WEBSOCKET_HPP
#define BEAST_WEBSOCKET_HPP
+4 -1
View File
@@ -44,7 +44,10 @@ enum class error
request_invalid,
/// Upgrade request denied
request_denied
request_denied,
/// General WebSocket error
general
};
#if ! GENERATING_DOCS
+1
View File
@@ -861,6 +861,7 @@ build_response(http::request_v1<Body, Headers> const& req)
res.version = req.version;
res.body = text;
// VFALCO TODO respect keep-alive here
prepare(res);
return res;
};
if(req.version < 11)
+2 -2
View File
@@ -202,7 +202,7 @@ public:
void
set_option(read_buffer_size const& o)
{
stream_.reserve(o.value);
stream_.capacity(o.value);
}
/// Set the maximum incoming message size allowed
@@ -217,7 +217,7 @@ public:
set_option(write_buffer_size const& o)
{
wr_buf_size_ = std::max<std::size_t>(o.value, 1024);
stream_.reserve(o.value);
stream_.capacity(o.value);
}
/** Get the io_service associated with the stream.
+6 -18
View File
@@ -1,21 +1,9 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_WEBSOCKET_TEARDOWN_HPP
#define BEAST_WEBSOCKET_TEARDOWN_HPP