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
2016-05-07 17:06:46 -04:00
parent c49cde844b
commit b62d1b2164
44 changed files with 1064 additions and 688 deletions

View File

@@ -49,10 +49,10 @@ HTTP:
* Fix prepare() calling content_length() without init()
* Use construct,destroy allocator routines in basic_headers
* Complete allocator testing in basic_streambuf, basic_headers
* Fix http::async_write op, case 3 should break at the end.
* Add tests for writer using the resume function / coros
* Custom HTTP error codes for various situations
* Make empty_body write-only, remove reader nested type
* Add concepts WritableBody ReadableBody with type checks,
check them in read and write functions
* Branch prediction hints in parser
* Check basic_parser_v1 against rfc7230 for leading message whitespace

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_EXAMPLE_FILE_BODY_H_INCLUDED
#define BEAST_EXAMPLE_FILE_BODY_H_INCLUDED

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_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
#define BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED

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)
//
#include "http_stream.hpp"
#include "urls_large_data.hpp"

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)
//
#include "http_async_server.hpp"
#include "http_sync_server.hpp"

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_HTTP_STREAM_H_INCLUDED
#define BEAST_HTTP_STREAM_H_INCLUDED

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_HTTP_STREAM_IPP_INCLUDED
#define BEAST_HTTP_STREAM_IPP_INCLUDED

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_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED
#define BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED

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)
//
#include "urls_large_data.hpp"

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 URLS_LARGE_DATA_H_INCLUDED
#define URLS_LARGE_DATA_H_INCLUDED

View File

@@ -0,0 +1,69 @@
//
// 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_TEST_FAIL_COUNTER_HPP
#define BEAST_TEST_FAIL_COUNTER_HPP
#include <beast/core/error.hpp>
namespace beast {
namespace test {
/** A countdown to simulated failure.
On the Nth operation, the class will fail with the specified
error code, or the default error code of invalid_argument.
*/
class fail_counter
{
std::size_t n_;
error_code ec_;
public:
fail_counter(fail_counter&&) = default;
/** Construct a counter.
@param The 0-based index of the operation to fail on or after.
*/
explicit
fail_counter(std::size_t n = 0)
: n_(n)
, ec_(boost::system::errc::make_error_code(
boost::system::errc::errc_t::invalid_argument))
{
}
/// Throw an exception on the Nth failure
void
fail()
{
if(n_ > 0)
--n_;
if(! n_)
throw system_error{ec_};
}
/// Set an error code on the Nth failure
bool
fail(error_code& ec)
{
if(n_ > 0)
--n_;
if(! n_)
{
ec = ec_;
return true;
}
return false;
}
};
} // test
} // beast
#endif

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_TEST_FAIL_STREAM_HPP
#define BEAST_TEST_FAIL_STREAM_HPP
@@ -25,6 +13,7 @@
#include <beast/core/error.hpp>
#include <beast/core/detail/get_lowest_layer.hpp>
#include <beast/websocket/teardown.hpp>
#include <beast/test/fail_counter.hpp>
namespace beast {
namespace test {
@@ -37,32 +26,10 @@ namespace test {
template<class NextLayer>
class fail_stream
{
error_code ec_;
std::size_t n_ = 0;
fail_counter* pfc_;
fail_counter fc_;
NextLayer next_layer_;
void
fail()
{
if(n_ > 0)
--n_;
if(! n_)
throw system_error{ec_};
}
bool
fail(error_code& ec)
{
if(n_ > 0)
--n_;
if(! n_)
{
ec = ec_;
return true;
}
return false;
}
public:
using next_layer_type =
typename std::remove_reference<NextLayer>::type;
@@ -71,15 +38,24 @@ public:
typename beast::detail::get_lowest_layer<
next_layer_type>::type;
fail_stream(fail_stream&&) = default;
fail_stream& operator=(fail_stream&&) = default;
fail_stream(fail_stream&&) = delete;
fail_stream(fail_stream const&) = delete;
fail_stream& operator=(fail_stream&&) = delete;
fail_stream& operator=(fail_stream const&) = delete;
template<class... Args>
explicit
fail_stream(std::size_t n, Args&&... args)
: ec_(boost::system::errc::make_error_code(
boost::system::errc::errc_t::invalid_argument))
, n_(n)
: pfc_(&fc_)
, fc_(n)
, next_layer_(std::forward<Args>(args)...)
{
}
template<class... Args>
explicit
fail_stream(fail_counter& fc, Args&&... args)
: pfc_(&fc)
, next_layer_(std::forward<Args>(args)...)
{
}
@@ -112,7 +88,7 @@ public:
std::size_t
read_some(MutableBufferSequence const& buffers)
{
fail();
pfc_->fail();
return next_layer_.read_some(buffers);
}
@@ -120,7 +96,7 @@ public:
std::size_t
read_some(MutableBufferSequence const& buffers, error_code& ec)
{
if(fail(ec))
if(pfc_->fail(ec))
return 0;
return next_layer_.read_some(buffers, ec);
}
@@ -132,7 +108,7 @@ public:
ReadHandler&& handler)
{
error_code ec;
if(fail(ec))
if(pfc_->fail(ec))
{
async_completion<
ReadHandler, void(error_code, std::size_t)
@@ -149,7 +125,7 @@ public:
std::size_t
write_some(ConstBufferSequence const& buffers)
{
fail();
pfc_->fail();
return next_layer_.write_some(buffers);
}
@@ -157,7 +133,7 @@ public:
std::size_t
write_some(ConstBufferSequence const& buffers, error_code& ec)
{
if(fail(ec))
if(pfc_->fail(ec))
return 0;
return next_layer_.write_some(buffers, ec);
}
@@ -169,7 +145,7 @@ public:
WriteHandler&& handler)
{
error_code ec;
if(fail(ec))
if(pfc_->fail(ec))
{
async_completion<
WriteHandler, void(error_code, std::size_t)

View File

@@ -60,7 +60,10 @@ public:
{
auto const n = boost::asio::buffer_copy(
buffers, boost::asio::buffer(s_));
s_.erase(0, n);
if(n > 0)
s_.erase(0, n);
else
ec = boost::asio::error::eof;
return n;
}
@@ -72,11 +75,15 @@ public:
{
auto const n = boost::asio::buffer_copy(
buffers, boost::asio::buffer(s_));
s_.erase(0, n);
error_code ec;
if(n > 0)
s_.erase(0, n);
else
ec = boost::asio::error::eof;
async_completion<ReadHandler,
void(error_code, std::size_t)> completion(handler);
ios_.post(bind_handler(
completion.handler, error_code{}, n));
completion.handler, ec, n));
return completion.result.get();
}

View File

@@ -37,7 +37,7 @@ private:
std::condition_variable cv_;
bool running_ = false;;
protected:
public:
/// The type of yield context passed to functions.
using yield_context =
boost::asio::yield_context;
@@ -58,6 +58,13 @@ protected:
thread_.join();
}
/// Return the `io_service` associated with the object
boost::asio::io_service&
get_io_service()
{
return ios_;
}
/** Run a function in a coroutine.
This call will block until the coroutine terminates.

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)
//
#include <beast/unit_test/amount.hpp>
#include <beast/unit_test/global_suites.hpp>

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>

View File

@@ -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;
}

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

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.

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.

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)

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";
}
}

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

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

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)

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.

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

View File

@@ -3,6 +3,7 @@ set -e
gdb --silent \
--batch \
--return-child-result \
-ex="set print thread-events off" \
-ex=run \
-ex="thread apply all bt full" \
--args $@

View File

@@ -59,6 +59,7 @@ unit-test http-tests :
http/string_body.cpp
http/type_check.cpp
http/write.cpp
http/detail/chunk_encode.cpp
;
unit-test bench-tests :

View File

@@ -81,7 +81,15 @@ public:
const_buffer{buf+4, 2},
const_buffer{buf+6, 3}}};
auto bs = buffer_cat(b1, b2);
for(int n = 0;
n <= std::distance(bs.begin(), bs.end()); ++n)
{
auto it = std::next(bs.begin(), n);
decltype(it) it2(std::move(it));
it = std::move(it2);
auto pit = &it;
it = std::move(*pit);
}
try
{
std::size_t n = 0;

View File

@@ -9,15 +9,22 @@
#include <beast/core/streambuf_readstream.hpp>
#include <beast/core/streambuf.hpp>
#include <beast/test/fail_stream.hpp>
#include <beast/test/string_stream.hpp>
#include <beast/test/yield_to.hpp>
#include <beast/unit_test/suite.hpp>
#include <boost/asio.hpp>
namespace beast {
class streambuf_readstream_test : public beast::unit_test::suite
class streambuf_readstream_test
: public unit_test::suite
, public test::enable_yield_to
{
using self = streambuf_readstream_test;
public:
void testSpecial()
void testSpecialMembers()
{
using socket_type = boost::asio::ip::tcp::socket;
boost::asio::io_service ios;
@@ -33,12 +40,100 @@ public:
streambuf_readstream<socket_type&, streambuf> srs(sock);
streambuf_readstream<socket_type&, streambuf> srs2(std::move(srs));
}
pass();
}
void testRead(yield_context do_yield)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
static std::size_t constexpr limit = 100;
std::size_t n;
std::string s;
s.resize(13);
for(n = 0; n < limit; ++n)
{
test::fail_stream<
test::string_stream> fs(n, ios_, ", world!");
streambuf_readstream<
decltype(fs)&, streambuf> srs(fs);
srs.buffer().commit(buffer_copy(
srs.buffer().prepare(5), buffer("Hello", 5)));
boost::system::error_code ec;
boost::asio::read(srs, buffer(&s[0], s.size()), ec);
if(! ec)
{
expect(s == "Hello, world!");
break;
}
}
expect(n < limit);
for(n = 0; n < limit; ++n)
{
test::fail_stream<
test::string_stream> fs(n, ios_, ", world!");
streambuf_readstream<
decltype(fs)&, streambuf> srs(fs);
srs.capacity(3);
srs.buffer().commit(buffer_copy(
srs.buffer().prepare(5), buffer("Hello", 5)));
boost::system::error_code ec;
boost::asio::read(srs, buffer(&s[0], s.size()), ec);
if(! ec)
{
expect(s == "Hello, world!");
break;
}
}
expect(n < limit);
for(n = 0; n < limit; ++n)
{
test::fail_stream<
test::string_stream> fs(n, ios_, ", world!");
streambuf_readstream<
decltype(fs)&, streambuf> srs(fs);
srs.buffer().commit(buffer_copy(
srs.buffer().prepare(5), buffer("Hello", 5)));
boost::system::error_code ec;
boost::asio::async_read(
srs, buffer(&s[0], s.size()), do_yield[ec]);
if(! ec)
{
expect(s == "Hello, world!");
break;
}
}
expect(n < limit);
for(n = 0; n < limit; ++n)
{
test::fail_stream<
test::string_stream> fs(n, ios_, ", world!");
streambuf_readstream<
decltype(fs)&, streambuf> srs(fs);
srs.capacity(3);
srs.buffer().commit(buffer_copy(
srs.buffer().prepare(5), buffer("Hello", 5)));
boost::system::error_code ec;
boost::asio::async_read(
srs, buffer(&s[0], s.size()), do_yield[ec]);
if(! ec)
{
expect(s == "Hello, world!");
break;
}
}
expect(n < limit);
}
void run() override
{
testSpecial();
testSpecialMembers();
yield_to(std::bind(&self::testRead,
this, std::placeholders::_1));
}
};

View File

@@ -26,6 +26,7 @@ add_executable (http-tests
string_body.cpp
type_check.cpp
write.cpp
detail/chunk_encode.cpp
)
if (NOT WIN32)

View File

@@ -58,5 +58,5 @@ public:
BEAST_DEFINE_TESTSUITE(basic_headers,http,beast);
} // asio
} // http
} // beast

View File

@@ -573,11 +573,14 @@ public:
parse_ev<true>("GET / HTTP/1.1\r\nf :", parse_error::bad_field);
}
void testCorrupt()
void testInvalidMatrix()
{
using boost::asio::buffer;
static std::size_t constexpr limit = 200;
std::string s;
for(std::size_t n = 0;;++n)
std::size_t n;
for(n = 0; n < limit; ++n)
{
// Create a request and set one octet to an invalid char
s =
@@ -607,6 +610,41 @@ public:
expect(ec);
}
}
expect(n < limit);
for(n = 0; n < limit; ++n)
{
// Create a response and set one octet to an invalid char
s =
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Transer-Encoding: chunked\r\n"
"\r\n"
"10\r\n"
"****************\r\n"
"0\r\n\r\n";
auto const len = s.size();
if(n >= s.size())
{
pass();
break;
}
s[n] = 0;
for(std::size_t m = 1; m < len - 1; ++m)
{
null_parser<true> p;
error_code ec;
p.write(buffer(s.data(), m), ec);
if(ec)
{
pass();
continue;
}
p.write(buffer(s.data() + m, len - m), ec);
expect(ec);
}
}
expect(n < limit);
}
void
@@ -758,7 +796,7 @@ public:
testFlags();
testUpgrade();
testBad();
testCorrupt();
testInvalidMatrix();
testRandomReq(100);
testRandomResp(100);
testBody();

View File

@@ -0,0 +1,132 @@
//
// 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)
//
#include <beast/http/detail/chunk_encode.hpp>
#include <beast/core/to_string.hpp>
#include <beast/unit_test/suite.hpp>
namespace beast {
namespace http {
namespace detail {
class chunk_encode_test : public beast::unit_test::suite
{
public:
struct final_chunk
{
std::string s;
final_chunk() = default;
explicit
final_chunk(std::string s_)
: s(std::move(s_))
{
}
};
static
void
encode1(std::string& s, final_chunk const& fc)
{
using boost::asio::buffer;
if(! fc.s.empty())
s.append(to_string(
chunk_encode(buffer(fc.s.data(), fc.s.size()))));
s.append(to_string(chunk_encode_final()));
}
static
void
encode1(std::string& s, std::string const& piece)
{
using boost::asio::buffer;
s.append(to_string(
chunk_encode(buffer(piece.data(), piece.size()))));
}
static
inline
void
encode(std::string&)
{
}
template<class Arg, class... Args>
static
void
encode(std::string& s, Arg const& arg, Args const&... args)
{
encode1(s, arg);
encode(s, args...);
}
template<class... Args>
void
check(std::string const& answer, Args const&... args)
{
std::string s;
encode(s, args...);
expect(s == answer);
}
void run() override
{
check(
"0\r\n\r\n"
"0\r\n\r\n",
"", final_chunk{});
check(
"1\r\n"
"*\r\n"
"0\r\n\r\n",
final_chunk("*"));
check(
"2\r\n"
"**\r\n"
"0\r\n\r\n",
final_chunk("**"));
check(
"1\r\n"
"*\r\n"
"1\r\n"
"*\r\n"
"0\r\n\r\n",
"*", final_chunk("*"));
check(
"5\r\n"
"*****\r\n"
"7\r\n"
"*******\r\n"
"0\r\n\r\n",
"*****", final_chunk("*******"));
check(
"1\r\n"
"*\r\n"
"1\r\n"
"*\r\n"
"0\r\n\r\n",
"*", "*", final_chunk{});
check(
"4\r\n"
"****\r\n"
"0\r\n\r\n",
"****", final_chunk{});
}
};
BEAST_DEFINE_TESTSUITE(chunk_encode,http,beast);
} // detail
} // http
} // beast

View File

@@ -47,6 +47,7 @@ public:
check("http", parse_error::bad_on_headers_rv);
check("http", parse_error::invalid_chunk_size);
check("http", parse_error::short_read);
check("http", parse_error::general);
}
};

View File

@@ -27,7 +27,8 @@ public:
{
static std::size_t constexpr limit = 100;
std::size_t n;
for(n = 1; n < limit; ++n)
for(n = 0; n < limit; ++n)
{
streambuf sb;
test::fail_stream<test::string_stream> fs(n, ios_,
@@ -48,7 +49,8 @@ public:
}
}
expect(n < limit);
for(n = 1; n < limit; ++n)
for(n = 0; n < limit; ++n)
{
streambuf sb;
test::fail_stream<test::string_stream> fs(n, ios_,
@@ -65,11 +67,8 @@ public:
break;
}
expect(n < limit);
ios_.post(
[&]{
n = 1;
});
for(n = 1; n < limit; ++n)
for(n = 0; n < limit; ++n)
{
streambuf sb;
test::fail_stream<test::string_stream> fs(n, ios_,

View File

@@ -16,20 +16,40 @@
#include <beast/core/error.hpp>
#include <beast/core/streambuf.hpp>
#include <beast/core/to_string.hpp>
#include <beast/test/fail_stream.hpp>
#include <beast/test/yield_to.hpp>
#include <beast/unit_test/suite.hpp>
#include <boost/asio/error.hpp>
#include <sstream>
#include <string>
namespace beast {
namespace http {
class write_test : public beast::unit_test::suite
class write_test
: public beast::unit_test::suite
, public test::enable_yield_to
{
public:
struct string_SyncStream
class string_write_stream
{
boost::asio::io_service& ios_;
public:
std::string str;
explicit
string_write_stream(boost::asio::io_service& ios)
: ios_(ios)
{
}
boost::asio::io_service&
get_io_service()
{
return ios_;
}
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers)
@@ -42,7 +62,8 @@ public:
}
template<class ConstBufferSequence>
std::size_t write_some(
std::size_t
write_some(
ConstBufferSequence const& buffers, error_code& ec)
{
auto const n = buffer_size(buffers);
@@ -54,9 +75,25 @@ public:
buffer_size(buffer));
return n;
}
template<class ConstBufferSequence, class WriteHandler>
typename async_completion<
WriteHandler, void(error_code)>::result_type
async_write_some(ConstBufferSequence const& buffers,
WriteHandler&& handler)
{
error_code ec;
auto const bytes_transferred = write_some(buffers, ec);
async_completion<
WriteHandler, void(error_code, std::size_t)
> completion(handler);
get_io_service().post(
bind_handler(completion.handler, ec, bytes_transferred));
return completion.result.get();
}
};
struct fail_body
struct unsized_body
{
using value_type = std::string;
@@ -64,6 +101,70 @@ public:
{
value_type const& body_;
public:
template<bool isRequest, class Allocator>
explicit
writer(message<isRequest, unsized_body, Allocator> const& msg)
: body_(msg.body)
{
}
void
init(error_code& ec)
{
}
template<class Write>
boost::tribool
operator()(resume_context&&, error_code&, Write&& write)
{
write(boost::asio::buffer(body_));
return true;
}
};
};
struct fail_body
{
class writer;
class value_type
{
friend class writer;
std::string s_;
test::fail_counter& fc_;
boost::asio::io_service& ios_;
public:
value_type(test::fail_counter& fc,
boost::asio::io_service& ios)
: fc_(fc)
, ios_(ios)
{
}
boost::asio::io_service&
get_io_service() const
{
return ios_;
}
value_type&
operator=(std::string s)
{
s_ = std::move(s);
return *this;
}
};
class writer
{
std::size_t n_ = 0;
value_type const& body_;
bool suspend_ = false;
enable_yield_to yt_;
public:
template<bool isRequest, class Allocator>
explicit
@@ -75,48 +176,44 @@ public:
void
init(error_code& ec)
{
ec = boost::system::errc::make_error_code(
boost::system::errc::errc_t::invalid_argument);
body_.fc_.fail(ec);
}
class do_resume
{
resume_context rc_;
public:
explicit
do_resume(resume_context&& rc)
: rc_(std::move(rc))
{
}
void
operator()()
{
rc_();
}
};
template<class Write>
boost::tribool
operator()(resume_context&&, error_code&, Write&& write)
operator()(resume_context&& rc, error_code& ec, Write&& write)
{
write(boost::asio::buffer(body_));
return true;
}
};
};
struct test_body
{
using value_type = std::string;
class writer
{
std::size_t pos_ = 0;
value_type const& body_;
public:
template<bool isRequest, class Allocator>
explicit
writer(message<isRequest, test_body, Allocator> const& msg)
: body_(msg.body)
{
}
void
init(error_code& ec)
{
}
template<class Write>
boost::tribool
operator()(resume_context&&, error_code&, Write&& write)
{
write(boost::asio::buffer(body_));
return true;
if(body_.fc_.fail(ec))
return false;
suspend_ = ! suspend_;
if(suspend_)
{
yt_.get_io_service().post(do_resume{std::move(rc)});
return boost::indeterminate;
}
if(n_ >= body_.s_.size())
return true;
write(boost::asio::buffer(body_.s_.data() + n_, 1));
++n_;
return n_ == body_.s_.size();
}
};
};
@@ -125,13 +222,228 @@ public:
std::string
str(message_v1<isRequest, Body, Headers> const& m)
{
string_SyncStream ss;
string_write_stream ss(ios_);
write(ss, m);
return ss.str;
}
void
testWrite()
testAsyncWrite(yield_context do_yield)
{
{
message_v1<false, string_body, headers> m;
m.version = 10;
m.status = 200;
m.reason = "OK";
m.headers.insert("Server", "test");
m.headers.insert("Content-Length", "5");
m.body = "*****";
error_code ec;
string_write_stream ss(ios_);
async_write(ss, m, do_yield[ec]);
if(expect(! ec, ec.message()))
expect(ss.str ==
"HTTP/1.0 200 OK\r\n"
"Server: test\r\n"
"Content-Length: 5\r\n"
"\r\n"
"*****");
}
{
message_v1<false, string_body, headers> m;
m.version = 11;
m.status = 200;
m.reason = "OK";
m.headers.insert("Server", "test");
m.headers.insert("Transfer-Encoding", "chunked");
m.body = "*****";
error_code ec;
string_write_stream ss(ios_);
async_write(ss, m, do_yield[ec]);
if(expect(! ec, ec.message()))
expect(ss.str ==
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"5\r\n"
"*****\r\n"
"0\r\n\r\n");
}
}
void
testFailures(yield_context do_yield)
{
static std::size_t constexpr limit = 100;
std::size_t n;
for(n = 0; n < limit; ++n)
{
test::fail_counter fc(n);
test::fail_stream<
string_write_stream> fs(fc, ios_);
message_v1<true, fail_body, headers> m(
std::piecewise_construct,
std::forward_as_tuple(fc, ios_));
m.method = "GET";
m.url = "/";
m.version = 10;
m.headers.insert("User-Agent", "test");
m.headers.insert("Content-Length", "5");
m.body = "*****";
try
{
write(fs, m);
expect(fs.next_layer().str ==
"GET / HTTP/1.0\r\n"
"User-Agent: test\r\n"
"Content-Length: 5\r\n"
"\r\n"
"*****"
);
pass();
break;
}
catch(std::exception const&)
{
}
}
expect(n < limit);
for(n = 0; n < limit; ++n)
{
test::fail_counter fc(n);
test::fail_stream<
string_write_stream> fs(fc, ios_);
message_v1<true, fail_body, headers> m(
std::piecewise_construct,
std::forward_as_tuple(fc, ios_));
m.method = "GET";
m.url = "/";
m.version = 10;
m.headers.insert("User-Agent", "test");
m.headers.insert("Transfer-Encoding", "chunked");
m.body = "*****";
error_code ec;
write(fs, m, ec);
if(ec == boost::asio::error::eof)
{
expect(fs.next_layer().str ==
"GET / HTTP/1.0\r\n"
"User-Agent: test\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"1\r\n*\r\n"
"1\r\n*\r\n"
"1\r\n*\r\n"
"1\r\n*\r\n"
"1\r\n*\r\n"
"0\r\n\r\n"
);
break;
}
}
expect(n < limit);
for(n = 0; n < limit; ++n)
{
test::fail_counter fc(n);
test::fail_stream<
string_write_stream> fs(fc, ios_);
message_v1<true, fail_body, headers> m(
std::piecewise_construct,
std::forward_as_tuple(fc, ios_));
m.method = "GET";
m.url = "/";
m.version = 10;
m.headers.insert("User-Agent", "test");
m.headers.insert("Transfer-Encoding", "chunked");
m.body = "*****";
error_code ec;
async_write(fs, m, do_yield[ec]);
if(ec == boost::asio::error::eof)
{
expect(fs.next_layer().str ==
"GET / HTTP/1.0\r\n"
"User-Agent: test\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"1\r\n*\r\n"
"1\r\n*\r\n"
"1\r\n*\r\n"
"1\r\n*\r\n"
"1\r\n*\r\n"
"0\r\n\r\n"
);
break;
}
}
expect(n < limit);
for(n = 0; n < limit; ++n)
{
test::fail_counter fc(n);
test::fail_stream<
string_write_stream> fs(fc, ios_);
message_v1<true, fail_body, headers> m(
std::piecewise_construct,
std::forward_as_tuple(fc, ios_));
m.method = "GET";
m.url = "/";
m.version = 10;
m.headers.insert("User-Agent", "test");
m.headers.insert("Content-Length", "5");
m.body = "*****";
error_code ec;
write(fs, m, ec);
if(! ec)
{
expect(fs.next_layer().str ==
"GET / HTTP/1.0\r\n"
"User-Agent: test\r\n"
"Content-Length: 5\r\n"
"\r\n"
"*****"
);
break;
}
}
expect(n < limit);
for(n = 0; n < limit; ++n)
{
test::fail_counter fc(n);
test::fail_stream<
string_write_stream> fs(fc, ios_);
message_v1<true, fail_body, headers> m(
std::piecewise_construct,
std::forward_as_tuple(fc, ios_));
m.method = "GET";
m.url = "/";
m.version = 10;
m.headers.insert("User-Agent", "test");
m.headers.insert("Content-Length", "5");
m.body = "*****";
error_code ec;
async_write(fs, m, do_yield[ec]);
if(! ec)
{
expect(fs.next_layer().str ==
"GET / HTTP/1.0\r\n"
"User-Agent: test\r\n"
"Content-Length: 5\r\n"
"\r\n"
"*****"
);
break;
}
}
expect(n < limit);
}
void
testOutput()
{
// auto content-length HTTP/1.0
{
@@ -188,14 +500,14 @@ public:
}
// no content-length HTTP/1.0
{
message_v1<true, test_body, headers> m;
message_v1<true, unsized_body, headers> m;
m.method = "GET";
m.url = "/";
m.version = 10;
m.headers.insert("User-Agent", "test");
m.body = "*";
prepare(m);
string_SyncStream ss;
string_write_stream ss(ios_);
error_code ec;
write(ss, m, ec);
expect(ec == boost::asio::error::eof);
@@ -232,7 +544,7 @@ public:
m.headers.insert("User-Agent", "test");
m.body = "*";
prepare(m, connection::close);
string_SyncStream ss;
string_write_stream ss(ios_);
error_code ec;
write(ss, m, ec);
expect(ec == boost::asio::error::eof);
@@ -262,14 +574,14 @@ public:
}
// no content-length HTTP/1.1
{
message_v1<true, test_body, headers> m;
message_v1<true, unsized_body, headers> m;
m.method = "GET";
m.url = "/";
m.version = 11;
m.headers.insert("User-Agent", "test");
m.body = "*";
prepare(m);
string_SyncStream ss;
string_write_stream ss(ios_);
error_code ec;
write(ss, m, ec);
expect(ss.str ==
@@ -297,10 +609,38 @@ public:
"GET / HTTP/1.1\r\nUser-Agent: test\r\nContent-Length: 1\r\n\r\n*");
}
void testOstream()
{
message_v1<true, string_body, headers> m;
m.method = "GET";
m.url = "/";
m.version = 11;
m.headers.insert("User-Agent", "test");
m.body = "*";
prepare(m);
std::stringstream ss;
ss.setstate(ss.rdstate() |
std::stringstream::failbit);
try
{
ss << m;
fail();
}
catch(std::exception const&)
{
pass();
}
}
void run() override
{
testWrite();
yield_to(std::bind(&write_test::testAsyncWrite,
this, std::placeholders::_1));
yield_to(std::bind(&write_test::testFailures,
this, std::placeholders::_1));
testOutput();
testConvert();
testOstream();
}
};

View File

@@ -154,6 +154,14 @@ public:
check(fh);
fh.rsv1 = false;
fh.rsv2 = true;
check(fh);
fh.rsv2 = false;
fh.rsv3 = true;
check(fh);
fh.rsv3 = false;
fh.op = opcode::rsv3;
check(fh);
fh.op = opcode::text;

View File

@@ -42,6 +42,7 @@ public:
check("websocket", error::request_malformed);
check("websocket", error::request_invalid);
check("websocket", error::request_denied);
check("websocket", error::general);
}
};

View File

@@ -33,6 +33,13 @@ public:
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
void testClamp()
{
expect(detail::clamp(
std::numeric_limits<std::uint64_t>::max()) ==
std::numeric_limits<std::size_t>::max());
}
void testSpecialMembers()
{
stream<socket_type> ws(ios_);
@@ -304,7 +311,7 @@ public:
std::size_t n;
// synchronous, exceptions
for(n = 1; n < limit; ++n)
for(n = 0; n < limit; ++n)
{
error_code ec;
socket_type sock(ios_);
@@ -343,7 +350,7 @@ public:
expect(n < limit);
// synchronous, error codes
for(n = 1; n < limit; ++n)
for(n = 0; n < limit; ++n)
{
error_code ec;
socket_type sock(ios_);
@@ -378,7 +385,7 @@ public:
expect(n < limit);
// asynchronous
for(n = 1; n < limit; ++n)
for(n = 0; n < limit; ++n)
{
error_code ec;
socket_type sock(ios_);
@@ -470,8 +477,35 @@ public:
}
}
void testWriteFrame(endpoint_type const& ep)
{
for(;;)
{
boost::asio::io_service ios;
error_code ec;
socket_type sock(ios);
sock.connect(ep, ec);
if(! expect(! ec, ec.message()))
break;
stream<socket_type&> ws(sock);
ws.handshake("localhost", "/", ec);
if(! expect(! ec, ec.message()))
break;
ws.async_write_frame(false,
boost::asio::null_buffers{},
[](error_code){ });
//
// Destruction of the io_service will cause destruction
// of the write_frame_op without invoking the final handler.
//
break;
}
}
void run() override
{
testClamp();
testSpecialMembers();
testOptions();
@@ -483,33 +517,31 @@ public:
address_type::from_string("127.0.0.1"), 0};
{
sync_echo_peer server(true, any);
auto const ep = server.local_endpoint();
yield_to(std::bind(&stream_test::testHandshake,
this, server.local_endpoint(),
std::placeholders::_1));
this, ep, std::placeholders::_1));
yield_to(std::bind(&stream_test::testErrorHandling,
this, server.local_endpoint(),
std::placeholders::_1));
this, ep, std::placeholders::_1));
yield_to(std::bind(&stream_test::testMask,
this, server.local_endpoint(),
std::placeholders::_1));
this, ep, std::placeholders::_1));
testWriteFrame(ep);
}
{
async_echo_peer server(true, any, 1);
auto const ep = server.local_endpoint();
yield_to(std::bind(&stream_test::testHandshake,
this, server.local_endpoint(),
std::placeholders::_1));
this, ep, std::placeholders::_1));
yield_to(std::bind(&stream_test::testErrorHandling,
this, server.local_endpoint(),
std::placeholders::_1));
this, ep, std::placeholders::_1));
yield_to(std::bind(&stream_test::testMask,
this, server.local_endpoint(),
std::placeholders::_1));
this, ep, std::placeholders::_1));
}
pass();

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_ASYNC_ECHO_PEER_H_INCLUDED
#define BEAST_WEBSOCKET_ASYNC_ECHO_PEER_H_INCLUDED

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)
//
#include "websocket_async_echo_peer.hpp"
#include "websocket_sync_echo_peer.hpp"

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_SYNC_ECHO_PEER_H_INCLUDED
#define BEAST_WEBSOCKET_SYNC_ECHO_PEER_H_INCLUDED