Add 'src/beast/' from commit '2f9a8440c2432d8a196571d6300404cb76314125'

git-subtree-dir: src/beast
git-subtree-mainline: 7c90b9ef88
git-subtree-split: 2f9a8440c2
This commit is contained in:
Vinnie Falco
2016-09-15 15:07:45 -04:00
251 changed files with 55530 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
# Part of Beast
GroupSources(extras/beast extras)
GroupSources(include/beast beast)
GroupSources(test "/")
add_executable (lib-tests
${BEAST_INCLUDES}
${EXTRAS_INCLUDES}
../extras/beast/unit_test/main.cpp
core.cpp
http.cpp
version.cpp
websocket.cpp
)
if (NOT WIN32)
target_link_libraries(lib-tests ${Boost_LIBRARIES})
endif()

86
src/beast/test/Jamfile Normal file
View File

@@ -0,0 +1,86 @@
#
# 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)
#
import os ;
compile core.cpp : : ;
compile http.cpp : : ;
compile version.cpp : : ;
compile websocket.cpp : : ;
unit-test core-tests :
../extras/beast/unit_test/main.cpp
core/async_completion.cpp
core/basic_streambuf.cpp
core/bind_handler.cpp
core/buffer_cat.cpp
core/buffer_concepts.cpp
core/buffers_adapter.cpp
core/consuming_buffers.cpp
core/dynabuf_readstream.cpp
core/error.cpp
core/handler_alloc.cpp
core/handler_concepts.cpp
core/placeholders.cpp
core/prepare_buffers.cpp
core/static_streambuf.cpp
core/static_string.cpp
core/stream_concepts.cpp
core/streambuf.cpp
core/to_string.cpp
core/write_dynabuf.cpp
core/base64.cpp
core/empty_base_optimization.cpp
core/get_lowest_layer.cpp
core/sha1.cpp
;
unit-test http-tests :
../extras/beast/unit_test/main.cpp
http/basic_dynabuf_body.cpp
http/basic_headers.cpp
http/basic_parser_v1.cpp
http/body_type.cpp
http/concepts.cpp
http/empty_body.cpp
http/headers.cpp
http/message.cpp
http/message_v1.cpp
http/parse_error.cpp
http/parser_v1.cpp
http/read.cpp
http/reason.cpp
http/resume_context.cpp
http/rfc7230.cpp
http/streambuf_body.cpp
http/string_body.cpp
http/write.cpp
http/chunk_encode.cpp
;
unit-test bench-tests :
../extras/beast/unit_test/main.cpp
http/nodejs_parser.cpp
http/parser_bench.cpp
;
unit-test websocket-tests :
../extras/beast/unit_test/main.cpp
websocket/error.cpp
websocket/option.cpp
websocket/rfc6455.cpp
websocket/stream.cpp
websocket/teardown.cpp
websocket/frame.cpp
websocket/mask.cpp
websocket/stream_base.cpp
websocket/utf8_checker.cpp
;
exe websocket-echo :
websocket/websocket_echo.cpp
;

9
src/beast/test/core.cpp Normal file
View File

@@ -0,0 +1,9 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core.hpp>

View File

@@ -0,0 +1,39 @@
# Part of Beast
GroupSources(extras/beast extras)
GroupSources(include/beast beast)
GroupSources(test/core "/")
add_executable (core-tests
${BEAST_INCLUDES}
${EXTRAS_INCLUDES}
../../extras/beast/unit_test/main.cpp
buffer_test.hpp
async_completion.cpp
basic_streambuf.cpp
bind_handler.cpp
buffer_cat.cpp
buffer_concepts.cpp
buffers_adapter.cpp
consuming_buffers.cpp
dynabuf_readstream.cpp
error.cpp
handler_alloc.cpp
handler_concepts.cpp
placeholders.cpp
prepare_buffers.cpp
static_streambuf.cpp
static_string.cpp
stream_concepts.cpp
streambuf.cpp
to_string.cpp
write_dynabuf.cpp
base64.cpp
empty_base_optimization.cpp
get_lowest_layer.cpp
sha1.cpp
)
if (NOT WIN32)
target_link_libraries(core-tests ${Boost_LIBRARIES} Threads::Threads)
endif()

View File

@@ -0,0 +1,9 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/async_completion.hpp>

View File

@@ -0,0 +1,44 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/detail/base64.hpp>
#include <beast/unit_test/suite.hpp>
namespace beast {
namespace detail {
class base64_test : public beast::unit_test::suite
{
public:
void
check (std::string const& in, std::string const& out)
{
auto const encoded = base64_encode (in);
BEAST_EXPECT(encoded == out);
BEAST_EXPECT(base64_decode (encoded) == in);
}
void
run()
{
check ("", "");
check ("f", "Zg==");
check ("fo", "Zm8=");
check ("foo", "Zm9v");
check ("foob", "Zm9vYg==");
check ("fooba", "Zm9vYmE=");
check ("foobar", "Zm9vYmFy");
}
};
BEAST_DEFINE_TESTSUITE(base64,core,beast);
} // detail
} // beast

View File

@@ -0,0 +1,462 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/basic_streambuf.hpp>
#include "buffer_test.hpp"
#include <beast/core/streambuf.hpp>
#include <beast/core/to_string.hpp>
#include <beast/unit_test/suite.hpp>
#include <boost/asio/buffer.hpp>
#include <algorithm>
#include <atomic>
#include <memory>
#include <string>
namespace beast {
struct test_allocator_info
{
std::size_t ncopy = 0;
std::size_t nmove = 0;
std::size_t nselect = 0;
};
template<class T,
bool Assign, bool Move, bool Swap, bool Select>
class test_allocator;
template<class T,
bool Assign, bool Move, bool Swap, bool Select>
struct test_allocator_base
{
};
template<class T,
bool Assign, bool Move, bool Swap>
struct test_allocator_base<T, Assign, Move, Swap, true>
{
static
test_allocator<T, Assign, Move, Swap, true>
select_on_container_copy_construction(
test_allocator<T, Assign, Move, Swap, true> const& a)
{
return test_allocator<T, Assign, Move, Swap, true>{};
}
};
template<class T,
bool Assign, bool Move, bool Swap, bool Select>
class test_allocator : public test_allocator_base<
T, Assign, Move, Swap, Select>
{
std::size_t id_;
std::shared_ptr<test_allocator_info> info_;
template<class, bool, bool, bool, bool>
friend class test_allocator;
public:
using value_type = T;
using propagate_on_container_copy_assignment =
std::integral_constant<bool, Assign>;
using propagate_on_container_move_assignment =
std::integral_constant<bool, Move>;
using propagate_on_container_swap =
std::integral_constant<bool, Swap>;
template<class U>
struct rebind
{
using other = test_allocator<
U, Assign, Move, Swap, Select>;
};
test_allocator()
: id_([]
{
static std::atomic<
std::size_t> sid(0);
return ++sid;
}())
, info_(std::make_shared<test_allocator_info>())
{
}
test_allocator(test_allocator const& u) noexcept
: id_(u.id_)
, info_(u.info_)
{
++info_->ncopy;
}
template<class U>
test_allocator(test_allocator<
U, Assign, Move, Swap, Select> const& u) noexcept
: id_(u.id_)
, info_(u.info_)
{
++info_->ncopy;
}
test_allocator(test_allocator&& t)
: id_(t.id_)
, info_(t.info_)
{
++info_->nmove;
}
value_type*
allocate(std::size_t n)
{
return static_cast<value_type*>(
::operator new (n*sizeof(value_type)));
}
void
deallocate(value_type* p, std::size_t) noexcept
{
::operator delete(p);
}
std::size_t
id() const
{
return id_;
}
test_allocator_info const*
operator->() const
{
return info_.get();
}
};
class basic_streambuf_test : public beast::unit_test::suite
{
public:
template<class Alloc1, class Alloc2>
static
bool
eq(basic_streambuf<Alloc1> const& sb1,
basic_streambuf<Alloc2> const& sb2)
{
return to_string(sb1.data()) == to_string(sb2.data());
}
template<class ConstBufferSequence>
void
expect_size(std::size_t n, ConstBufferSequence const& buffers)
{
BEAST_EXPECT(test::size_pre(buffers) == n);
BEAST_EXPECT(test::size_post(buffers) == n);
BEAST_EXPECT(test::size_rev_pre(buffers) == n);
BEAST_EXPECT(test::size_rev_post(buffers) == n);
}
template<class U, class V>
static
void
self_assign(U& u, V&& v)
{
u = std::forward<V>(v);
}
void testSpecialMembers()
{
using boost::asio::buffer;
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
std::string const s = "Hello, world";
BEAST_EXPECT(s.size() == 12);
for(std::size_t i = 1; i < 12; ++i) {
for(std::size_t x = 1; x < 4; ++x) {
for(std::size_t y = 1; y < 4; ++y) {
std::size_t z = s.size() - (x + y);
{
streambuf sb(i);
sb.commit(buffer_copy(sb.prepare(x), buffer(s.data(), x)));
sb.commit(buffer_copy(sb.prepare(y), buffer(s.data()+x, y)));
sb.commit(buffer_copy(sb.prepare(z), buffer(s.data()+x+y, z)));
BEAST_EXPECT(to_string(sb.data()) == s);
{
streambuf sb2(sb);
BEAST_EXPECT(eq(sb, sb2));
}
{
streambuf sb2;
sb2 = sb;
BEAST_EXPECT(eq(sb, sb2));
}
{
streambuf sb2(std::move(sb));
BEAST_EXPECT(to_string(sb2.data()) == s);
expect_size(0, sb.data());
sb = std::move(sb2);
BEAST_EXPECT(to_string(sb.data()) == s);
expect_size(0, sb2.data());
}
self_assign(sb, sb);
BEAST_EXPECT(to_string(sb.data()) == s);
self_assign(sb, std::move(sb));
BEAST_EXPECT(to_string(sb.data()) == s);
}
}}}
try
{
streambuf sb0(0);
fail();
}
catch(std::exception const&)
{
pass();
}
}
void testAllocator()
{
// VFALCO This needs work
{
using alloc_type =
test_allocator<char, false, false, false, false>;
using sb_type = basic_streambuf<alloc_type>;
sb_type sb;
BEAST_EXPECT(sb.get_allocator().id() == 1);
}
{
using alloc_type =
test_allocator<char, false, false, false, false>;
using sb_type = basic_streambuf<alloc_type>;
sb_type sb;
BEAST_EXPECT(sb.get_allocator().id() == 2);
sb_type sb2(sb);
BEAST_EXPECT(sb2.get_allocator().id() == 2);
sb_type sb3(sb, alloc_type{});
}
}
void
testPrepare()
{
using boost::asio::buffer_size;
{
streambuf sb(2);
BEAST_EXPECT(buffer_size(sb.prepare(5)) == 5);
BEAST_EXPECT(buffer_size(sb.prepare(8)) == 8);
BEAST_EXPECT(buffer_size(sb.prepare(7)) == 7);
}
{
streambuf sb(2);
sb.prepare(2);
BEAST_EXPECT(test::buffer_count(sb.prepare(5)) == 2);
BEAST_EXPECT(test::buffer_count(sb.prepare(8)) == 3);
BEAST_EXPECT(test::buffer_count(sb.prepare(4)) == 2);
}
}
void testCommit()
{
using boost::asio::buffer_size;
streambuf sb(2);
sb.prepare(2);
sb.prepare(5);
sb.commit(1);
expect_size(1, sb.data());
}
void testConsume()
{
using boost::asio::buffer_size;
streambuf sb(1);
expect_size(5, sb.prepare(5));
sb.commit(3);
expect_size(3, sb.data());
sb.consume(1);
expect_size(2, sb.data());
}
void testMatrix()
{
using boost::asio::buffer;
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
std::string const s = "Hello, world";
BEAST_EXPECT(s.size() == 12);
for(std::size_t i = 1; i < 12; ++i) {
for(std::size_t x = 1; x < 4; ++x) {
for(std::size_t y = 1; y < 4; ++y) {
for(std::size_t t = 1; t < 4; ++ t) {
for(std::size_t u = 1; u < 4; ++ u) {
std::size_t z = s.size() - (x + y);
std::size_t v = s.size() - (t + u);
{
streambuf sb(i);
{
auto d = sb.prepare(z);
BEAST_EXPECT(buffer_size(d) == z);
}
{
auto d = sb.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
{
auto d = sb.prepare(y);
BEAST_EXPECT(buffer_size(d) == y);
}
{
auto d = sb.prepare(x);
BEAST_EXPECT(buffer_size(d) == x);
sb.commit(buffer_copy(d, buffer(s.data(), x)));
}
BEAST_EXPECT(sb.size() == x);
BEAST_EXPECT(buffer_size(sb.data()) == sb.size());
{
auto d = sb.prepare(x);
BEAST_EXPECT(buffer_size(d) == x);
}
{
auto d = sb.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
{
auto d = sb.prepare(z);
BEAST_EXPECT(buffer_size(d) == z);
}
{
auto d = sb.prepare(y);
BEAST_EXPECT(buffer_size(d) == y);
sb.commit(buffer_copy(d, buffer(s.data()+x, y)));
}
sb.commit(1);
BEAST_EXPECT(sb.size() == x + y);
BEAST_EXPECT(buffer_size(sb.data()) == sb.size());
{
auto d = sb.prepare(x);
BEAST_EXPECT(buffer_size(d) == x);
}
{
auto d = sb.prepare(y);
BEAST_EXPECT(buffer_size(d) == y);
}
{
auto d = sb.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
{
auto d = sb.prepare(z);
BEAST_EXPECT(buffer_size(d) == z);
sb.commit(buffer_copy(d, buffer(s.data()+x+y, z)));
}
sb.commit(2);
BEAST_EXPECT(sb.size() == x + y + z);
BEAST_EXPECT(buffer_size(sb.data()) == sb.size());
BEAST_EXPECT(to_string(sb.data()) == s);
sb.consume(t);
{
auto d = sb.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
BEAST_EXPECT(to_string(sb.data()) == s.substr(t, std::string::npos));
sb.consume(u);
BEAST_EXPECT(to_string(sb.data()) == s.substr(t + u, std::string::npos));
sb.consume(v);
BEAST_EXPECT(to_string(sb.data()) == "");
sb.consume(1);
{
auto d = sb.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
}
}}}}}
}
void testIterators()
{
using boost::asio::buffer_size;
streambuf sb(1);
sb.prepare(1);
sb.commit(1);
sb.prepare(2);
sb.commit(2);
expect_size(3, sb.data());
sb.prepare(1);
expect_size(3, sb.prepare(3));
sb.commit(2);
BEAST_EXPECT(test::buffer_count(sb.data()) == 4);
}
void testOutputStream()
{
streambuf sb;
sb << "x";
BEAST_EXPECT(to_string(sb.data()) == "x");
}
void testReadSizeHelper()
{
using boost::asio::buffer_size;
{
streambuf sb(10);
BEAST_EXPECT(read_size_helper(sb, 0) == 0);
BEAST_EXPECT(read_size_helper(sb, 1) == 1);
BEAST_EXPECT(read_size_helper(sb, 10) == 10);
BEAST_EXPECT(read_size_helper(sb, 20) == 20);
BEAST_EXPECT(read_size_helper(sb, 1000) == 512);
sb.prepare(3);
sb.commit(3);
BEAST_EXPECT(read_size_helper(sb, 10) == 7);
BEAST_EXPECT(read_size_helper(sb, 1000) == 7);
}
{
streambuf sb(1000);
BEAST_EXPECT(read_size_helper(sb, 0) == 0);
BEAST_EXPECT(read_size_helper(sb, 1) == 1);
BEAST_EXPECT(read_size_helper(sb, 1000) == 1000);
BEAST_EXPECT(read_size_helper(sb, 2000) == 1000);
sb.prepare(3);
BEAST_EXPECT(read_size_helper(sb, 0) == 0);
BEAST_EXPECT(read_size_helper(sb, 1) == 1);
BEAST_EXPECT(read_size_helper(sb, 1000) == 1000);
BEAST_EXPECT(read_size_helper(sb, 2000) == 1000);
sb.commit(3);
BEAST_EXPECT(read_size_helper(sb, 0) == 0);
BEAST_EXPECT(read_size_helper(sb, 1) == 1);
BEAST_EXPECT(read_size_helper(sb, 1000) == 997);
BEAST_EXPECT(read_size_helper(sb, 2000) == 997);
sb.consume(2);
BEAST_EXPECT(read_size_helper(sb, 0) == 0);
BEAST_EXPECT(read_size_helper(sb, 1) == 1);
BEAST_EXPECT(read_size_helper(sb, 1000) == 997);
BEAST_EXPECT(read_size_helper(sb, 2000) == 997);
}
{
streambuf sb(2);
BEAST_EXPECT(test::buffer_count(sb.prepare(2)) == 1);
BEAST_EXPECT(test::buffer_count(sb.prepare(3)) == 2);
BEAST_EXPECT(buffer_size(sb.prepare(5)) == 5);
BEAST_EXPECT(read_size_helper(sb, 10) == 6);
}
}
void run() override
{
testSpecialMembers();
testAllocator();
testPrepare();
testCommit();
testConsume();
testMatrix();
testIterators();
testOutputStream();
testReadSizeHelper();
}
};
BEAST_DEFINE_TESTSUITE(basic_streambuf,core,beast);
} // beast

View File

@@ -0,0 +1,35 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/bind_handler.hpp>
#include <beast/unit_test/suite.hpp>
#include <functional>
namespace beast {
class bind_handler_test : public unit_test::suite
{
public:
static void foo (int)
{
}
void run()
{
auto f (bind_handler (
std::bind (&foo, std::placeholders::_1),
42));
f();
pass();
}
};
BEAST_DEFINE_TESTSUITE(bind_handler,core,beast);
} // beast

View File

@@ -0,0 +1,151 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/buffer_cat.hpp>
#include <beast/unit_test/suite.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/streambuf.hpp>
#include <iterator>
#include <list>
#include <vector>
namespace beast {
class buffer_cat_test : public unit_test::suite
{
public:
template< class Iterator >
static
std::reverse_iterator<Iterator>
make_reverse_iterator( Iterator i )
{
return std::reverse_iterator<Iterator>(i);
}
void testBufferCat()
{
using boost::asio::buffer_size;
using boost::asio::const_buffer;
char buf[10];
std::list<const_buffer> b1;
std::vector<const_buffer> b2{
const_buffer{buf+0, 1},
const_buffer{buf+1, 2}};
std::list<const_buffer> b3;
std::array<const_buffer, 3> b4{{
const_buffer{buf+3, 1},
const_buffer{buf+4, 2},
const_buffer{buf+6, 3}}};
std::list<const_buffer> b5{
const_buffer{buf+9, 1}};
std::list<const_buffer> b6;
auto bs = buffer_cat(
b1, b2, b3, b4, b5, b6);
BEAST_EXPECT(buffer_size(bs) == 10);
std::vector<const_buffer> v;
for(auto iter = make_reverse_iterator(bs.end());
iter != make_reverse_iterator(bs.begin()); ++iter)
v.emplace_back(*iter);
BEAST_EXPECT(buffer_size(bs) == 10);
decltype(bs) bs2(bs);
auto bs3(std::move(bs));
bs = bs2;
bs3 = std::move(bs2);
{
boost::asio::streambuf sb1, sb2;
BEAST_EXPECT(buffer_size(buffer_cat(
sb1.prepare(5), sb2.prepare(7))) == 12);
sb1.commit(5);
sb2.commit(7);
BEAST_EXPECT(buffer_size(buffer_cat(
sb1.data(), sb2.data())) == 12);
}
for(auto it = bs.begin(); it != bs.end(); ++it)
{
decltype(bs)::const_iterator copy;
copy = it;
BEAST_EXPECT(copy == it);
copy = copy;
BEAST_EXPECT(copy == it);
}
}
void testIterators()
{
using boost::asio::buffer_size;
using boost::asio::const_buffer;
char buf[9];
std::vector<const_buffer> b1{
const_buffer{buf+0, 1},
const_buffer{buf+1, 2}};
std::array<const_buffer, 3> b2{{
const_buffer{buf+3, 1},
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;
for(auto it = bs.begin(); n < 100; ++it)
++n;
fail();
}
catch(std::exception const&)
{
pass();
}
try
{
std::size_t n = 0;
for(auto it = bs.end(); n < 100; --it)
++n;
fail();
}
catch(std::exception const&)
{
pass();
}
try
{
buffer_size(*bs.end());
fail();
}
catch(std::exception const&)
{
pass();
}
auto bs2 = bs;
BEAST_EXPECT(bs.begin() != bs2.begin());
BEAST_EXPECT(bs.end() != bs2.end());
decltype(bs)::const_iterator it;
decltype(bs2)::const_iterator it2;
BEAST_EXPECT(it == it2);
}
void run() override
{
testBufferCat();
testIterators();
}
};
BEAST_DEFINE_TESTSUITE(buffer_cat,core,beast);
} // beast

View File

@@ -0,0 +1,25 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/buffer_concepts.hpp>
namespace beast {
namespace {
struct T
{
};
}
static_assert(is_ConstBufferSequence<detail::ConstBufferSequence>::value, "");
static_assert(! is_ConstBufferSequence<T>::value, "");
static_assert(is_MutableBufferSequence<detail::MutableBufferSequence>::value, "");
static_assert(! is_MutableBufferSequence<T>::value, "");
} // beast

View File

@@ -0,0 +1,89 @@
//
// 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_BUFFER_TEST_HPP
#define BEAST_TEST_BUFFER_TEST_HPP
#include <beast/core/buffer_concepts.hpp>
#include <boost/asio/buffer.hpp>
#include <algorithm>
#include <type_traits>
namespace beast {
namespace test {
template<class ConstBufferSequence>
typename std::enable_if<
is_ConstBufferSequence<ConstBufferSequence>::value,
std::size_t>::type
buffer_count(ConstBufferSequence const& buffers)
{
return std::distance(buffers.begin(), buffers.end());
}
template<class ConstBufferSequence>
typename std::enable_if<
is_ConstBufferSequence<ConstBufferSequence>::value,
std::size_t>::type
size_pre(ConstBufferSequence const& buffers)
{
std::size_t n = 0;
for(auto it = buffers.begin(); it != buffers.end(); ++it)
{
typename ConstBufferSequence::const_iterator it0(std::move(it));
typename ConstBufferSequence::const_iterator it1(it0);
typename ConstBufferSequence::const_iterator it2;
it2 = it1;
n += boost::asio::buffer_size(*it2);
it = std::move(it2);
}
return n;
}
template<class ConstBufferSequence>
typename std::enable_if<
is_ConstBufferSequence<ConstBufferSequence>::value,
std::size_t>::type
size_post(ConstBufferSequence const& buffers)
{
std::size_t n = 0;
for(auto it = buffers.begin(); it != buffers.end(); it++)
n += boost::asio::buffer_size(*it);
return n;
}
template<class ConstBufferSequence>
typename std::enable_if<
is_ConstBufferSequence<ConstBufferSequence>::value,
std::size_t>::type
size_rev_pre(ConstBufferSequence const& buffers)
{
std::size_t n = 0;
for(auto it = buffers.end(); it != buffers.begin();)
n += boost::asio::buffer_size(*--it);
return n;
}
template<class ConstBufferSequence>
typename std::enable_if<
is_ConstBufferSequence<ConstBufferSequence>::value,
std::size_t>::type
size_rev_post(ConstBufferSequence const& buffers)
{
std::size_t n = 0;
for(auto it = buffers.end(); it != buffers.begin();)
{
it--;
n += boost::asio::buffer_size(*it);
}
return n;
}
} // test
} // beast
#endif

View File

@@ -0,0 +1,190 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/buffers_adapter.hpp>
#include <beast/core/streambuf.hpp>
#include <beast/unit_test/suite.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/streambuf.hpp>
#include <iterator>
namespace beast {
class buffers_adapter_test : public unit_test::suite
{
public:
template<class ConstBufferSequence>
static
std::string
to_string(ConstBufferSequence const& bs)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
std::string s;
s.reserve(buffer_size(bs));
for(auto const& b : bs)
s.append(buffer_cast<char const*>(b),
buffer_size(b));
return s;
}
void testBuffersAdapter()
{
using boost::asio::buffer;
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
using boost::asio::const_buffer;
using boost::asio::mutable_buffer;
char buf[12];
std::string const s = "Hello, world";
BEAST_EXPECT(s.size() == sizeof(buf));
for(std::size_t i = 1; i < 4; ++i) {
for(std::size_t j = 1; j < 4; ++j) {
for(std::size_t x = 1; x < 4; ++x) {
for(std::size_t y = 1; y < 4; ++y) {
for(std::size_t t = 1; t < 4; ++ t) {
for(std::size_t u = 1; u < 4; ++ u) {
std::size_t k = sizeof(buf) - (i + j);
std::size_t z = sizeof(buf) - (x + y);
std::size_t v = sizeof(buf) - (t + u);
{
std::memset(buf, 0, sizeof(buf));
std::array<mutable_buffer, 3> bs{{
mutable_buffer{&buf[0], i},
mutable_buffer{&buf[i], j},
mutable_buffer{&buf[i+j], k}}};
buffers_adapter<decltype(bs)> ba(std::move(bs));
BEAST_EXPECT(ba.max_size() == sizeof(buf));
{
auto d = ba.prepare(z);
BEAST_EXPECT(buffer_size(d) == z);
}
{
auto d = ba.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
{
auto d = ba.prepare(y);
BEAST_EXPECT(buffer_size(d) == y);
}
{
auto d = ba.prepare(x);
BEAST_EXPECT(buffer_size(d) == x);
ba.commit(buffer_copy(d, buffer(s.data(), x)));
}
BEAST_EXPECT(ba.size() == x);
BEAST_EXPECT(ba.max_size() == sizeof(buf) - x);
BEAST_EXPECT(buffer_size(ba.data()) == ba.size());
{
auto d = ba.prepare(x);
BEAST_EXPECT(buffer_size(d) == x);
}
{
auto d = ba.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
{
auto d = ba.prepare(z);
BEAST_EXPECT(buffer_size(d) == z);
}
{
auto d = ba.prepare(y);
BEAST_EXPECT(buffer_size(d) == y);
ba.commit(buffer_copy(d, buffer(s.data()+x, y)));
}
ba.commit(1);
BEAST_EXPECT(ba.size() == x + y);
BEAST_EXPECT(ba.max_size() == sizeof(buf) - (x + y));
BEAST_EXPECT(buffer_size(ba.data()) == ba.size());
{
auto d = ba.prepare(x);
BEAST_EXPECT(buffer_size(d) == x);
}
{
auto d = ba.prepare(y);
BEAST_EXPECT(buffer_size(d) == y);
}
{
auto d = ba.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
{
auto d = ba.prepare(z); BEAST_EXPECT(buffer_size(d) == z);
ba.commit(buffer_copy(d, buffer(s.data()+x+y, z)));
}
ba.commit(2);
BEAST_EXPECT(ba.size() == x + y + z);
BEAST_EXPECT(ba.max_size() == 0);
BEAST_EXPECT(buffer_size(ba.data()) == ba.size());
BEAST_EXPECT(to_string(ba.data()) == s);
ba.consume(t);
{
auto d = ba.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
BEAST_EXPECT(to_string(ba.data()) == s.substr(t, std::string::npos));
ba.consume(u);
BEAST_EXPECT(to_string(ba.data()) == s.substr(t + u, std::string::npos));
ba.consume(v);
BEAST_EXPECT(to_string(ba.data()) == "");
ba.consume(1);
{
auto d = ba.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
try
{
ba.prepare(1);
fail();
}
catch(...)
{
pass();
}
}
}}}}}}
}
void testCommit()
{
using boost::asio::buffer_size;
{
using sb_type = boost::asio::streambuf;
sb_type sb;
buffers_adapter<
sb_type::mutable_buffers_type> ba(sb.prepare(3));
BEAST_EXPECT(buffer_size(ba.prepare(3)) == 3);
ba.commit(2);
BEAST_EXPECT(buffer_size(ba.data()) == 2);
}
{
using sb_type = beast::streambuf;
sb_type sb(2);
sb.prepare(3);
buffers_adapter<
sb_type::mutable_buffers_type> ba(sb.prepare(8));
BEAST_EXPECT(buffer_size(ba.prepare(8)) == 8);
ba.commit(2);
BEAST_EXPECT(buffer_size(ba.data()) == 2);
ba.consume(1);
ba.commit(6);
ba.consume(2);
BEAST_EXPECT(buffer_size(ba.data()) == 5);
ba.consume(5);
}
}
void run() override
{
testBuffersAdapter();
testCommit();
}
};
BEAST_DEFINE_TESTSUITE(buffers_adapter,core,beast);
} // beast

View File

@@ -0,0 +1,117 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/consuming_buffers.hpp>
#include "buffer_test.hpp"
#include <beast/core/to_string.hpp>
#include <beast/unit_test/suite.hpp>
#include <boost/asio/buffer.hpp>
#include <string>
namespace beast {
class consuming_buffers_test : public beast::unit_test::suite
{
public:
template<class Buffers1, class Buffers2>
static
bool
eq(Buffers1 const& lhs, Buffers2 const& rhs)
{
return to_string(lhs) == to_string(rhs);
}
template<class ConstBufferSequence>
void
expect_size(std::size_t n, ConstBufferSequence const& buffers)
{
BEAST_EXPECT(test::size_pre(buffers) == n);
BEAST_EXPECT(test::size_post(buffers) == n);
BEAST_EXPECT(test::size_rev_pre(buffers) == n);
BEAST_EXPECT(test::size_rev_post(buffers) == n);
}
void testMatrix()
{
using boost::asio::buffer;
using boost::asio::const_buffer;
char buf[12];
std::string const s = "Hello, world";
BEAST_EXPECT(s.size() == sizeof(buf));
buffer_copy(buffer(buf), buffer(s));
BEAST_EXPECT(to_string(buffer(buf)) == s);
for(std::size_t i = 1; i < 4; ++i) {
for(std::size_t j = 1; j < 4; ++j) {
for(std::size_t x = 1; x < 4; ++x) {
for(std::size_t y = 1; y < 4; ++y) {
std::size_t k = sizeof(buf) - (i + j);
std::size_t z = sizeof(buf) - (x + y);
{
std::array<const_buffer, 3> bs{{
const_buffer{&buf[0], i},
const_buffer{&buf[i], j},
const_buffer{&buf[i+j], k}}};
consuming_buffers<decltype(bs)> cb(bs);
BEAST_EXPECT(to_string(cb) == s);
expect_size(s.size(), cb);
cb.consume(0);
BEAST_EXPECT(eq(cb, consumed_buffers(bs, 0)));
BEAST_EXPECT(to_string(cb) == s);
expect_size(s.size(), cb);
cb.consume(x);
BEAST_EXPECT(to_string(cb) == s.substr(x));
BEAST_EXPECT(eq(cb, consumed_buffers(bs, x)));
cb.consume(y);
BEAST_EXPECT(to_string(cb) == s.substr(x+y));
BEAST_EXPECT(eq(cb, consumed_buffers(bs, x+y)));
cb.consume(z);
BEAST_EXPECT(to_string(cb) == "");
BEAST_EXPECT(eq(cb, consumed_buffers(bs, x+y+z)));
cb.consume(1);
BEAST_EXPECT(to_string(cb) == "");
BEAST_EXPECT(eq(cb, consumed_buffers(bs, x+y+z)));
}
}}}}
}
void testNullBuffers()
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
using boost::asio::null_buffers;
consuming_buffers<null_buffers> cb(
null_buffers{});
BEAST_EXPECT(buffer_size(cb) == 0);
consuming_buffers<null_buffers> cb2(
null_buffers{});
BEAST_EXPECT(buffer_copy(cb2, cb) == 0);
}
void testIterator()
{
using boost::asio::const_buffer;
std::array<const_buffer, 3> ba;
consuming_buffers<decltype(ba)> cb(ba);
std::size_t n = 0;
for(auto it = cb.end(); it != cb.begin(); --it)
++n;
BEAST_EXPECT(n == 3);
}
void run() override
{
testMatrix();
testNullBuffers();
testIterator();
}
};
BEAST_DEFINE_TESTSUITE(consuming_buffers,core,beast);
} // beast

View File

@@ -0,0 +1,143 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/dynabuf_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 dynabuf_readstream_test
: public unit_test::suite
, public test::enable_yield_to
{
using self = dynabuf_readstream_test;
public:
void testSpecialMembers()
{
using socket_type = boost::asio::ip::tcp::socket;
boost::asio::io_service ios;
{
dynabuf_readstream<socket_type, streambuf> srs(ios);
dynabuf_readstream<socket_type, streambuf> srs2(std::move(srs));
srs = std::move(srs2);
BEAST_EXPECT(&srs.get_io_service() == &ios);
BEAST_EXPECT(&srs.get_io_service() == &srs2.get_io_service());
}
{
socket_type sock(ios);
dynabuf_readstream<socket_type&, streambuf> srs(sock);
dynabuf_readstream<socket_type&, streambuf> srs2(std::move(srs));
}
}
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!");
dynabuf_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)
{
BEAST_EXPECT(s == "Hello, world!");
break;
}
}
BEAST_EXPECT(n < limit);
for(n = 0; n < limit; ++n)
{
test::fail_stream<
test::string_stream> fs(n, ios_, ", world!");
dynabuf_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)
{
BEAST_EXPECT(s == "Hello, world!");
break;
}
}
BEAST_EXPECT(n < limit);
for(n = 0; n < limit; ++n)
{
test::fail_stream<
test::string_stream> fs(n, ios_, ", world!");
dynabuf_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)
{
BEAST_EXPECT(s == "Hello, world!");
break;
}
}
BEAST_EXPECT(n < limit);
for(n = 0; n < limit; ++n)
{
test::fail_stream<
test::string_stream> fs(n, ios_, ", world!");
dynabuf_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)
{
BEAST_EXPECT(s == "Hello, world!");
break;
}
}
BEAST_EXPECT(n < limit);
}
void run() override
{
testSpecialMembers();
yield_to(std::bind(&self::testRead,
this, std::placeholders::_1));
}
};
BEAST_DEFINE_TESTSUITE(dynabuf_readstream,core,beast);
} // beast

View File

@@ -0,0 +1,94 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/detail/empty_base_optimization.hpp>
#include <beast/unit_test/suite.hpp>
namespace beast {
namespace detail {
class empty_base_optimization_test
: public beast::unit_test::suite
{
public:
template<class T>
class test1
: private empty_base_optimization<T>
{
using Base = empty_base_optimization<T>;
void* m_p;
public:
explicit test1 (T const& t)
: Base (t)
{}
T& member() {return Base::member();}
T const& member() const {return Base::member();}
};
template<class T>
class test2
{
void* m_p;
T m_t;
public:
explicit test2 (T const& t)
: m_t (t)
{}
T& member() {return m_t;}
T const& member() const {return m_t;}
};
struct Empty
{
operator bool() {return true;}
};
static
bool
test_one()
{
test1<int> t1(1);
test2<int> t2(2);
static_assert(sizeof(t1) == sizeof(t2), "don't optimize for int");
if(t1.member() != 1)
return false;
if(t2.member() != 2)
return false;
return true;
}
static
bool
test_two()
{
test1<Empty> t1((Empty()));
test2<Empty> t2((Empty()));
static_assert(sizeof(t1) < sizeof(t2), "do optimize for Empty");
if(t1.member() != true)
return false;
if(t2.member() != true)
return false;
return true;
}
void
run()
{
BEAST_EXPECT(test_one());
BEAST_EXPECT(test_two());
pass();
}
};
BEAST_DEFINE_TESTSUITE(empty_base_optimization,core,beast);
} // detail
} // beast

View File

@@ -0,0 +1,9 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/error.hpp>

View File

@@ -0,0 +1,88 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/detail/get_lowest_layer.hpp>
#include <beast/unit_test/suite.hpp>
#include <type_traits>
namespace beast {
namespace detail {
class get_lowest_layer_test
: public beast::unit_test::suite
{
public:
struct F1
{
};
struct F2
{
};
template<class F>
struct F3
{
using next_layer_type =
typename std::remove_reference<F>::type;
using lowest_layer_type = typename
get_lowest_layer<next_layer_type>::type;
};
template<class F>
struct F4
{
using next_layer_type =
typename std::remove_reference<F>::type;
using lowest_layer_type = typename
get_lowest_layer<next_layer_type>::type;
};
void
run()
{
static_assert(! has_lowest_layer<F1>::value, "");
static_assert(! has_lowest_layer<F2>::value, "");
static_assert(has_lowest_layer<F3<F1>>::value, "");
static_assert(has_lowest_layer<F4<F3<F2>>>::value, "");
static_assert(std::is_same<
get_lowest_layer<F1>::type, F1>::value, "");
static_assert(std::is_same<
get_lowest_layer<F2>::type, F2>::value, "");
static_assert(std::is_same<
get_lowest_layer<F3<F1>>::type, F1>::value, "");
static_assert(std::is_same<
get_lowest_layer<F3<F2>>::type, F2>::value, "");
static_assert(std::is_same<
get_lowest_layer<F4<F1>>::type, F1>::value, "");
static_assert(std::is_same<
get_lowest_layer<F4<F2>>::type, F2>::value, "");
static_assert(std::is_same<
get_lowest_layer<F4<F3<F1>>>::type, F1>::value, "");
static_assert(std::is_same<
get_lowest_layer<F4<F3<F2>>>::type, F2>::value, "");
pass();
}
};
BEAST_DEFINE_TESTSUITE(get_lowest_layer,core,beast);
} // detail
} // beast

View File

@@ -0,0 +1,28 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/to_string.hpp>
#include <beast/unit_test/suite.hpp>
#include <boost/asio/buffer.hpp>
namespace beast {
class to_string_test : public beast::unit_test::suite
{
public:
void run()
{
BEAST_EXPECT(to_string(boost::asio::const_buffers_1("x", 1)) == "x");
}
};
BEAST_DEFINE_TESTSUITE(to_string,core,beast);
} // beast

View File

@@ -0,0 +1,23 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/handler_concepts.hpp>
namespace beast {
namespace {
struct T
{
void operator()(int);
};
}
static_assert(is_CompletionHandler<T, void(int)>::value, "");
static_assert(! is_CompletionHandler<T, void(void)>::value, "");
} // beast

View File

@@ -0,0 +1,9 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/placeholders.hpp>

View File

@@ -0,0 +1,122 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/prepare_buffers.hpp>
#include <beast/core/consuming_buffers.hpp>
#include <beast/unit_test/suite.hpp>
#include <boost/asio/buffer.hpp>
#include <string>
namespace beast {
class prepare_buffers_test : public beast::unit_test::suite
{
public:
template<class ConstBufferSequence>
static
std::string
to_string(ConstBufferSequence const& bs)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
std::string s;
s.reserve(buffer_size(bs));
for(auto const& b : bs)
s.append(buffer_cast<char const*>(b),
buffer_size(b));
return s;
}
template<class BufferType>
void testMatrix()
{
using boost::asio::buffer_size;
std::string s = "Hello, world";
BEAST_EXPECT(s.size() == 12);
for(std::size_t x = 1; x < 4; ++x) {
for(std::size_t y = 1; y < 4; ++y) {
std::size_t z = s.size() - (x + y);
{
std::array<BufferType, 3> bs{{
BufferType{&s[0], x},
BufferType{&s[x], y},
BufferType{&s[x+y], z}}};
for(std::size_t i = 0; i <= s.size() + 1; ++i)
{
auto pb = prepare_buffers(i, bs);
BEAST_EXPECT(to_string(pb) == s.substr(0, i));
auto pb2 = pb;
BEAST_EXPECT(to_string(pb2) == to_string(pb));
pb = prepare_buffers(0, bs);
pb2 = pb;
BEAST_EXPECT(buffer_size(pb2) == 0);
pb2 = prepare_buffers(i, bs);
BEAST_EXPECT(to_string(pb2) == s.substr(0, i));
}
}
}}
}
void testNullBuffers()
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
using boost::asio::null_buffers;
auto pb0 = prepare_buffers(0, null_buffers{});
BEAST_EXPECT(buffer_size(pb0) == 0);
auto pb1 = prepare_buffers(1, null_buffers{});
BEAST_EXPECT(buffer_size(pb1) == 0);
BEAST_EXPECT(buffer_copy(pb0, pb1) == 0);
using pb_type = decltype(pb0);
consuming_buffers<pb_type> cb(pb0);
BEAST_EXPECT(buffer_size(cb) == 0);
BEAST_EXPECT(buffer_copy(cb, pb1) == 0);
cb.consume(1);
BEAST_EXPECT(buffer_size(cb) == 0);
BEAST_EXPECT(buffer_copy(cb, pb1) == 0);
auto pbc = prepare_buffers(2, cb);
BEAST_EXPECT(buffer_size(pbc) == 0);
BEAST_EXPECT(buffer_copy(pbc, cb) == 0);
}
void testIterator()
{
using boost::asio::buffer_size;
using boost::asio::const_buffer;
char b[3];
std::array<const_buffer, 3> bs{{
const_buffer{&b[0], 1},
const_buffer{&b[1], 1},
const_buffer{&b[2], 1}}};
auto pb = prepare_buffers(2, bs);
std::size_t n = 0;
for(auto it = pb.end(); it != pb.begin(); --it)
{
decltype(pb)::const_iterator it2(std::move(it));
BEAST_EXPECT(buffer_size(*it2) == 1);
it = std::move(it2);
++n;
}
BEAST_EXPECT(n == 2);
}
void run() override
{
testMatrix<boost::asio::const_buffer>();
testMatrix<boost::asio::mutable_buffer>();
testNullBuffers();
testIterator();
}
};
BEAST_DEFINE_TESTSUITE(prepare_buffers,core,beast);
} // beast

View File

@@ -0,0 +1,80 @@
//
// 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/core/detail/sha1.hpp>
#include <beast/unit_test/suite.hpp>
#include <array>
namespace beast {
namespace detail {
class sha1_test : public beast::unit_test::suite
{
public:
static
inline
std::uint8_t
unhex(char c)
{
if(c >= '0' && c <= '9')
return c - '0';
if(c >= 'a' && c <= 'f')
return c - 'a' + 10;
if(c >= 'A' && c <= 'F')
return c - 'A' + 10;
throw std::invalid_argument("not a hex digit");
}
static
std::string
unhex(std::string const& in)
{
std::string out;
out.reserve(in.size() / 2);
if(in.size() % 2)
throw std::domain_error("invalid hex string");
for(std::size_t i = 0; i < in.size(); i += 2)
out.push_back(
(unhex(in[i])<<4) + unhex(in[i+1]));
return out;
}
void
check(std::string const& message, std::string const& answer)
{
std::string digest;
digest = unhex(answer);
sha1_context ctx;
std::string result;
result.resize(sha1_context::digest_size);
init(ctx);
update(ctx, message.data(), message.size());
finish(ctx, &result[0]);
BEAST_EXPECT(result == digest);
}
void
run()
{
// http://www.di-mgt.com.au/sha_testvectors.html
//
check("abc",
"a9993e36" "4706816a" "ba3e2571" "7850c26c" "9cd0d89d");
check("",
"da39a3ee" "5e6b4b0d" "3255bfef" "95601890" "afd80709");
check("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
"84983e44" "1c3bd26e" "baae4aa1" "f95129e5" "e54670f1");
check("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
"a49b2446" "a02c645b" "f419f995" "b6709125" "3a04a259");
}
};
BEAST_DEFINE_TESTSUITE(sha1,core,beast);
} // test
} // beast

View File

@@ -0,0 +1,206 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/static_streambuf.hpp>
#include <beast/unit_test/suite.hpp>
#include <boost/asio/buffer.hpp>
#include <string>
namespace beast {
class static_streambuf_test : public beast::unit_test::suite
{
public:
template<class ConstBufferSequence>
static
std::string
to_string(ConstBufferSequence const& bs)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
std::string s;
s.reserve(buffer_size(bs));
for(auto const& b : bs)
s.append(buffer_cast<char const*>(b),
buffer_size(b));
return s;
}
void testStaticStreambuf()
{
using boost::asio::buffer;
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
char buf[12];
std::string const s = "Hello, world";
BEAST_EXPECT(s.size() == sizeof(buf));
for(std::size_t i = 1; i < 4; ++i) {
for(std::size_t j = 1; j < 4; ++j) {
for(std::size_t x = 1; x < 4; ++x) {
for(std::size_t y = 1; y < 4; ++y) {
for(std::size_t t = 1; t < 4; ++ t) {
for(std::size_t u = 1; u < 4; ++ u) {
std::size_t z = sizeof(buf) - (x + y);
std::size_t v = sizeof(buf) - (t + u);
{
std::memset(buf, 0, sizeof(buf));
static_streambuf_n<sizeof(buf)> ba;
{
auto d = ba.prepare(z);
BEAST_EXPECT(buffer_size(d) == z);
}
{
auto d = ba.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
{
auto d = ba.prepare(y);
BEAST_EXPECT(buffer_size(d) == y);
}
{
auto d = ba.prepare(x);
BEAST_EXPECT(buffer_size(d) == x);
ba.commit(buffer_copy(d, buffer(s.data(), x)));
}
BEAST_EXPECT(ba.size() == x);
BEAST_EXPECT(buffer_size(ba.data()) == ba.size());
{
auto d = ba.prepare(x);
BEAST_EXPECT(buffer_size(d) == x);
}
{
auto d = ba.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
{
auto d = ba.prepare(z);
BEAST_EXPECT(buffer_size(d) == z);
}
{
auto d = ba.prepare(y);
BEAST_EXPECT(buffer_size(d) == y);
ba.commit(buffer_copy(d, buffer(s.data()+x, y)));
}
ba.commit(1);
BEAST_EXPECT(ba.size() == x + y);
BEAST_EXPECT(buffer_size(ba.data()) == ba.size());
{
auto d = ba.prepare(x);
BEAST_EXPECT(buffer_size(d) == x);
}
{
auto d = ba.prepare(y);
BEAST_EXPECT(buffer_size(d) == y);
}
{
auto d = ba.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
{
auto d = ba.prepare(z);
BEAST_EXPECT(buffer_size(d) == z);
ba.commit(buffer_copy(d, buffer(s.data()+x+y, z)));
}
ba.commit(2);
BEAST_EXPECT(ba.size() == x + y + z);
BEAST_EXPECT(buffer_size(ba.data()) == ba.size());
BEAST_EXPECT(to_string(ba.data()) == s);
ba.consume(t);
{
auto d = ba.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
BEAST_EXPECT(to_string(ba.data()) == s.substr(t, std::string::npos));
ba.consume(u);
BEAST_EXPECT(to_string(ba.data()) == s.substr(t + u, std::string::npos));
ba.consume(v);
BEAST_EXPECT(to_string(ba.data()) == "");
ba.consume(1);
{
auto d = ba.prepare(0);
BEAST_EXPECT(buffer_size(d) == 0);
}
try
{
ba.prepare(1);
fail();
}
catch(...)
{
pass();
}
}
}}}}}}
}
void testIterators()
{
static_streambuf_n<2> ba;
{
auto mb = ba.prepare(2);
std::size_t n;
n = 0;
for(auto it = mb.begin();
it != mb.end(); it++)
++n;
BEAST_EXPECT(n == 1);
mb = ba.prepare(2);
n = 0;
for(auto it = mb.begin();
it != mb.end(); ++it)
++n;
BEAST_EXPECT(n == 1);
mb = ba.prepare(2);
n = 0;
for(auto it = mb.end();
it != mb.begin(); it--)
++n;
BEAST_EXPECT(n == 1);
mb = ba.prepare(2);
n = 0;
for(auto it = mb.end();
it != mb.begin(); --it)
++n;
BEAST_EXPECT(n == 1);
}
ba.prepare(2);
ba.commit(1);
std::size_t n;
n = 0;
for(auto it = ba.data().begin();
it != ba.data().end(); it++)
++n;
BEAST_EXPECT(n == 1);
n = 0;
for(auto it = ba.data().begin();
it != ba.data().end(); ++it)
++n;
BEAST_EXPECT(n == 1);
n = 0;
for(auto it = ba.data().end();
it != ba.data().begin(); it--)
++n;
BEAST_EXPECT(n == 1);
n = 0;
for(auto it = ba.data().end();
it != ba.data().begin(); --it)
++n;
BEAST_EXPECT(n == 1);
}
void run() override
{
testStaticStreambuf();
testIterators();
}
};
BEAST_DEFINE_TESTSUITE(static_streambuf,core,beast);
} // beastp

View File

@@ -0,0 +1,263 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/static_string.hpp>
#include <beast/unit_test/suite.hpp>
namespace beast {
class static_string_test : public beast::unit_test::suite
{
public:
void testMembers()
{
using str1 = static_string<1>;
using str2 = static_string<2>;
{
str1 s1;
BEAST_EXPECT(s1 == "");
BEAST_EXPECT(s1.empty());
BEAST_EXPECT(s1.size() == 0);
BEAST_EXPECT(s1.max_size() == 1);
BEAST_EXPECT(s1.capacity() == 1);
BEAST_EXPECT(s1.begin() == s1.end());
BEAST_EXPECT(s1.cbegin() == s1.cend());
BEAST_EXPECT(s1.rbegin() == s1.rend());
BEAST_EXPECT(s1.crbegin() == s1.crend());
try
{
BEAST_EXPECT(s1.at(0) == 0);
fail();
}
catch(std::exception const&)
{
pass();
}
BEAST_EXPECT(s1.data()[0] == 0);
BEAST_EXPECT(*s1.c_str() == 0);
BEAST_EXPECT(std::distance(s1.begin(), s1.end()) == 0);
BEAST_EXPECT(std::distance(s1.cbegin(), s1.cend()) == 0);
BEAST_EXPECT(std::distance(s1.rbegin(), s1.rend()) == 0);
BEAST_EXPECT(std::distance(s1.crbegin(), s1.crend()) == 0);
BEAST_EXPECT(s1.compare(s1) == 0);
BEAST_EXPECT(s1.to_string() == std::string{});
}
{
str1 const s1;
BEAST_EXPECT(s1 == "");
BEAST_EXPECT(s1.empty());
BEAST_EXPECT(s1.size() == 0);
BEAST_EXPECT(s1.max_size() == 1);
BEAST_EXPECT(s1.capacity() == 1);
BEAST_EXPECT(s1.begin() == s1.end());
BEAST_EXPECT(s1.cbegin() == s1.cend());
BEAST_EXPECT(s1.rbegin() == s1.rend());
BEAST_EXPECT(s1.crbegin() == s1.crend());
try
{
BEAST_EXPECT(s1.at(0) == 0);
fail();
}
catch(std::exception const&)
{
pass();
}
BEAST_EXPECT(s1.data()[0] == 0);
BEAST_EXPECT(*s1.c_str() == 0);
BEAST_EXPECT(std::distance(s1.begin(), s1.end()) == 0);
BEAST_EXPECT(std::distance(s1.cbegin(), s1.cend()) == 0);
BEAST_EXPECT(std::distance(s1.rbegin(), s1.rend()) == 0);
BEAST_EXPECT(std::distance(s1.crbegin(), s1.crend()) == 0);
BEAST_EXPECT(s1.compare(s1) == 0);
BEAST_EXPECT(s1.to_string() == std::string{});
}
{
str1 s1;
str1 s2("x");
BEAST_EXPECT(s2 == "x");
BEAST_EXPECT(s2[0] == 'x');
BEAST_EXPECT(s2.at(0) == 'x');
BEAST_EXPECT(s2.front() == 'x');
BEAST_EXPECT(s2.back() == 'x');
str1 const s3(s2);
BEAST_EXPECT(s3 == "x");
BEAST_EXPECT(s3[0] == 'x');
BEAST_EXPECT(s3.at(0) == 'x');
BEAST_EXPECT(s3.front() == 'x');
BEAST_EXPECT(s3.back() == 'x');
s2 = "y";
BEAST_EXPECT(s2 == "y");
BEAST_EXPECT(s3 == "x");
s1 = s2;
BEAST_EXPECT(s1 == "y");
s1.clear();
BEAST_EXPECT(s1.empty());
BEAST_EXPECT(s1.size() == 0);
}
{
str2 s1("x");
str1 s2(s1);
BEAST_EXPECT(s2 == "x");
str1 s3;
s3 = s2;
BEAST_EXPECT(s3 == "x");
s1 = "xy";
BEAST_EXPECT(s1.size() == 2);
BEAST_EXPECT(s1[0] == 'x');
BEAST_EXPECT(s1[1] == 'y');
BEAST_EXPECT(s1.at(0) == 'x');
BEAST_EXPECT(s1.at(1) == 'y');
BEAST_EXPECT(s1.front() == 'x');
BEAST_EXPECT(s1.back() == 'y');
auto const s4 = s1;
BEAST_EXPECT(s4[0] == 'x');
BEAST_EXPECT(s4[1] == 'y');
BEAST_EXPECT(s4.at(0) == 'x');
BEAST_EXPECT(s4.at(1) == 'y');
BEAST_EXPECT(s4.front() == 'x');
BEAST_EXPECT(s4.back() == 'y');
try
{
s3 = s1;
fail();
}
catch(std::exception const&)
{
pass();
}
try
{
str1 s5(s1);
fail();
}
catch(std::exception const&)
{
pass();
}
}
{
str1 s1("x");
str2 s2;
s2 = s1;
try
{
s1.resize(2);
fail();
}
catch(std::length_error const&)
{
pass();
}
}
pass();
}
void testCompare()
{
using str1 = static_string<1>;
using str2 = static_string<2>;
{
str1 s1;
str2 s2;
s1 = "1";
s2 = "22";
BEAST_EXPECT(s1.compare(s2) < 0);
BEAST_EXPECT(s2.compare(s1) > 0);
BEAST_EXPECT(s1 < "10");
BEAST_EXPECT(s2 > "1");
BEAST_EXPECT("10" > s1);
BEAST_EXPECT("1" < s2);
BEAST_EXPECT(s1 < "20");
BEAST_EXPECT(s2 > "1");
BEAST_EXPECT(s2 > "2");
}
{
str2 s1("x");
str2 s2("x");
BEAST_EXPECT(s1 == s2);
BEAST_EXPECT(s1 <= s2);
BEAST_EXPECT(s1 >= s2);
BEAST_EXPECT(! (s1 < s2));
BEAST_EXPECT(! (s1 > s2));
BEAST_EXPECT(! (s1 != s2));
}
{
str1 s1("x");
str2 s2("x");
BEAST_EXPECT(s1 == s2);
BEAST_EXPECT(s1 <= s2);
BEAST_EXPECT(s1 >= s2);
BEAST_EXPECT(! (s1 < s2));
BEAST_EXPECT(! (s1 > s2));
BEAST_EXPECT(! (s1 != s2));
}
{
str2 s("x");
BEAST_EXPECT(s == "x");
BEAST_EXPECT(s <= "x");
BEAST_EXPECT(s >= "x");
BEAST_EXPECT(! (s < "x"));
BEAST_EXPECT(! (s > "x"));
BEAST_EXPECT(! (s != "x"));
BEAST_EXPECT("x" == s);
BEAST_EXPECT("x" <= s);
BEAST_EXPECT("x" >= s);
BEAST_EXPECT(! ("x" < s));
BEAST_EXPECT(! ("x" > s));
BEAST_EXPECT(! ("x" != s));
}
{
str2 s("x");
BEAST_EXPECT(s <= "y");
BEAST_EXPECT(s < "y");
BEAST_EXPECT(s != "y");
BEAST_EXPECT(! (s == "y"));
BEAST_EXPECT(! (s >= "y"));
BEAST_EXPECT(! (s > "x"));
BEAST_EXPECT("y" >= s);
BEAST_EXPECT("y" > s);
BEAST_EXPECT("y" != s);
BEAST_EXPECT(! ("y" == s));
BEAST_EXPECT(! ("y" <= s));
BEAST_EXPECT(! ("y" < s));
}
{
str1 s1("x");
str2 s2("y");
BEAST_EXPECT(s1 <= s2);
BEAST_EXPECT(s1 < s2);
BEAST_EXPECT(s1 != s2);
BEAST_EXPECT(! (s1 == s2));
BEAST_EXPECT(! (s1 >= s2));
BEAST_EXPECT(! (s1 > s2));
}
{
str1 s1("x");
str2 s2("xx");
BEAST_EXPECT(s1 < s2);
BEAST_EXPECT(s2 > s1);
}
{
str1 s1("x");
str2 s2("yy");
BEAST_EXPECT(s1 < s2);
BEAST_EXPECT(s2 > s1);
}
}
void run() override
{
testMembers();
testCompare();
}
};
BEAST_DEFINE_TESTSUITE(static_string,core,beast);
} // beast

View File

@@ -0,0 +1,30 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/stream_concepts.hpp>
#include <boost/asio/ip/tcp.hpp>
namespace beast {
using stream_type = boost::asio::ip::tcp::socket;
static_assert(has_get_io_service<stream_type>::value, "");
static_assert(is_AsyncReadStream<stream_type>::value, "");
static_assert(is_AsyncWriteStream<stream_type>::value, "");
static_assert(is_AsyncStream<stream_type>::value, "");
static_assert(is_SyncReadStream<stream_type>::value, "");
static_assert(is_SyncWriteStream<stream_type>::value, "");
static_assert(is_SyncStream<stream_type>::value, "");
static_assert(! has_get_io_service<int>::value, "");
static_assert(! is_AsyncReadStream<int>::value, "");
static_assert(! is_AsyncWriteStream<int>::value, "");
static_assert(! is_SyncReadStream<int>::value, "");
static_assert(! is_SyncWriteStream<int>::value, "");
} // beast

View File

@@ -0,0 +1,9 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/streambuf.hpp>

View File

@@ -0,0 +1,10 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/to_string.hpp>

View File

@@ -0,0 +1,36 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/core/write_dynabuf.hpp>
#include <beast/core/streambuf.hpp>
#include <beast/unit_test/suite.hpp>
namespace beast {
class write_dynabuf_test : public beast::unit_test::suite
{
public:
void run() override
{
streambuf sb;
std::string s;
write(sb, boost::asio::const_buffer{"", 0});
write(sb, boost::asio::mutable_buffer{nullptr, 0});
write(sb, boost::asio::null_buffers{});
write(sb, boost::asio::const_buffers_1{"", 0});
write(sb, boost::asio::mutable_buffers_1{nullptr, 0});
write(sb, s);
write(sb, 23);
pass();
}
};
BEAST_DEFINE_TESTSUITE(write_dynabuf,core,beast);
} // beast

9
src/beast/test/http.cpp Normal file
View File

@@ -0,0 +1,9 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/http.hpp>

View File

@@ -0,0 +1,49 @@
# Part of Beast
GroupSources(extras/beast extras)
GroupSources(include/beast beast)
GroupSources(test/http "/")
add_executable (http-tests
${BEAST_INCLUDES}
${EXTRAS_INCLUDES}
message_fuzz.hpp
fail_parser.hpp
../../extras/beast/unit_test/main.cpp
basic_dynabuf_body.cpp
basic_headers.cpp
basic_parser_v1.cpp
body_type.cpp
concepts.cpp
empty_body.cpp
headers.cpp
message.cpp
message_v1.cpp
parse_error.cpp
parser_v1.cpp
read.cpp
reason.cpp
resume_context.cpp
rfc7230.cpp
streambuf_body.cpp
string_body.cpp
write.cpp
chunk_encode.cpp
)
if (NOT WIN32)
target_link_libraries(http-tests ${Boost_LIBRARIES} Threads::Threads)
endif()
add_executable (bench-tests
${BEAST_INCLUDES}
${EXTRAS_INCLUDES}
nodejs_parser.hpp
../../extras/beast/unit_test/main.cpp
nodejs_parser.cpp
parser_bench.cpp
)
if (NOT WIN32)
target_link_libraries(bench-tests ${Boost_LIBRARIES})
endif()

View File

@@ -0,0 +1,9 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/http/basic_dynabuf_body.hpp>

View File

@@ -0,0 +1,95 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/http/basic_headers.hpp>
#include <beast/unit_test/suite.hpp>
namespace beast {
namespace http {
class basic_headers_test : public beast::unit_test::suite
{
public:
template<class Allocator>
using bha = basic_headers<Allocator>;
using bh = basic_headers<std::allocator<char>>;
template<class Allocator>
static
void
fill(std::size_t n, basic_headers<Allocator>& h)
{
for(std::size_t i = 1; i<= n; ++i)
h.insert(std::to_string(i), i);
}
template<class U, class V>
static
void
self_assign(U& u, V&& v)
{
u = std::forward<V>(v);
}
void testHeaders()
{
bh h1;
BEAST_EXPECT(h1.empty());
fill(1, h1);
BEAST_EXPECT(h1.size() == 1);
bh h2;
h2 = h1;
BEAST_EXPECT(h2.size() == 1);
h2.insert("2", "2");
BEAST_EXPECT(std::distance(h2.begin(), h2.end()) == 2);
h1 = std::move(h2);
BEAST_EXPECT(h1.size() == 2);
BEAST_EXPECT(h2.size() == 0);
bh h3(std::move(h1));
BEAST_EXPECT(h3.size() == 2);
BEAST_EXPECT(h1.size() == 0);
self_assign(h3, std::move(h3));
BEAST_EXPECT(h3.size() == 2);
BEAST_EXPECT(h2.erase("Not-Present") == 0);
}
void testRFC2616()
{
bh h;
h.insert("a", "w");
h.insert("a", "x");
h.insert("aa", "y");
h.insert("b", "z");
BEAST_EXPECT(h.count("a") == 2);
}
void testErase()
{
bh h;
h.insert("a", "w");
h.insert("a", "x");
h.insert("aa", "y");
h.insert("b", "z");
BEAST_EXPECT(h.size() == 4);
h.erase("a");
BEAST_EXPECT(h.size() == 2);
}
void run() override
{
testHeaders();
testRFC2616();
}
};
BEAST_DEFINE_TESTSUITE(basic_headers,http,beast);
} // http
} // beast

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/http/body_type.hpp>

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...);
BEAST_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

@@ -0,0 +1,9 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/http/concepts.hpp>

View File

@@ -0,0 +1,9 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/http/empty_body.hpp>

View File

@@ -0,0 +1,112 @@
//
// 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_TEST_FAIL_PARSER_HPP
#define BEAST_HTTP_TEST_FAIL_PARSER_HPP
#include <beast/http/basic_parser_v1.hpp>
#include <beast/test/fail_counter.hpp>
namespace beast {
namespace http {
template<bool isRequest>
class fail_parser
: public basic_parser_v1<isRequest, fail_parser<isRequest>>
{
test::fail_counter& fc_;
std::uint64_t content_length_ = no_content_length;
int body_rv_ = 0;
public:
std::string body;
template<class... Args>
explicit
fail_parser(test::fail_counter& fc, Args&&... args)
: fc_(fc)
{
}
void
on_body_rv(int rv)
{
body_rv_ = rv;
}
// valid on successful parse
std::uint64_t
content_length() const
{
return content_length_;
}
void on_start(error_code& ec)
{
fc_.fail(ec);
}
void on_method(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
void on_uri(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
void on_reason(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
void on_request(error_code& ec)
{
fc_.fail(ec);
}
void on_response(error_code& ec)
{
fc_.fail(ec);
}
void on_field(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
void on_value(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
int on_headers(std::uint64_t content_length, error_code& ec)
{
if(fc_.fail(ec))
return 0;
content_length_ = content_length;
return body_rv_;
}
void on_body(boost::string_ref const& s, error_code& ec)
{
if(fc_.fail(ec))
return;
body.append(s.data(), s.size());
}
void on_complete(error_code& ec)
{
fc_.fail(ec);
}
};
} // http
} // beast
#endif

View File

@@ -0,0 +1,9 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/http/headers.hpp>

View File

@@ -0,0 +1,161 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/http/message.hpp>
#include <beast/http/headers.hpp>
#include <beast/http/string_body.hpp>
#include <beast/unit_test/suite.hpp>
#include <type_traits>
namespace beast {
namespace http {
class message_test : public beast::unit_test::suite
{
public:
struct Arg1
{
bool moved = false;
Arg1() = default;
Arg1(Arg1&& other)
{
other.moved = true;
}
};
struct Arg2 { };
struct Arg3 { };
// default constructible Body
struct default_body
{
using value_type = std::string;
};
// 1-arg constructible Body
struct one_arg_body
{
struct value_type
{
explicit
value_type(Arg1 const&)
{
}
explicit
value_type(Arg1&& arg)
{
Arg1 arg_(std::move(arg));
}
};
};
// 2-arg constructible Body
struct two_arg_body
{
struct value_type
{
value_type(Arg1 const&, Arg2 const&)
{
}
};
};
void testConstruction()
{
static_assert(std::is_constructible<
message<true, default_body, headers>>::value, "");
static_assert(std::is_constructible<
message<true, one_arg_body, headers>, Arg1>::value, "");
static_assert(std::is_constructible<
message<true, one_arg_body, headers>, Arg1 const>::value, "");
static_assert(std::is_constructible<
message<true, one_arg_body, headers>, Arg1 const&>::value, "");
static_assert(std::is_constructible<
message<true, one_arg_body, headers>, Arg1&&>::value, "");
static_assert(! std::is_constructible<
message<true, one_arg_body, headers>>::value, "");
static_assert(std::is_constructible<
message<true, one_arg_body, headers>,
Arg1, headers::allocator_type>::value, "");
static_assert(std::is_constructible<
message<true, one_arg_body, headers>, std::piecewise_construct_t,
std::tuple<Arg1>>::value, "");
static_assert(std::is_constructible<
message<true, two_arg_body, headers>, std::piecewise_construct_t,
std::tuple<Arg1, Arg2>>::value, "");
static_assert(std::is_constructible<
message<true, two_arg_body, headers>, std::piecewise_construct_t,
std::tuple<Arg1, Arg2>, std::tuple<headers::allocator_type>>::value, "");
{
Arg1 arg1;
message<true, one_arg_body, headers>{std::move(arg1)};
BEAST_EXPECT(arg1.moved);
}
{
headers h;
h.insert("User-Agent", "test");
message<true, one_arg_body, headers> m{Arg1{}, h};
BEAST_EXPECT(h["User-Agent"] == "test");
BEAST_EXPECT(m.headers["User-Agent"] == "test");
}
{
headers h;
h.insert("User-Agent", "test");
message<true, one_arg_body, headers> m{Arg1{}, std::move(h)};
BEAST_EXPECT(! h.exists("User-Agent"));
BEAST_EXPECT(m.headers["User-Agent"] == "test");
}
}
void testSwap()
{
message<true, string_body, headers> m1;
message<true, string_body, headers> m2;
m1.url = "u";
m1.body = "1";
m1.headers.insert("h", "v");
m2.method = "G";
m2.body = "2";
swap(m1, m2);
BEAST_EXPECT(m1.method == "G");
BEAST_EXPECT(m2.method.empty());
BEAST_EXPECT(m1.url.empty());
BEAST_EXPECT(m2.url == "u");
BEAST_EXPECT(m1.body == "2");
BEAST_EXPECT(m2.body == "1");
BEAST_EXPECT(! m1.headers.exists("h"));
BEAST_EXPECT(m2.headers.exists("h"));
}
void run() override
{
testConstruction();
testSwap();
}
};
BEAST_DEFINE_TESTSUITE(message,http,beast);
} // http
} // beast

View File

@@ -0,0 +1,562 @@
//
// 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_TEST_MESSAGE_FUZZ_HPP
#define BEAST_HTTP_TEST_MESSAGE_FUZZ_HPP
#include <beast/core/write_dynabuf.hpp>
#include <beast/http/detail/basic_parser_v1.hpp>
#include <cstdint>
#include <random>
#include <string>
namespace beast {
namespace http {
template<class = void>
std::string
escaped_string(boost::string_ref const& s)
{
std::string out;
out.reserve(s.size());
char const* p = s.data();
while(p != s.end())
{
if(*p == '\r')
out.append("\\r");
else if(*p == '\n')
out.append("\\n");
else if(*p == '\t')
out.append("\\t");
else
out.append(p, 1);
++p;
}
return out;
}
// Produces random HTTP messages
//
template<class = void>
class message_fuzz_t
{
std::mt19937 rng_;
static
std::string
to_hex(std::size_t v)
{
if(! v)
return "0";
std::string s;
while(v > 0)
{
s.insert(s.begin(),
"0123456789abcdef"[v&0xf]);
v >>= 4;
}
return s;
}
public:
template<class UInt = std::size_t>
UInt
rand(std::size_t n)
{
return static_cast<UInt>(
std::uniform_int_distribution<
std::size_t>{0, n-1}(rng_));
}
std::string
method()
{
#if 0
// All IANA registered methods
static char const* const list[39] = {
"ACL", "BASELINE-CONTROL", "BIND", "CHECKIN", "CHECKOUT", "CONNECT",
"COPY", "DELETE", "GET", "HEAD", "LABEL", "LINK", "LOCK", "MERGE",
"MKACTIVITY", "MKCALENDAR", "MKCOL", "MKREDIRECTREF", "MKWORKSPACE",
"MOVE", "OPTIONS", "ORDERPATCH", "PATCH", "POST", "PRI", "PROPFIND",
"PROPPATCH", "PUT", "REBIND", "REPORT", "SEARCH", "TRACE", "UNBIND",
"UNCHECKOUT", "UNLINK", "UNLOCK", "UPDATE", "UPDATEREDIRECTREF",
"VERSION-CONTROL"
};
return list[rand(39)];
#else
// methods parsed by nodejs-http-parser
static char const* const list[33] = {
"ACL", "BIND", "CHECKOUT", "CONNECT", "COPY", "DELETE", "HEAD", "GET",
"LINK", "LOCK", "MERGE", "MKCOL", "MKCALENDAR", "MKACTIVITY", "M-SEARCH",
"MOVE", "NOTIFY", "OPTIONS", "PATCH", "POST", "PROPFIND", "PROPPATCH",
"PURGE", "PUT", "REBIND", "REPORT", "SEARCH", "SUBSCRIBE", "TRACE",
"UNBIND", "UNLINK", "UNLOCK", "UNSUBSCRIBE"
};
return list[rand(33)];
#endif
}
std::string
scheme()
{
static char const* const list[241] = {
"aaa", "aaas", "about", "acap", "acct", "acr", "adiumxtra", "afp", "afs",
"aim", "appdata", "apt", "attachment", "aw", "barion", "beshare", "bitcoin",
"blob", "bolo", "callto", "cap", "chrome", "chrome-extension", "cid",
"coap", "coaps", "com-eventbrite-attendee", "content", "crid", "cvs",
"data", "dav", "dict", "dis", "dlna-playcontainer", "dlna-playsingle",
"dns", "dntp", "dtn", "dvb", "ed2k", "example", "facetime", "fax", "feed",
"feedready", "file", "filesystem", "finger", "fish", "ftp", "geo", "gg",
"git", "gizmoproject", "go", "gopher", "gtalk", "h323", "ham", "hcp",
"http", "https", "iax", "icap", "icon", "im", "imap", "info", "iotdisco",
"ipn", "ipp", "ipps", "irc", "irc6", "ircs", "iris", "iris.beep",
"iris.lwz", "iris.xpc", "iris.xpcs", "isostore", "itms", "jabber", "jar",
"jms", "keyparc", "lastfm", "ldap", "ldaps", "magnet", "mailserver",
"mailto", "maps", "market", "message", "mid", "mms",
"modem", "ms-access", "ms-drive-to", "ms-enrollment", "ms-excel",
"ms-getoffice", "ms-help", "ms-infopath", "ms-media-stream-id", "ms-project",
"ms-powerpoint", "ms-publisher", "ms-search-repair",
"ms-secondary-screen-controller", "ms-secondary-screen-setup",
"ms-settings", "ms-settings-airplanemode", "ms-settings-bluetooth",
"ms-settings-camera", "ms-settings-cellular", "ms-settings-cloudstorage",
"ms-settings-emailandaccounts", "ms-settings-language", "ms-settings-location",
"ms-settings-lock", "ms-settings-nfctransactions", "ms-settings-notifications",
"ms-settings-power", "ms-settings-privacy", "ms-settings-proximity",
"ms-settings-screenrotation", "ms-settings-wifi", "ms-settings-workplace",
"ms-spd", "ms-transit-to", "ms-visio", "ms-walk-to", "ms-word", "msnim",
"msrp", "msrps", "mtqp", "mumble", "mupdate", "mvn", "news", "nfs", "ni",
"nih", "nntp", "notes", "oid", "opaquelocktoken", "pack", "palm", "paparazzi",
"pkcs11", "platform", "pop", "pres", "prospero", "proxy", "psyc", "query",
"redis", "rediss", "reload", "res", "resource", "rmi", "rsync", "rtmfp",
"rtmp", "rtsp", "rtsps", "rtspu", "secondlife", "service", "session", "sftp",
"sgn", "shttp", "sieve", "sip", "sips", "skype", "smb", "sms", "smtp",
"snews", "snmp", "soap.beep", "soap.beeps", "soldat", "spotify", "ssh",
"steam", "stun", "stuns", "submit", "svn", "tag", "teamspeak", "tel",
"teliaeid", "telnet", "tftp", "things", "thismessage", "tip", "tn3270",
"tool", "turn", "turns", "tv", "udp", "unreal", "urn", "ut2004", "v-event",
"vemmi", "ventrilo", "videotex", "vnc", "view-source", "wais", "webcal",
"wpid", "ws", "wss", "wtai", "wyciwyg", "xcon", "xcon-userid", "xfire",
"xmlrpc.beep", "xmlrpc.beeps", "xmpp", "xri", "ymsgr", "z39.50", "z39.50r",
"z39.50s:"
};
return list[rand(241)];
}
std::string
pchar()
{
if(rand(4))
return std::string(1,
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
":@&=+$,"[rand(69)]);
std::string s = "%";
s += "0123456789abcdef"[rand(16)];
s += "0123456789abcdef"[rand(16)];
return s;
}
char
uric()
{
return 'a';
}
char
uric_no_slash()
{
return 'a';
}
std::string
param()
{
std::string s;
while(rand(2))
s += pchar();
return s;
}
std::string
query()
{
std::string s;
while(rand(2))
s += uric();
return s;
}
std::string
userinfo()
{
std::string s;
while(rand(2))
s += "a";
return s;
}
/* authority = server | reg_name
reg_name = 1*( unreserved | escaped | "$" | "," |
";" | ":" | "@" | "&" | "=" | "+" )
server = [ [ userinfo "@" ] hostport ]
userinfo = *( unreserved | escaped |
";" | ":" | "&" | "=" | "+" | "$" | "," )
hostport = host [ ":" port ]
host = hostname | IPv4address
hostname = *( domainlabel "." ) toplabel [ "." ]
domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
toplabel = alpha | alpha *( alphanum | "-" ) alphanum
IPv4address = 1*digit "." 1*digit "." 1*digit "." 1*digit
port = *digit
*/
std::string
server()
{
std::string s;
if(rand(2))
s += userinfo() + "@";
return s;
}
std::string
reg_name()
{
std::string s;
s = "a";
while(rand(2))
s += "a";
return s;
}
std::string
authority()
{
if(rand(2))
return server();
return reg_name();
}
std::string
opaque_part()
{
std::string s;
s += uric_no_slash();
while(rand(2))
s += uric();
return s;
}
/* abs_path = "/" path_segments
path_segments = segment *( "/" segment )
segment = *pchar *( ";" param )
param = *pchar
pchar = unreserved | escaped |
":" | "@" | "&" | "=" | "+" | "$" | ","
*/
std::string
abs_path()
{
std::string s = "/";
for(;;)
{
while(rand(2))
s += pchar();
while(rand(2))
s += ";" + param();
if(rand(2))
break;
s.append("/");
}
return s;
}
/* net_path = "//" authority [ abs_path ]
*/
std::string
net_path()
{
std::string s = "//";
s += authority();
if(rand(2))
s += abs_path();
return s;
}
/* absoluteURI = scheme ":" ( hier_part | opaque_part )
scheme = alpha *( alpha | digit | "+" | "-" | "." )
hier_part = ( net_path | abs_path ) [ "?" query ]
abs_path = "/" path_segments
query = *uric
opaque_part = uric_no_slash *uric
*/
std::string
abs_uri()
{
std::string s;
s = scheme() + ":";
if(rand(2))
{
if(rand(2))
s += net_path();
else
s += abs_path();
if(rand(2))
s += "?" + query();
}
else
{
s += opaque_part();
}
return s;
}
std::string
uri()
{
//switch(rand(4))
switch(1)
{
case 0: return abs_uri();
case 1: return abs_path();
case 2: return authority();
default:
case 3: break;
}
return "*";
}
std::string
token()
{
static char constexpr valid[78] =
"!#$%&\'*+-." "0123456789" "ABCDEFGHIJ" "KLMNOPQRST"
"UVWXYZ^_`a" "bcdefghijk" "lmnopqrstu" "vwxyz|~";
std::string s;
s.append(1, valid[rand(77)]);
while(rand(4))
s.append(1, valid[rand(77)]);
return s;
}
#if 0
std::string
uri()
{
static char constexpr alpha[63] =
"0123456789" "ABCDEFGHIJ" "KLMNOPQRST"
"UVWXYZabcd" "efghijklmn" "opqrstuvwx" "yz";
std::string s;
s = "/";
while(rand(4))
s.append(1, alpha[rand(62)]);
return s;
}
#endif
std::string
field()
{ static char const* const list[289] =
{
"A-IM",
"Accept", "Accept-Additions", "Accept-Charset", "Accept-Datetime", "Accept-Encoding",
"Accept-Features", "Accept-Language", "Accept-Patch", "Accept-Ranges", "Age", "Allow",
"ALPN", "Also-Control", "Alt-Svc", "Alt-Used", "Alternate-Recipient", "Alternates",
"Apply-To-Redirect-Ref", "Approved", "Archive", "Archived-At", "Article-Names",
"Article-Updates", "Authentication-Info", "Authentication-Results", "Authorization",
"Auto-Submitted", "Autoforwarded", "Autosubmitted", "Base", "Bcc", "Body", "C-Ext",
"C-Man", "C-Opt", "C-PEP", "C-PEP-Info", "Cache-Control",
"CalDAV-Timezones", "Cc", "Close", "Comments", /*"Connection",*/ "Content-Alternative",
"Content-Base", "Content-Description", "Content-Disposition", "Content-Duration",
"Content-Encoding", "Content-features", "Content-ID", "Content-Identifier",
"Content-Language", /*"Content-Length",*/ "Content-Location", "Content-MD5",
"Content-Range", "Content-Return", "Content-Script-Type", "Content-Style-Type",
"Content-Transfer-Encoding", "Content-Type", "Content-Version", "Control", "Conversion",
"Conversion-With-Loss", "Cookie", "Cookie2", "DASL", "DAV", "DL-Expansion-History", "Date",
"Date-Received", "Default-Style", "Deferred-Delivery", "Delivery-Date", "Delta-Base",
"Depth", "Derived-From", "Destination", "Differential-ID", "Digest",
"Discarded-X400-IPMS-Extensions", "Discarded-X400-MTS-Extensions", "Disclose-Recipients",
"Disposition-Notification-Options", "Disposition-Notification-To", "Distribution",
"DKIM-Signature", "Downgraded-Bcc", "Downgraded-Cc", "Downgraded-Disposition-Notification-To",
"Downgraded-Final-Recipient", "Downgraded-From", "Downgraded-In-Reply-To",
"Downgraded-Mail-From", "Downgraded-Message-Id", "Downgraded-Original-Recipient",
"Downgraded-Rcpt-To", "Downgraded-References", "Downgraded-Reply-To", "Downgraded-Resent-Bcc",
"Downgraded-Resent-Cc", "Downgraded-Resent-From", "Downgraded-Resent-Reply-To",
"Downgraded-Resent-Sender", "Downgraded-Resent-To", "Downgraded-Return-Path",
"Downgraded-Sender", "Downgraded-To", "Encoding", "Encrypted", "ETag", "Expect",
"Expires", "Expiry-Date", "Ext", "Followup-To", "Forwarded", "From",
"Generate-Delivery-Report", "GetProfile", "Hobareg", "Host", "HTTP2-Settings", "IM", "If",
"If-Match", "If-Modified-Since", "If-None-Match", "If-Range", "If-Schedule-Tag-Match",
"If-Unmodified-Since", "Importance", "In-Reply-To", "Incomplete-Copy", "Injection-Date",
"Injection-Info", "Keep-Alive", "Keywords", "Label", "Language", "Last-Modified",
"Latest-Delivery-Time", "Lines", "Link", "List-Archive", "List-Help", "List-ID",
"List-Owner", "List-Post", "List-Subscribe", "List-Unsubscribe", "Location", "Lock-Token",
"Man", "Max-Forwards", "Memento-Datetime", "Message-Context", "Message-ID", "Message-Type",
"Meter", "MIME-Version", "MMHS-Exempted-Address", "MMHS-Extended-Authorisation-Info",
"MMHS-Subject-Indicator-Codes", "MMHS-Handling-Instructions", "MMHS-Message-Instructions",
"MMHS-Codress-Message-Indicator", "MMHS-Originator-Reference", "MMHS-Primary-Precedence",
"MMHS-Copy-Precedence", "MMHS-Message-Type", "MMHS-Other-Recipients-Indicator-To",
"MMHS-Other-Recipients-Indicator-CC", "MMHS-Acp127-Message-Identifier", "MMHS-Originator-PLAD",
"MT-Priority", "Negotiate", "Newsgroups", "NNTP-Posting-Date", "NNTP-Posting-Host",
"Obsoletes", "Opt", "Ordering-Type", "Organization", "Origin",
"Original-Encoded-Information-Types", "Original-From", "Original-Message-ID",
"Original-Recipient", "Original-Sender", "Originator-Return-Address", "Original-Subject",
"Overwrite", "P3P", "Path", "PEP", "PICS-Label", "Pep-Info", "Position", "Posting-Version",
"Pragma", "Prefer", "Preference-Applied", "Prevent-NonDelivery-Report", "Priority",
"ProfileObject", "Protocol", "Protocol-Info", "Protocol-Query", "Protocol-Request",
"Proxy-Authenticate", "Proxy-Authentication-Info", "Proxy-Authorization", "Proxy-Features",
"Proxy-Instruction", "Public", "Public-Key-Pins", "Public-Key-Pins-Report-Only", "Range",
"Received", "Received-SPF", "Redirect-Ref", "References", "Referer", "Relay-Version",
"Reply-By", "Reply-To", "Require-Recipient-Valid-Since", "Resent-Bcc", "Resent-Cc",
"Resent-Date", "Resent-From", "Resent-Message-ID", "Resent-Reply-To", "Resent-Sender",
"Resent-To", "Retry-After", "Return-Path", "Safe", "Schedule-Reply", "Schedule-Tag",
"Sec-WebSocket-Accept", "Sec-WebSocket-Extensions", "Sec-WebSocket-Key",
"Sec-WebSocket-Protocol", "Sec-WebSocket-Version", "Security-Scheme", "See-Also", "Sender",
"Sensitivity", "Server", "Set-Cookie", "Set-Cookie2",
"SetProfile", "SLUG", "SoapAction", "Solicitation", "Status-URI", "Strict-Transport-Security",
"Subject", "Summary", "Supersedes", "Surrogate-Capability", "Surrogate-Control", "TCN",
"TE", "Timeout", "To", "Trailer", /*"Transfer-Encoding",*/ "URI", /*"Upgrade",*/ "User-Agent",
"Variant-Vary", "Vary", "VBR-Info", "Via", "WWW-Authenticate", "Want-Digest", "Warning",
"X400-Content-Identifier", "X400-Content-Return", "X400-Content-Type", "X400-MTS-Identifier",
"X400-Originator", "X400-Received", "X400-Recipients", "X400-Trace", "X-Frame-Options", "Xref"
};
return list[rand(289)];
}
std::string
text()
{
std::string s;
while(rand(3))
{
for(;;)
{
char c = rand<char>(256);
if(detail::is_text(c))
{
s.append(1, c);
break;
}
}
}
return s;
}
std::string
value()
{
std::string s;
while(rand(3))
{
if(rand(5))
{
s.append(text());
}
else
{
// LWS
if(! rand(4))
s.append("\r\n");
s.append(1, rand(2) ? ' ' : '\t');
while(rand(2))
s.append(1, rand(2) ? ' ' : '\t');
}
}
return s;
}
template<class DynamicBuffer>
void
headers(DynamicBuffer& db)
{
while(rand(6))
{
write(db, field());
write(db, rand(4) ? ": " : ":");
write(db, value());
write(db, "\r\n");
}
}
template<class DynamicBuffer>
void
body(DynamicBuffer& db)
{
if(! rand(4))
{
write(db, "Content-Length: 0\r\n\r\n");
return;
}
if(rand(2))
{
auto const len = rand(500);
write(db, "Content-Length: ", len, "\r\n\r\n");
for(auto const& b : db.prepare(len))
{
auto p = boost::asio::buffer_cast<char*>(b);
auto n = boost::asio::buffer_size(b);
while(n--)
*p++ = static_cast<char>(32 + rand(26+26+10+6));
}
db.commit(len);
}
else
{
auto len = rand(500);
write(db, "Transfer-Encoding: chunked\r\n\r\n");
while(len > 0)
{
auto n = std::min(1 + rand(300), len);
len -= n;
write(db, to_hex(n), "\r\n");
for(auto const& b : db.prepare(n))
{
auto p = boost::asio::buffer_cast<char*>(b);
auto m = boost::asio::buffer_size(b);
while(m--)
*p++ = static_cast<char>(32 + rand(26+26+10+6));
}
db.commit(n);
write(db, "\r\n");
}
write(db, "0\r\n\r\n");
}
}
template<class DynamicBuffer>
void
request(DynamicBuffer& db)
{
write(db, method(), " ", uri(), " HTTP/1.1\r\n");
headers(db);
body(db);
}
template<class DynamicBuffer>
void
response(DynamicBuffer& db)
{
write(db, "HTTP/1.");
write(db, rand(2) ? "0" : "1");
write(db, " ", 100 + rand(401), " ");
write(db, token());
write(db, "\r\n");
headers(db);
body(db);
write(db, "\r\n");
}
};
using message_fuzz = message_fuzz_t<>;
} // http
} // beast
#endif

View File

@@ -0,0 +1,119 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/http/message_v1.hpp>
#include <beast/http/headers.hpp>
#include <beast/http/string_body.hpp>
#include <beast/unit_test/suite.hpp>
#include <beast/http/empty_body.hpp>
namespace beast {
namespace http {
class message_v1_test : public beast::unit_test::suite
{
public:
void testFreeFunctions()
{
{
request_v1<empty_body> m;
m.method = "GET";
m.url = "/";
m.version = 11;
m.headers.insert("Upgrade", "test");
BEAST_EXPECT(! is_upgrade(m));
prepare(m, connection::upgrade);
BEAST_EXPECT(is_upgrade(m));
BEAST_EXPECT(m.headers["Connection"] == "upgrade");
m.version = 10;
BEAST_EXPECT(! is_upgrade(m));
}
}
void testPrepare()
{
request_v1<empty_body> m;
m.version = 10;
BEAST_EXPECT(! is_upgrade(m));
m.headers.insert("Transfer-Encoding", "chunked");
try
{
prepare(m);
fail();
}
catch(std::exception const&)
{
}
m.headers.erase("Transfer-Encoding");
m.headers.insert("Content-Length", "0");
try
{
prepare(m);
fail();
}
catch(std::exception const&)
{
pass();
}
m.headers.erase("Content-Length");
m.headers.insert("Connection", "keep-alive");
try
{
prepare(m);
fail();
}
catch(std::exception const&)
{
pass();
}
m.version = 11;
m.headers.erase("Connection");
m.headers.insert("Connection", "close");
BEAST_EXPECT(! is_keep_alive(m));
}
void testSwap()
{
message_v1<false, string_body, headers> m1;
message_v1<false, string_body, headers> m2;
m1.status = 200;
m1.version = 10;
m1.body = "1";
m1.headers.insert("h", "v");
m2.status = 404;
m2.reason = "OK";
m2.body = "2";
m2.version = 11;
swap(m1, m2);
BEAST_EXPECT(m1.status == 404);
BEAST_EXPECT(m2.status == 200);
BEAST_EXPECT(m1.reason == "OK");
BEAST_EXPECT(m2.reason.empty());
BEAST_EXPECT(m1.version == 11);
BEAST_EXPECT(m2.version == 10);
BEAST_EXPECT(m1.body == "2");
BEAST_EXPECT(m2.body == "1");
BEAST_EXPECT(! m1.headers.exists("h"));
BEAST_EXPECT(m2.headers.exists("h"));
}
void run() override
{
testFreeFunctions();
testPrepare();
testSwap();
}
};
BEAST_DEFINE_TESTSUITE(message_v1,http,beast);
} // http
} // beast

View File

@@ -0,0 +1,68 @@
# Authors ordered by first contribution.
Ryan Dahl <ry@tinyclouds.org>
Jeremy Hinegardner <jeremy@hinegardner.org>
Sergey Shepelev <temotor@gmail.com>
Joe Damato <ice799@gmail.com>
tomika <tomika_nospam@freemail.hu>
Phoenix Sol <phoenix@burninglabs.com>
Cliff Frey <cliff@meraki.com>
Ewen Cheslack-Postava <ewencp@cs.stanford.edu>
Santiago Gala <sgala@apache.org>
Tim Becker <tim.becker@syngenio.de>
Jeff Terrace <jterrace@gmail.com>
Ben Noordhuis <info@bnoordhuis.nl>
Nathan Rajlich <nathan@tootallnate.net>
Mark Nottingham <mnot@mnot.net>
Aman Gupta <aman@tmm1.net>
Tim Becker <tim.becker@kuriositaet.de>
Sean Cunningham <sean.cunningham@mandiant.com>
Peter Griess <pg@std.in>
Salman Haq <salman.haq@asti-usa.com>
Cliff Frey <clifffrey@gmail.com>
Jon Kolb <jon@b0g.us>
Fouad Mardini <f.mardini@gmail.com>
Paul Querna <pquerna@apache.org>
Felix Geisendörfer <felix@debuggable.com>
koichik <koichik@improvement.jp>
Andre Caron <andre.l.caron@gmail.com>
Ivo Raisr <ivosh@ivosh.net>
James McLaughlin <jamie@lacewing-project.org>
David Gwynne <loki@animata.net>
Thomas LE ROUX <thomas@november-eleven.fr>
Randy Rizun <rrizun@ortivawireless.com>
Andre Louis Caron <andre.louis.caron@usherbrooke.ca>
Simon Zimmermann <simonz05@gmail.com>
Erik Dubbelboer <erik@dubbelboer.com>
Martell Malone <martellmalone@gmail.com>
Bertrand Paquet <bpaquet@octo.com>
BogDan Vatra <bogdan@kde.org>
Peter Faiman <peter@thepicard.org>
Corey Richardson <corey@octayn.net>
Tóth Tamás <tomika_nospam@freemail.hu>
Cam Swords <cam.swords@gmail.com>
Chris Dickinson <christopher.s.dickinson@gmail.com>
Uli Köhler <ukoehler@btronik.de>
Charlie Somerville <charlie@charliesomerville.com>
Patrik Stutz <patrik.stutz@gmail.com>
Fedor Indutny <fedor.indutny@gmail.com>
runner <runner.mei@gmail.com>
Alexis Campailla <alexis@janeasystems.com>
David Wragg <david@wragg.org>
Vinnie Falco <vinnie.falco@gmail.com>
Alex Butum <alexbutum@linux.com>
Rex Feng <rexfeng@gmail.com>
Alex Kocharin <alex@kocharin.ru>
Mark Koopman <markmontymark@yahoo.com>
Helge Heß <me@helgehess.eu>
Alexis La Goutte <alexis.lagoutte@gmail.com>
George Miroshnykov <george.miroshnykov@gmail.com>
Maciej Małecki <me@mmalecki.com>
Marc O'Morain <github.com@marcomorain.com>
Jeff Pinner <jpinner@twitter.com>
Timothy J Fontaine <tjfontaine@gmail.com>
Akagi201 <akagi201@gmail.com>
Romain Giraud <giraud.romain@gmail.com>
Jay Satiro <raysatiro@yahoo.com>
Arne Steen <Arne.Steen@gmx.de>
Kjell Schubert <kjell.schubert@gmail.com>
Olivier Mengué <dolmen@cpan.org>

View File

@@ -0,0 +1,23 @@
http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright
Igor Sysoev.
Additional changes are licensed under the same terms as NGINX and
copyright Joyent, Inc. and other Node contributors. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

View File

@@ -0,0 +1,246 @@
HTTP Parser
===========
[![Build Status](https://api.travis-ci.org/nodejs/http-parser.svg?branch=master)](https://travis-ci.org/nodejs/http-parser)
This is a parser for HTTP messages written in C. It parses both requests and
responses. The parser is designed to be used in performance HTTP
applications. It does not make any syscalls nor allocations, it does not
buffer data, it can be interrupted at anytime. Depending on your
architecture, it only requires about 40 bytes of data per message
stream (in a web server that is per connection).
Features:
* No dependencies
* Handles persistent streams (keep-alive).
* Decodes chunked encoding.
* Upgrade support
* Defends against buffer overflow attacks.
The parser extracts the following information from HTTP messages:
* Header fields and values
* Content-Length
* Request method
* Response status code
* Transfer-Encoding
* HTTP version
* Request URL
* Message body
Usage
-----
One `http_parser` object is used per TCP connection. Initialize the struct
using `http_parser_init()` and set the callbacks. That might look something
like this for a request parser:
```c
http_parser_settings settings;
settings.on_url = my_url_callback;
settings.on_header_field = my_header_field_callback;
/* ... */
http_parser *parser = malloc(sizeof(http_parser));
http_parser_init(parser, HTTP_REQUEST);
parser->data = my_socket;
```
When data is received on the socket execute the parser and check for errors.
```c
size_t len = 80*1024, nparsed;
char buf[len];
ssize_t recved;
recved = recv(fd, buf, len, 0);
if (recved < 0) {
/* Handle error. */
}
/* Start up / continue the parser.
* Note we pass recved==0 to signal that EOF has been received.
*/
nparsed = http_parser_execute(parser, &settings, buf, recved);
if (parser->upgrade) {
/* handle new protocol */
} else if (nparsed != recved) {
/* Handle error. Usually just close the connection. */
}
```
HTTP needs to know where the end of the stream is. For example, sometimes
servers send responses without Content-Length and expect the client to
consume input (for the body) until EOF. To tell http_parser about EOF, give
`0` as the fourth parameter to `http_parser_execute()`. Callbacks and errors
can still be encountered during an EOF, so one must still be prepared
to receive them.
Scalar valued message information such as `status_code`, `method`, and the
HTTP version are stored in the parser structure. This data is only
temporally stored in `http_parser` and gets reset on each new message. If
this information is needed later, copy it out of the structure during the
`headers_complete` callback.
The parser decodes the transfer-encoding for both requests and responses
transparently. That is, a chunked encoding is decoded before being sent to
the on_body callback.
The Special Problem of Upgrade
------------------------------
HTTP supports upgrading the connection to a different protocol. An
increasingly common example of this is the WebSocket protocol which sends
a request like
GET /demo HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
WebSocket-Protocol: sample
followed by non-HTTP data.
(See [RFC6455](https://tools.ietf.org/html/rfc6455) for more information the
WebSocket protocol.)
To support this, the parser will treat this as a normal HTTP message without a
body, issuing both on_headers_complete and on_message_complete callbacks. However
http_parser_execute() will stop parsing at the end of the headers and return.
The user is expected to check if `parser->upgrade` has been set to 1 after
`http_parser_execute()` returns. Non-HTTP data begins at the buffer supplied
offset by the return value of `http_parser_execute()`.
Callbacks
---------
During the `http_parser_execute()` call, the callbacks set in
`http_parser_settings` will be executed. The parser maintains state and
never looks behind, so buffering the data is not necessary. If you need to
save certain data for later usage, you can do that from the callbacks.
There are two types of callbacks:
* notification `typedef int (*http_cb) (http_parser*);`
Callbacks: on_message_begin, on_headers_complete, on_message_complete.
* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);`
Callbacks: (requests only) on_url,
(common) on_header_field, on_header_value, on_body;
Callbacks must return 0 on success. Returning a non-zero value indicates
error to the parser, making it exit immediately.
For cases where it is necessary to pass local information to/from a callback,
the `http_parser` object's `data` field can be used.
An example of such a case is when using threads to handle a socket connection,
parse a request, and then give a response over that socket. By instantiation
of a thread-local struct containing relevant data (e.g. accepted socket,
allocated memory for callbacks to write into, etc), a parser's callbacks are
able to communicate data between the scope of the thread and the scope of the
callback in a threadsafe manner. This allows http-parser to be used in
multi-threaded contexts.
Example:
```
typedef struct {
socket_t sock;
void* buffer;
int buf_len;
} custom_data_t;
int my_url_callback(http_parser* parser, const char *at, size_t length) {
/* access to thread local custom_data_t struct.
Use this access save parsed data for later use into thread local
buffer, or communicate over socket
*/
parser->data;
...
return 0;
}
...
void http_parser_thread(socket_t sock) {
int nparsed = 0;
/* allocate memory for user data */
custom_data_t *my_data = malloc(sizeof(custom_data_t));
/* some information for use by callbacks.
* achieves thread -> callback information flow */
my_data->sock = sock;
/* instantiate a thread-local parser */
http_parser *parser = malloc(sizeof(http_parser));
http_parser_init(parser, HTTP_REQUEST); /* initialise parser */
/* this custom data reference is accessible through the reference to the
parser supplied to callback functions */
parser->data = my_data;
http_parser_settings settings; / * set up callbacks */
settings.on_url = my_url_callback;
/* execute parser */
nparsed = http_parser_execute(parser, &settings, buf, recved);
...
/* parsed information copied from callback.
can now perform action on data copied into thread-local memory from callbacks.
achieves callback -> thread information flow */
my_data->buffer;
...
}
```
In case you parse HTTP message in chunks (i.e. `read()` request line
from socket, parse, read half headers, parse, etc) your data callbacks
may be called more than once. Http-parser guarantees that data pointer is only
valid for the lifetime of callback. You can also `read()` into a heap allocated
buffer to avoid copying memory around if this fits your application.
Reading headers may be a tricky task if you read/parse headers partially.
Basically, you need to remember whether last header callback was field or value
and apply the following logic:
(on_header_field and on_header_value shortened to on_h_*)
------------------------ ------------ --------------------------------------------
| State (prev. callback) | Callback | Description/action |
------------------------ ------------ --------------------------------------------
| nothing (first call) | on_h_field | Allocate new buffer and copy callback data |
| | | into it |
------------------------ ------------ --------------------------------------------
| value | on_h_field | New header started. |
| | | Copy current name,value buffers to headers |
| | | list and allocate new buffer for new name |
------------------------ ------------ --------------------------------------------
| field | on_h_field | Previous name continues. Reallocate name |
| | | buffer and append callback data to it |
------------------------ ------------ --------------------------------------------
| field | on_h_value | Value for current header started. Allocate |
| | | new buffer and copy callback data to it |
------------------------ ------------ --------------------------------------------
| value | on_h_value | Value continues. Reallocate value buffer |
| | | and append callback data to it |
------------------------ ------------ --------------------------------------------
Parsing URLs
------------
A simplistic zero-copy URL parser is provided as `http_parser_parse_url()`.
Users of this library may wish to use it to parse URLs constructed from
consecutive `on_url` callbacks.
See examples of reading in headers:
* [partial example](http://gist.github.com/155877) in C
* [from http-parser tests](http://github.com/joyent/http-parser/blob/37a0ff8/test.c#L403) in C
* [from Node library](http://github.com/joyent/node/blob/842eaf4/src/http.js#L284) in Javascript

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,362 @@
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef http_parser_h
#define http_parser_h
#ifdef __cplusplus
extern "C" {
#endif
/* Also update SONAME in the Makefile whenever you change these. */
#define HTTP_PARSER_VERSION_MAJOR 2
#define HTTP_PARSER_VERSION_MINOR 7
#define HTTP_PARSER_VERSION_PATCH 0
#include <sys/types.h>
#if defined(_WIN32) && !defined(__MINGW32__) && \
(!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
#include <BaseTsd.h>
#include <stddef.h>
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
* faster
*/
#ifndef HTTP_PARSER_STRICT
# define HTTP_PARSER_STRICT 1
#endif
/* Maximium header size allowed. If the macro is not defined
* before including this header then the default is used. To
* change the maximum header size, define the macro in the build
* environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
* the effective limit on the size of the header, define the macro
* to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
*/
#ifndef HTTP_MAX_HEADER_SIZE
# define HTTP_MAX_HEADER_SIZE (80*1024)
#endif
typedef struct http_parser http_parser;
typedef struct http_parser_settings http_parser_settings;
/* Callbacks should return non-zero to indicate an error. The parser will
* then halt execution.
*
* The one exception is on_headers_complete. In a HTTP_RESPONSE parser
* returning '1' from on_headers_complete will tell the parser that it
* should not expect a body. This is used when receiving a response to a
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
* chunked' headers that indicate the presence of a body.
*
* Returning `2` from on_headers_complete will tell parser that it should not
* expect neither a body nor any futher responses on this connection. This is
* useful for handling responses to a CONNECT request which may not contain
* `Upgrade` or `Connection: upgrade` headers.
*
* http_data_cb does not return data chunks. It will be called arbitrarily
* many times for each string. E.G. you might get 10 callbacks for "on_url"
* each providing just a few characters more data.
*/
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
typedef int (*http_cb) (http_parser*);
/* Request Methods */
#define HTTP_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
XX(1, GET, GET) \
XX(2, HEAD, HEAD) \
XX(3, POST, POST) \
XX(4, PUT, PUT) \
/* pathological */ \
XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \
/* WebDAV */ \
XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \
XX(10, MKCOL, MKCOL) \
XX(11, MOVE, MOVE) \
XX(12, PROPFIND, PROPFIND) \
XX(13, PROPPATCH, PROPPATCH) \
XX(14, SEARCH, SEARCH) \
XX(15, UNLOCK, UNLOCK) \
XX(16, BIND, BIND) \
XX(17, REBIND, REBIND) \
XX(18, UNBIND, UNBIND) \
XX(19, ACL, ACL) \
/* subversion */ \
XX(20, REPORT, REPORT) \
XX(21, MKACTIVITY, MKACTIVITY) \
XX(22, CHECKOUT, CHECKOUT) \
XX(23, MERGE, MERGE) \
/* upnp */ \
XX(24, MSEARCH, M-SEARCH) \
XX(25, NOTIFY, NOTIFY) \
XX(26, SUBSCRIBE, SUBSCRIBE) \
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
/* RFC-5789 */ \
XX(28, PATCH, PATCH) \
XX(29, PURGE, PURGE) \
/* CalDAV */ \
XX(30, MKCALENDAR, MKCALENDAR) \
/* RFC-2068, section 19.6.1.2 */ \
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
enum http_method
{
#define XX(num, name, string) HTTP_##name = num,
HTTP_METHOD_MAP(XX)
#undef XX
};
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
/* Flag values for http_parser.flags field */
enum flags
{ F_CHUNKED = 1 << 0
, F_CONNECTION_KEEP_ALIVE = 1 << 1
, F_CONNECTION_CLOSE = 1 << 2
, F_CONNECTION_UPGRADE = 1 << 3
, F_TRAILING = 1 << 4
, F_UPGRADE = 1 << 5
, F_SKIPBODY = 1 << 6
, F_CONTENTLENGTH = 1 << 7
};
/* Map for errno-related constants
*
* The provided argument should be a macro that takes 2 arguments.
*/
#define HTTP_ERRNO_MAP(XX) \
/* No error */ \
XX(OK, "success") \
\
/* Callback-related errors */ \
XX(CB_message_begin, "the on_message_begin callback failed") \
XX(CB_url, "the on_url callback failed") \
XX(CB_header_field, "the on_header_field callback failed") \
XX(CB_header_value, "the on_header_value callback failed") \
XX(CB_headers_complete, "the on_headers_complete callback failed") \
XX(CB_body, "the on_body callback failed") \
XX(CB_message_complete, "the on_message_complete callback failed") \
XX(CB_status, "the on_status callback failed") \
XX(CB_chunk_header, "the on_chunk_header callback failed") \
XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
\
/* Parsing-related errors */ \
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
XX(HEADER_OVERFLOW, \
"too many header bytes seen; overflow detected") \
XX(CLOSED_CONNECTION, \
"data received after completed connection: close message") \
XX(INVALID_VERSION, "invalid HTTP version") \
XX(INVALID_STATUS, "invalid HTTP status code") \
XX(INVALID_METHOD, "invalid HTTP method") \
XX(INVALID_URL, "invalid URL") \
XX(INVALID_HOST, "invalid host") \
XX(INVALID_PORT, "invalid port") \
XX(INVALID_PATH, "invalid path") \
XX(INVALID_QUERY_STRING, "invalid query string") \
XX(INVALID_FRAGMENT, "invalid fragment") \
XX(LF_EXPECTED, "LF character expected") \
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
XX(INVALID_CONTENT_LENGTH, \
"invalid character in content-length header") \
XX(UNEXPECTED_CONTENT_LENGTH, \
"unexpected content-length header") \
XX(INVALID_CHUNK_SIZE, \
"invalid character in chunk size header") \
XX(INVALID_CONSTANT, "invalid constant string") \
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
XX(STRICT, "strict mode assertion failed") \
XX(PAUSED, "parser is paused") \
XX(UNKNOWN, "an unknown error occurred")
/* Define HPE_* values for each errno value above */
#define HTTP_ERRNO_GEN(n, s) HPE_##n,
enum http_errno {
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
};
#undef HTTP_ERRNO_GEN
/* Get an http_errno value from an http_parser */
#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
struct http_parser {
/** PRIVATE **/
unsigned int type : 2; /* enum http_parser_type */
unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */
unsigned int state : 7; /* enum state from http_parser.c */
unsigned int header_state : 7; /* enum header_state from http_parser.c */
unsigned int index : 7; /* index into current matcher */
unsigned int lenient_http_headers : 1;
uint32_t nread; /* # bytes read in various scenarios */
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
/** READ-ONLY **/
unsigned short http_major;
unsigned short http_minor;
unsigned int status_code : 16; /* responses only */
unsigned int method : 8; /* requests only */
unsigned int http_errno : 7;
/* 1 = Upgrade header was present and the parser has exited because of that.
* 0 = No upgrade header present.
* Should be checked when http_parser_execute() returns in addition to
* error checking.
*/
unsigned int upgrade : 1;
/** PUBLIC **/
void *data; /* A pointer to get hook to the "connection" or "socket" object */
};
struct http_parser_settings {
http_cb on_message_begin;
http_data_cb on_url;
http_data_cb on_status;
http_data_cb on_header_field;
http_data_cb on_header_value;
http_cb on_headers_complete;
http_data_cb on_body;
http_cb on_message_complete;
/* When on_chunk_header is called, the current chunk length is stored
* in parser->content_length.
*/
http_cb on_chunk_header;
http_cb on_chunk_complete;
};
enum http_parser_url_fields
{ UF_SCHEMA = 0
, UF_HOST = 1
, UF_PORT = 2
, UF_PATH = 3
, UF_QUERY = 4
, UF_FRAGMENT = 5
, UF_USERINFO = 6
, UF_MAX = 7
};
/* Result structure for http_parser_parse_url().
*
* Callers should index into field_data[] with UF_* values iff field_set
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
* because we probably have padding left over), we convert any port to
* a uint16_t.
*/
struct http_parser_url {
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
uint16_t port; /* Converted UF_PORT string */
struct {
uint16_t off; /* Offset into buffer in which field starts */
uint16_t len; /* Length of run in buffer */
} field_data[UF_MAX];
};
/* Returns the library version. Bits 16-23 contain the major version number,
* bits 8-15 the minor version number and bits 0-7 the patch level.
* Usage example:
*
* unsigned long version = http_parser_version();
* unsigned major = (version >> 16) & 255;
* unsigned minor = (version >> 8) & 255;
* unsigned patch = version & 255;
* printf("http_parser v%u.%u.%u\n", major, minor, patch);
*/
unsigned long http_parser_version(void);
void http_parser_init(http_parser *parser, enum http_parser_type type);
/* Initialize http_parser_settings members to 0
*/
void http_parser_settings_init(http_parser_settings *settings);
/* Executes the parser. Returns number of parsed bytes. Sets
* `parser->http_errno` on error. */
size_t http_parser_execute(http_parser *parser,
const http_parser_settings *settings,
const char *data,
size_t len);
/* If http_should_keep_alive() in the on_headers_complete or
* on_message_complete callback returns 0, then this should be
* the last message on the connection.
* If you are the server, respond with the "Connection: close" header.
* If you are the client, close the connection.
*/
int http_should_keep_alive(const http_parser *parser);
/* Returns a string version of the HTTP method. */
const char *http_method_str(enum http_method m);
/* Return a string name of the given error */
const char *http_errno_name(enum http_errno err);
/* Return a string description of the given error */
const char *http_errno_description(enum http_errno err);
/* Initialize all http_parser_url members to 0 */
void http_parser_url_init(struct http_parser_url *u);
/* Parse a URL; return nonzero on failure */
int http_parser_parse_url(const char *buf, size_t buflen,
int is_connect,
struct http_parser_url *u);
/* Pause or un-pause the parser; a nonzero value pauses */
void http_parser_pause(http_parser *parser, int paused);
/* Checks if this is the final chunk of the body. */
int http_body_is_final(const http_parser *parser);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,17 @@
//
// 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)
//
#ifdef _MSC_VER
# pragma warning (push)
# pragma warning (disable: 4127) // conditional expression is constant
# pragma warning (disable: 4244) // integer conversion, possible loss of data
#endif
#include "nodejs-parser/http_parser.c"
#ifdef _MSC_VER
# pragma warning (pop)
#endif

View File

@@ -0,0 +1,868 @@
//
// 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_NODEJS_PARSER_HPP
#define BEAST_HTTP_NODEJS_PARSER_HPP
#include "nodejs-parser/http_parser.h"
#include <beast/http/message_v1.hpp>
#include <beast/http/rfc7230.hpp>
#include <beast/core/buffer_concepts.hpp>
#include <beast/core/error.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/system/error_code.hpp>
#include <cstdint>
#include <string>
#include <type_traits>
#include <utility>
namespace beast {
namespace http {
namespace detail {
class nodejs_message_category
: public boost::system::error_category
{
public:
const char*
name() const noexcept override
{
return "nodejs-http-error";
}
std::string
message(int ev) const override
{
return http_errno_description(
static_cast<http_errno>(ev));
}
boost::system::error_condition
default_error_condition(int ev) const noexcept override
{
return boost::system::error_condition{ev, *this};
}
bool
equivalent(int ev,
boost::system::error_condition const& condition
) const noexcept override
{
return condition.value() == ev &&
&condition.category() == this;
}
bool
equivalent(boost::system::error_code const& error,
int ev) const noexcept override
{
return error.value() == ev &&
&error.category() == this;
}
};
template<class = void>
boost::system::error_code
make_nodejs_error(int http_errno)
{
static nodejs_message_category const mc{};
return boost::system::error_code{http_errno, mc};
}
inline
char const*
method_to_string(unsigned method)
{
using namespace beast;
switch(static_cast<http_method>(method))
{
case HTTP_DELETE: return "DELETE";
case HTTP_GET: return "GET";
case HTTP_HEAD: return "HEAD";
case HTTP_POST: return "POST";
case HTTP_PUT: return "PUT";
// pathological
case HTTP_CONNECT: return "CONNECT";
case HTTP_OPTIONS: return "OPTIONS";
case HTTP_TRACE: return "TRACE";
// webdav
case HTTP_COPY: return "COPY";
case HTTP_LOCK: return "LOCK";
case HTTP_MKCOL: return "MKCOL";
case HTTP_MOVE: return "MOVE";
case HTTP_PROPFIND: return "PROPFIND";
case HTTP_PROPPATCH: return "PROPPATCH";
case HTTP_SEARCH: return "SEARCH";
case HTTP_UNLOCK: return "UNLOCK";
case HTTP_BIND: return "BIND";
case HTTP_REBIND: return "REBIND";
case HTTP_UNBIND: return "UNBIND";
case HTTP_ACL: return "ACL";
// subversion
case HTTP_REPORT: return "REPORT";
case HTTP_MKACTIVITY: return "MKACTIVITY";
case HTTP_CHECKOUT: return "CHECKOUT";
case HTTP_MERGE: return "MERGE";
// upnp
case HTTP_MSEARCH: return "MSEARCH";
case HTTP_NOTIFY: return "NOTIFY";
case HTTP_SUBSCRIBE: return "SUBSCRIBE";
case HTTP_UNSUBSCRIBE: return "UNSUBSCRIBE";
// RFC-5789
case HTTP_PATCH: return "PATCH";
case HTTP_PURGE: return "PURGE";
// CalDav
case HTTP_MKCALENDAR: return "MKCALENDAR";
// RFC-2068, section 19.6.1.2
case HTTP_LINK: return "LINK";
case HTTP_UNLINK: return "UNLINK";
};
return "<unknown>";
}
} // detail
template<class Derived>
class nodejs_basic_parser
{
http_parser state_;
boost::system::error_code* ec_;
bool complete_ = false;
std::string url_;
std::string status_;
std::string field_;
std::string value_;
public:
using error_code = boost::system::error_code;
nodejs_basic_parser(nodejs_basic_parser&& other);
nodejs_basic_parser&
operator=(nodejs_basic_parser&& other);
nodejs_basic_parser(nodejs_basic_parser const& other);
nodejs_basic_parser& operator=(nodejs_basic_parser const& other);
explicit
nodejs_basic_parser(bool request) noexcept;
bool
complete() const noexcept
{
return complete_;
}
std::size_t
write(void const* data, std::size_t size)
{
error_code ec;
auto const used = write(data, size, ec);
if(ec)
throw boost::system::system_error{ec};
return used;
}
std::size_t
write(void const* data, std::size_t size,
error_code& ec);
template<class ConstBufferSequence>
std::size_t
write(ConstBufferSequence const& buffers)
{
error_code ec;
auto const used = write(buffers, ec);
if(ec)
throw boost::system::system_error{ec};
return used;
}
template<class ConstBufferSequence>
std::size_t
write(ConstBufferSequence const& buffers,
error_code& ec);
void
write_eof()
{
error_code ec;
write_eof(ec);
if(ec)
throw boost::system::system_error{ec};
}
void
write_eof(error_code& ec);
private:
Derived&
impl()
{
return *static_cast<Derived*>(this);
}
template<class C>
class has_on_start_t
{
template<class T, class R =
decltype(std::declval<T>().on_start(), std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_start =
std::integral_constant<bool, has_on_start_t<C>::value>;
void
call_on_start(std::true_type)
{
impl().on_start();
}
void
call_on_start(std::false_type)
{
}
template<class C>
class has_on_field_t
{
template<class T, class R =
decltype(std::declval<T>().on_field(
std::declval<std::string const&>(),
std::declval<std::string const&>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_field =
std::integral_constant<bool, has_on_field_t<C>::value>;
void
call_on_field(std::string const& field,
std::string const& value, std::true_type)
{
impl().on_field(field, value);
}
void
call_on_field(std::string const&, std::string const&,
std::false_type)
{
}
template<class C>
class has_on_headers_complete_t
{
template<class T, class R =
decltype(std::declval<T>().on_headers_complete(
std::declval<error_code&>()), std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_headers_complete =
std::integral_constant<bool, has_on_headers_complete_t<C>::value>;
void
call_on_headers_complete(error_code& ec, std::true_type)
{
impl().on_headers_complete(ec);
}
void
call_on_headers_complete(error_code&, std::false_type)
{
}
template<class C>
class has_on_request_t
{
template<class T, class R =
decltype(std::declval<T>().on_request(
std::declval<unsigned>(), std::declval<std::string>(),
std::declval<int>(), std::declval<int>(),
std::declval<bool>(), std::declval<bool>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_request =
std::integral_constant<bool, has_on_request_t<C>::value>;
void
call_on_request(unsigned method, std::string url,
int major, int minor, bool keep_alive, bool upgrade,
std::true_type)
{
impl().on_request(
method, url, major, minor, keep_alive, upgrade);
}
void
call_on_request(unsigned, std::string, int, int, bool, bool,
std::false_type)
{
}
template<class C>
class has_on_response_t
{
template<class T, class R =
decltype(std::declval<T>().on_response(
std::declval<int>(), std::declval<std::string>,
std::declval<int>(), std::declval<int>(),
std::declval<bool>(), std::declval<bool>()),
std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
#if 0
using type = decltype(check<C>(0));
#else
// VFALCO Trait seems broken for http::parser
using type = std::true_type;
#endif
public:
static bool const value = type::value;
};
template<class C>
using has_on_response =
std::integral_constant<bool, has_on_response_t<C>::value>;
bool
call_on_response(int status, std::string text,
int major, int minor, bool keep_alive, bool upgrade,
std::true_type)
{
return impl().on_response(
status, text, major, minor, keep_alive, upgrade);
}
bool
call_on_response(int, std::string, int, int, bool, bool,
std::false_type)
{
// VFALCO Certainly incorrect
return true;
}
template<class C>
class has_on_body_t
{
template<class T, class R =
decltype(std::declval<T>().on_body(
std::declval<void const*>(), std::declval<std::size_t>(),
std::declval<error_code&>()), std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_body =
std::integral_constant<bool, has_on_body_t<C>::value>;
void
call_on_body(void const* data, std::size_t bytes,
error_code& ec, std::true_type)
{
impl().on_body(data, bytes, ec);
}
void
call_on_body(void const*, std::size_t,
error_code&, std::false_type)
{
}
template<class C>
class has_on_complete_t
{
template<class T, class R =
decltype(std::declval<T>().on_complete(), std::true_type{})>
static R check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_complete =
std::integral_constant<bool, has_on_complete_t<C>::value>;
void
call_on_complete(std::true_type)
{
impl().on_complete();
}
void
call_on_complete(std::false_type)
{
}
void
check_header();
static int cb_message_start(http_parser*);
static int cb_url(http_parser*, char const*, std::size_t);
static int cb_status(http_parser*, char const*, std::size_t);
static int cb_header_field(http_parser*, char const*, std::size_t);
static int cb_header_value(http_parser*, char const*, std::size_t);
static int cb_headers_complete(http_parser*);
static int cb_body(http_parser*, char const*, std::size_t);
static int cb_message_complete(http_parser*);
static int cb_chunk_header(http_parser*);
static int cb_chunk_complete(http_parser*);
struct hooks_t : http_parser_settings
{
hooks_t()
{
http_parser_settings_init(this);
on_message_begin = &nodejs_basic_parser::cb_message_start;
on_url = &nodejs_basic_parser::cb_url;
on_status = &nodejs_basic_parser::cb_status;
on_header_field = &nodejs_basic_parser::cb_header_field;
on_header_value = &nodejs_basic_parser::cb_header_value;
on_headers_complete = &nodejs_basic_parser::cb_headers_complete;
on_body = &nodejs_basic_parser::cb_body;
on_message_complete = &nodejs_basic_parser::cb_message_complete;
on_chunk_header = &nodejs_basic_parser::cb_chunk_header;
on_chunk_complete = &nodejs_basic_parser::cb_chunk_complete;
}
};
static
http_parser_settings const*
hooks();
};
template<class Derived>
template<class ConstBufferSequence>
std::size_t
nodejs_basic_parser<Derived>::write(
ConstBufferSequence const& buffers, error_code& ec)
{
static_assert(beast::is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
std::size_t bytes_used = 0;
for(auto const& buffer : buffers)
{
auto const n = write(
buffer_cast<void const*>(buffer),
buffer_size(buffer), ec);
if(ec)
return 0;
bytes_used += n;
if(complete())
break;
}
return bytes_used;
}
template<class Derived>
http_parser_settings const*
nodejs_basic_parser<Derived>::hooks()
{
static hooks_t const h;
return &h;
}
template<class Derived>
nodejs_basic_parser<Derived>::
nodejs_basic_parser(nodejs_basic_parser&& other)
{
state_ = other.state_;
state_.data = this;
complete_ = other.complete_;
url_ = std::move(other.url_);
status_ = std::move(other.status_);
field_ = std::move(other.field_);
value_ = std::move(other.value_);
}
template<class Derived>
auto
nodejs_basic_parser<Derived>::operator=(nodejs_basic_parser&& other) ->
nodejs_basic_parser&
{
state_ = other.state_;
state_.data = this;
complete_ = other.complete_;
url_ = std::move(other.url_);
status_ = std::move(other.status_);
field_ = std::move(other.field_);
value_ = std::move(other.value_);
return *this;
}
template<class Derived>
nodejs_basic_parser<Derived>::
nodejs_basic_parser(nodejs_basic_parser const& other)
{
state_ = other.state_;
state_.data = this;
complete_ = other.complete_;
url_ = other.url_;
status_ = other.status_;
field_ = other.field_;
value_ = other.value_;
}
template<class Derived>
auto
nodejs_basic_parser<Derived>::
operator=(nodejs_basic_parser const& other) ->
nodejs_basic_parser&
{
state_ = other.state_;
state_.data = this;
complete_ = other.complete_;
url_ = other.url_;
status_ = other.status_;
field_ = other.field_;
value_ = other.value_;
return *this;
}
template<class Derived>
nodejs_basic_parser<Derived>::nodejs_basic_parser(bool request) noexcept
{
state_.data = this;
http_parser_init(&state_, request
? http_parser_type::HTTP_REQUEST
: http_parser_type::HTTP_RESPONSE);
}
template<class Derived>
std::size_t
nodejs_basic_parser<Derived>::write(void const* data,
std::size_t size, error_code& ec)
{
ec_ = &ec;
auto const n = http_parser_execute(
&state_, hooks(),
static_cast<const char*>(data), size);
if(! ec)
ec = detail::make_nodejs_error(
static_cast<int>(state_.http_errno));
if(ec)
return 0;
return n;
}
template<class Derived>
void
nodejs_basic_parser<Derived>::write_eof(error_code& ec)
{
ec_ = &ec;
http_parser_execute(
&state_, hooks(), nullptr, 0);
if(! ec)
ec = detail::make_nodejs_error(
static_cast<int>(state_.http_errno));
}
template<class Derived>
void
nodejs_basic_parser<Derived>::check_header()
{
if(! value_.empty())
{
//detail::trim(value_);
call_on_field(field_, value_,
has_on_field<Derived>{});
field_.clear();
value_.clear();
}
}
template<class Derived>
int
nodejs_basic_parser<Derived>::cb_message_start(http_parser* p)
{
auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
t.complete_ = false;
t.url_.clear();
t.status_.clear();
t.field_.clear();
t.value_.clear();
t.call_on_start(has_on_start<Derived>{});
return 0;
}
template<class Derived>
int
nodejs_basic_parser<Derived>::cb_url(http_parser* p,
char const* in, std::size_t bytes)
{
auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
t.url_.append(in, bytes);
return 0;
}
template<class Derived>
int
nodejs_basic_parser<Derived>::cb_status(http_parser* p,
char const* in, std::size_t bytes)
{
auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
t.status_.append(in, bytes);
return 0;
}
template<class Derived>
int
nodejs_basic_parser<Derived>::cb_header_field(http_parser* p,
char const* in, std::size_t bytes)
{
auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
t.check_header();
t.field_.append(in, bytes);
return 0;
}
template<class Derived>
int
nodejs_basic_parser<Derived>::cb_header_value(http_parser* p,
char const* in, std::size_t bytes)
{
auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
t.value_.append(in, bytes);
return 0;
}
template<class Derived>
int
nodejs_basic_parser<Derived>::cb_headers_complete(http_parser* p)
{
auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
t.check_header();
t.call_on_headers_complete(*t.ec_,
has_on_headers_complete<Derived>{});
if(*t.ec_)
return 1;
bool const keep_alive =
http_should_keep_alive(p) != 0;
if(p->type == http_parser_type::HTTP_REQUEST)
{
t.call_on_request(p->method, t.url_,
p->http_major, p->http_minor, keep_alive,
p->upgrade, has_on_request<Derived>{});
return 0;
}
return t.call_on_response(p->status_code, t.status_,
p->http_major, p->http_minor, keep_alive,
p->upgrade, has_on_response<Derived>{}) ? 0 : 1;
}
template<class Derived>
int
nodejs_basic_parser<Derived>::cb_body(http_parser* p,
char const* in, std::size_t bytes)
{
auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
t.call_on_body(in, bytes, *t.ec_, has_on_body<Derived>{});
return *t.ec_ ? 1 : 0;
}
template<class Derived>
int
nodejs_basic_parser<Derived>::cb_message_complete(http_parser* p)
{
auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
t.complete_ = true;
t.call_on_complete(has_on_complete<Derived>{});
return 0;
}
template<class Derived>
int
nodejs_basic_parser<Derived>::cb_chunk_header(http_parser*)
{
return 0;
}
template<class Derived>
int
nodejs_basic_parser<Derived>::cb_chunk_complete(http_parser*)
{
return 0;
}
/** A HTTP parser.
The parser may only be used once.
*/
template<bool isRequest, class Body, class Headers>
class nodejs_parser
: public nodejs_basic_parser<nodejs_parser<isRequest, Body, Headers>>
{
using message_type =
message_v1<isRequest, Body, Headers>;
message_type m_;
typename message_type::body_type::reader r_;
bool started_ = false;
public:
nodejs_parser(nodejs_parser&&) = default;
nodejs_parser()
: http::nodejs_basic_parser<nodejs_parser>(isRequest)
, r_(m_)
{
}
/// Returns `true` if at least one byte has been processed
bool
started()
{
return started_;
}
message_type
release()
{
return std::move(m_);
}
private:
friend class http::nodejs_basic_parser<nodejs_parser>;
void
on_start()
{
started_ = true;
}
void
on_field(std::string const& field, std::string const& value)
{
m_.headers.insert(field, value);
}
void
on_headers_complete(error_code&)
{
// vFALCO TODO Decode the Content-Length and
// Transfer-Encoding, see if we can reserve the buffer.
//
// r_.reserve(content_length)
}
bool
on_request(unsigned method, std::string const& url,
int major, int minor, bool keep_alive, bool upgrade,
std::true_type)
{
m_.method = detail::method_to_string(method);
m_.url = url;
m_.version = major * 10 + minor;
return true;
}
bool
on_request(unsigned, std::string const&,
int, int, bool, bool,
std::false_type)
{
return true;
}
bool
on_request(unsigned method, std::string const& url,
int major, int minor, bool keep_alive, bool upgrade)
{
return on_request(method, url,
major, minor, keep_alive, upgrade,
typename message_type::is_request{});
}
bool
on_response(int status, std::string const& reason,
int major, int minor, bool keep_alive, bool upgrade,
std::true_type)
{
m_.status = status;
m_.reason = reason;
m_.version = major * 10 + minor;
// VFALCO TODO return expect_body_
return true;
}
bool
on_response(int, std::string const&, int, int, bool, bool,
std::false_type)
{
return true;
}
bool
on_response(int status, std::string const& reason,
int major, int minor, bool keep_alive, bool upgrade)
{
return on_response(
status, reason, major, minor, keep_alive, upgrade,
std::integral_constant<bool, ! message_type::is_request::value>{});
}
void
on_body(void const* data,
std::size_t size, error_code& ec)
{
r_.write(data, size, ec);
}
void
on_complete()
{
}
};
} // http
} // beast
#endif

View File

@@ -0,0 +1,57 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/http/parse_error.hpp>
#include <beast/unit_test/suite.hpp>
#include <memory>
namespace beast {
namespace http {
class parse_error_test : public unit_test::suite
{
public:
void check(char const* name, parse_error ev)
{
auto const ec = make_error_code(ev);
BEAST_EXPECT(std::string{ec.category().name()} == name);
BEAST_EXPECT(! ec.message().empty());
BEAST_EXPECT(std::addressof(ec.category()) ==
std::addressof(get_parse_error_category()));
BEAST_EXPECT(get_parse_error_category().equivalent(static_cast<int>(ev),
ec.category().default_error_condition(static_cast<int>(ev))));
BEAST_EXPECT(get_parse_error_category().equivalent(
ec, static_cast<int>(ev)));
}
void run() override
{
check("http", parse_error::connection_closed);
check("http", parse_error::bad_method);
check("http", parse_error::bad_uri);
check("http", parse_error::bad_version);
check("http", parse_error::bad_crlf);
check("http", parse_error::bad_request);
check("http", parse_error::bad_status);
check("http", parse_error::bad_reason);
check("http", parse_error::bad_field);
check("http", parse_error::bad_value);
check("http", parse_error::bad_content_length);
check("http", parse_error::illegal_content_length);
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);
}
};
BEAST_DEFINE_TESTSUITE(parse_error,http,beast);
} // http
} // beast

View File

@@ -0,0 +1,155 @@
//
// 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 "nodejs_parser.hpp"
#include "message_fuzz.hpp"
#include <beast/http.hpp>
#include <beast/core/streambuf.hpp>
#include <beast/core/to_string.hpp>
#include <beast/unit_test/suite.hpp>
#include <chrono>
#include <iostream>
#include <vector>
namespace beast {
namespace http {
class parser_bench_test : public beast::unit_test::suite
{
public:
static std::size_t constexpr N = 2000;
using corpus = std::vector<streambuf>;
corpus creq_;
corpus cres_;
std::size_t size_ = 0;
parser_bench_test()
{
creq_ = build_corpus(N/2, std::true_type{});
cres_ = build_corpus(N/2, std::false_type{});
}
corpus
build_corpus(std::size_t n, std::true_type)
{
corpus v;
v.resize(N);
message_fuzz mg;
for(std::size_t i = 0; i < n; ++i)
{
mg.request(v[i]);
size_ += v[i].size();
}
return v;
}
corpus
build_corpus(std::size_t n, std::false_type)
{
corpus v;
v.resize(N);
message_fuzz mg;
for(std::size_t i = 0; i < n; ++i)
{
mg.response(v[i]);
size_ += v[i].size();
}
return v;
}
template<class Parser>
void
testParser(std::size_t repeat, corpus const& v)
{
while(repeat--)
for(auto const& sb : v)
{
Parser p;
error_code ec;
p.write(sb.data(), ec);
if(! BEAST_EXPECTS(! ec, ec.message()))
log << to_string(sb.data()) << std::endl;
}
}
template<class Function>
void
timedTest(std::size_t repeat, std::string const& name, Function&& f)
{
using namespace std::chrono;
using clock_type = std::chrono::high_resolution_clock;
log << name << std::endl;
for(std::size_t trial = 1; trial <= repeat; ++trial)
{
auto const t0 = clock_type::now();
f();
auto const elapsed = clock_type::now() - t0;
log <<
"Trial " << trial << ": " <<
duration_cast<milliseconds>(elapsed).count() << " ms" << std::endl;
}
}
template<bool isRequest>
struct null_parser : basic_parser_v1<isRequest, null_parser<isRequest>>
{
};
void
testSpeed()
{
static std::size_t constexpr Trials = 3;
static std::size_t constexpr Repeat = 50;
log << "sizeof(request parser) == " <<
sizeof(basic_parser_v1<true, null_parser<true>>) << '\n';
log << "sizeof(response parser) == " <<
sizeof(basic_parser_v1<false, null_parser<true>>)<< '\n';
testcase << "Parser speed test, " <<
((Repeat * size_ + 512) / 1024) << "KB in " <<
(Repeat * (creq_.size() + cres_.size())) << " messages";
timedTest(Trials, "nodejs_parser",
[&]
{
testParser<nodejs_parser<
true, streambuf_body, headers>>(
Repeat, creq_);
testParser<nodejs_parser<
false, streambuf_body, headers>>(
Repeat, cres_);
});
timedTest(Trials, "http::basic_parser_v1",
[&]
{
testParser<parser_v1<
true, streambuf_body, headers>>(
Repeat, creq_);
testParser<parser_v1<
false, streambuf_body, headers>>(
Repeat, cres_);
});
pass();
}
void run() override
{
pass();
testSpeed();
}
};
BEAST_DEFINE_TESTSUITE(parser_bench,http,beast);
} // http
} // beast

View File

@@ -0,0 +1,83 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/http/parser_v1.hpp>
#include <beast/http/headers.hpp>
#include <beast/http/string_body.hpp>
#include <beast/unit_test/suite.hpp>
namespace beast {
namespace http {
class parser_v1_test : public beast::unit_test::suite
{
public:
void run() override
{
using boost::asio::buffer;
{
error_code ec;
parser_v1<true, string_body,
basic_headers<std::allocator<char>>> p;
std::string const s =
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"Content-Length: 1\r\n"
"\r\n"
"*";
p.write(buffer(s), ec);
BEAST_EXPECT(! ec);
BEAST_EXPECT(p.complete());
auto m = p.release();
BEAST_EXPECT(m.method == "GET");
BEAST_EXPECT(m.url == "/");
BEAST_EXPECT(m.version == 11);
BEAST_EXPECT(m.headers["User-Agent"] == "test");
BEAST_EXPECT(m.body == "*");
}
{
error_code ec;
parser_v1<false, string_body,
basic_headers<std::allocator<char>>> p;
std::string const s =
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Content-Length: 1\r\n"
"\r\n"
"*";
p.write(buffer(s), ec);
BEAST_EXPECT(! ec);
BEAST_EXPECT(p.complete());
auto m = p.release();
BEAST_EXPECT(m.status == 200);
BEAST_EXPECT(m.reason == "OK");
BEAST_EXPECT(m.version == 11);
BEAST_EXPECT(m.headers["Server"] == "test");
BEAST_EXPECT(m.body == "*");
}
// skip body
{
error_code ec;
parser_v1<false, string_body, headers> p;
std::string const s =
"HTTP/1.1 200 Connection Established\r\n"
"Proxy-Agent: Zscaler/5.1\r\n"
"\r\n";
p.set_option(skip_body{true});
p.write(buffer(s), ec);
BEAST_EXPECT(! ec);
BEAST_EXPECT(p.complete());
}
}
};
BEAST_DEFINE_TESTSUITE(parser_v1,http,beast);
} // http
} // beast

View File

@@ -0,0 +1,282 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/http/read.hpp>
#include "fail_parser.hpp"
#include <beast/http/headers.hpp>
#include <beast/http/streambuf_body.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/spawn.hpp>
namespace beast {
namespace http {
class read_test
: public beast::unit_test::suite
, public test::enable_yield_to
{
public:
template<bool isRequest>
void failMatrix(const char* s, yield_context do_yield)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
static std::size_t constexpr limit = 100;
std::size_t n;
auto const len = strlen(s);
for(n = 0; n < limit; ++n)
{
streambuf sb;
sb.commit(buffer_copy(
sb.prepare(len), buffer(s, len)));
test::fail_counter fc(n);
test::fail_stream<
test::string_stream> fs{fc, ios_, ""};
fail_parser<isRequest> p(fc);
error_code ec;
parse(fs, sb, p, ec);
if(! ec)
break;
}
BEAST_EXPECT(n < limit);
for(n = 0; n < limit; ++n)
{
static std::size_t constexpr pre = 10;
streambuf sb;
sb.commit(buffer_copy(
sb.prepare(pre), buffer(s, pre)));
test::fail_counter fc(n);
test::fail_stream<test::string_stream> fs{
fc, ios_, std::string{s + pre, len - pre}};
fail_parser<isRequest> p(fc);
error_code ec;
parse(fs, sb, p, ec);
if(! ec)
break;
}
BEAST_EXPECT(n < limit);
for(n = 0; n < limit; ++n)
{
streambuf sb;
sb.commit(buffer_copy(
sb.prepare(len), buffer(s, len)));
test::fail_counter fc(n);
test::fail_stream<
test::string_stream> fs{fc, ios_, ""};
fail_parser<isRequest> p(fc);
error_code ec;
async_parse(fs, sb, p, do_yield[ec]);
if(! ec)
break;
}
BEAST_EXPECT(n < limit);
for(n = 0; n < limit; ++n)
{
static std::size_t constexpr pre = 10;
streambuf sb;
sb.commit(buffer_copy(
sb.prepare(pre), buffer(s, pre)));
test::fail_counter fc(n);
test::fail_stream<test::string_stream> fs{
fc, ios_, std::string{s + pre, len - pre}};
fail_parser<isRequest> p(fc);
error_code ec;
async_parse(fs, sb, p, do_yield[ec]);
if(! ec)
break;
}
BEAST_EXPECT(n < limit);
}
void testThrow()
{
try
{
streambuf sb;
test::string_stream ss(ios_, "GET / X");
parser_v1<true, streambuf_body, headers> p;
parse(ss, sb, p);
fail();
}
catch(std::exception const&)
{
pass();
}
}
void testFailures(yield_context do_yield)
{
char const* req[] = {
"GET / HTTP/1.0\r\n"
"Host: localhost\r\n"
"User-Agent: test\r\n"
"Empty:\r\n"
"\r\n"
,
"GET / HTTP/1.1\r\n"
"Host: localhost\r\n"
"User-Agent: test\r\n"
"Content-Length: 2\r\n"
"\r\n"
"**"
,
"GET / HTTP/1.1\r\n"
"Host: localhost\r\n"
"User-Agent: test\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"10\r\n"
"****************\r\n"
"0\r\n\r\n"
,
nullptr
};
char const* res[] = {
"HTTP/1.0 200 OK\r\n"
"Server: test\r\n"
"\r\n"
,
"HTTP/1.0 200 OK\r\n"
"Server: test\r\n"
"\r\n"
"***"
,
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Content-Length: 3\r\n"
"\r\n"
"***"
,
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"10\r\n"
"****************\r\n"
"0\r\n\r\n"
,
nullptr
};
for(std::size_t i = 0; req[i]; ++i)
failMatrix<true>(req[i], do_yield);
for(std::size_t i = 0; res[i]; ++i)
failMatrix<false>(res[i], do_yield);
}
void testRead(yield_context do_yield)
{
static std::size_t constexpr limit = 100;
std::size_t n;
for(n = 0; n < limit; ++n)
{
test::fail_stream<test::string_stream> fs(n, ios_,
"GET / HTTP/1.1\r\n"
"Host: localhost\r\n"
"User-Agent: test\r\n"
"Content-Length: 0\r\n"
"\r\n"
);
request_v1<streambuf_body> m;
try
{
streambuf sb;
read(fs, sb, m);
break;
}
catch(std::exception const&)
{
}
}
BEAST_EXPECT(n < limit);
for(n = 0; n < limit; ++n)
{
test::fail_stream<test::string_stream> fs(n, ios_,
"GET / HTTP/1.1\r\n"
"Host: localhost\r\n"
"User-Agent: test\r\n"
"Content-Length: 0\r\n"
"\r\n"
);
request_v1<streambuf_body> m;
error_code ec;
streambuf sb;
read(fs, sb, m, ec);
if(! ec)
break;
}
BEAST_EXPECT(n < limit);
for(n = 0; n < limit; ++n)
{
test::fail_stream<test::string_stream> fs(n, ios_,
"GET / HTTP/1.1\r\n"
"Host: localhost\r\n"
"User-Agent: test\r\n"
"Content-Length: 0\r\n"
"\r\n"
);
request_v1<streambuf_body> m;
error_code ec;
streambuf sb;
async_read(fs, sb, m, do_yield[ec]);
if(! ec)
break;
}
BEAST_EXPECT(n < limit);
}
void testEof(yield_context do_yield)
{
{
streambuf sb;
test::string_stream ss(ios_, "");
parser_v1<true, streambuf_body, headers> p;
error_code ec;
parse(ss, sb, p, ec);
BEAST_EXPECT(ec == boost::asio::error::eof);
}
{
streambuf sb;
test::string_stream ss(ios_, "");
parser_v1<true, streambuf_body, headers> p;
error_code ec;
async_parse(ss, sb, p, do_yield[ec]);
BEAST_EXPECT(ec == boost::asio::error::eof);
}
}
void run() override
{
testThrow();
yield_to(std::bind(&read_test::testFailures,
this, std::placeholders::_1));
yield_to(std::bind(&read_test::testRead,
this, std::placeholders::_1));
yield_to(std::bind(&read_test::testEof,
this, std::placeholders::_1));
}
};
BEAST_DEFINE_TESTSUITE(read,http,beast);
} // http
} // beast

View File

@@ -0,0 +1,29 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/http/reason.hpp>
#include <beast/unit_test/suite.hpp>
namespace beast {
namespace http {
class reason_test : public unit_test::suite
{
public:
void run() override
{
for(int i = 1; i <= 999; ++i)
BEAST_EXPECT(reason_string(i) != nullptr);
}
};
BEAST_DEFINE_TESTSUITE(reason,http,beast);
} // http
} // beast

View File

@@ -0,0 +1,9 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/http/resume_context.hpp>

View File

@@ -0,0 +1,246 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/http/rfc7230.hpp>
#include <beast/http/detail/rfc7230.hpp>
#include <beast/unit_test/suite.hpp>
#include <string>
#include <vector>
namespace beast {
namespace http {
namespace test {
class rfc7230_test : public beast::unit_test::suite
{
public:
static
std::string
fmt(std::string const& s)
{
return '\'' + s + '\'';
}
static
std::string
str(boost::string_ref const& s)
{
return std::string(s.data(), s.size());
}
static
std::string
str(param_list const& c)
{
std::string s;
for(auto const& p : c)
{
s.push_back(';');
s.append(str(p.first));
s.push_back('=');
s.append(str(p.second));
}
return s;
}
void
testParamList()
{
auto const ce =
[&](std::string const& s)
{
auto const got = str(param_list{s});
BEAST_EXPECTS(got == s, fmt(got));
};
auto const cs =
[&](std::string const& s, std::string const& good)
{
ce(good);
auto const got = str(param_list{s});
ce(got);
BEAST_EXPECTS(got == good, fmt(got));
};
auto const cq =
[&](std::string const& s, std::string const& good)
{
auto const got = str(param_list{s});
BEAST_EXPECTS(got == good, fmt(got));
};
ce("");
cs(" ;\t i =\t 1 \t", ";i=1");
cq("\t; \t xyz=1 ; ijk=\"q\\\"t\"", ";xyz=1;ijk=q\"t");
// invalid strings
cs(";", "");
cs(";,", "");
cs(";xy", "");
cs(";xy", "");
cs(";xy ", "");
cs(";xy,", "");
cq(";x=,", "");
cq(";xy=\"", "");
cq(";xy=\"\x7f", "");
cq(";xy=\"\\", "");
cq(";xy=\"\\\x01\"", "");
}
static
std::string
str(ext_list const& ex)
{
std::string s;
for(auto const& e : ex)
{
if(! s.empty())
s += ',';
s.append(str(e.first));
s += str(e.second);
}
return s;
}
void
testExtList()
{
auto const ce =
[&](std::string const& s)
{
auto const got = str(ext_list{s});
BEAST_EXPECTS(got == s, fmt(got));
};
auto const cs =
[&](std::string const& s, std::string const& good)
{
ce(good);
auto const got = str(ext_list{s});
ce(got);
BEAST_EXPECTS(got == good, fmt(got));
};
auto const cq =
[&](std::string const& s, std::string const& good)
{
auto const got = str(ext_list{s});
BEAST_EXPECTS(got == good, fmt(got));
};
/*
ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
ext = token param-list
param-list = *( OWS ";" OWS param )
param = token OWS "=" OWS ( token / quoted-string )
*/
ce("");
cs(",", "");
cs(", ", "");
cs(",\t", "");
cs(", \t", "");
cs(" ", "");
cs(" ,", "");
cs("\t,", "");
cs("\t , \t", "");
cs(",,", "");
cs(" , \t,, \t,", "");
ce("a");
ce("ab");
ce("a,b");
cs(" a ", "a");
cs("\t a, b\t , c\t", "a,b,c");
cs("a; \t i\t=\t \t1\t ", "a;i=1");
ce("a;i=1;j=2;k=3");
ce("a;i=1;j=2;k=3,b;i=4;j=5;k=6");
cq("ab;x=\" \"", "ab;x= ");
cq("ab;x=\"\\\"\"", "ab;x=\"");
BEAST_EXPECT(ext_list{"a,b;i=1,c;j=2;k=3"}.exists("A"));
BEAST_EXPECT(ext_list{"a,b;i=1,c;j=2;k=3"}.exists("b"));
BEAST_EXPECT(! ext_list{"a,b;i=1,c;j=2;k=3"}.exists("d"));
// invalid strings
cs("i j", "i");
cs(";", "");
}
static
std::string
str(token_list const& c)
{
bool first = true;
std::string s;
for(auto const& p : c)
{
if(! first)
s.push_back(',');
s.append(str(p));
first = false;
}
return s;
}
void
testTokenList()
{
auto const ce =
[&](std::string const& s)
{
auto const got = str(token_list{s});
BEAST_EXPECTS(got == s, fmt(got));
};
auto const cs =
[&](std::string const& s, std::string const& good)
{
ce(good);
auto const got = str(token_list{s});
ce(got);
BEAST_EXPECTS(got == good, fmt(got));
};
cs("", "");
cs(" ", "");
cs(" ", "");
cs("\t", "");
cs(" \t ", "");
cs(",", "");
cs(",,", "");
cs(" ,", "");
cs(" , ,", "");
cs(" x", "x");
cs(" \t x", "x");
cs("x ", "x");
cs("x \t", "x");
cs(" \t x \t ", "x");
ce("x,y");
cs("x ,\ty ", "x,y");
cs("x, y, z", "x,y,z");
BEAST_EXPECT(token_list{"a,b,c"}.exists("A"));
BEAST_EXPECT(token_list{"a,b,c"}.exists("b"));
BEAST_EXPECT(! token_list{"a,b,c"}.exists("d"));
// invalid
cs("x y", "x");
}
void
run()
{
testParamList();
testExtList();
testTokenList();
}
};
BEAST_DEFINE_TESTSUITE(rfc7230,http,beast);
} // test
} // http
} // beast

View File

@@ -0,0 +1,48 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/http/streambuf_body.hpp>
#include <beast/core/to_string.hpp>
#include <beast/http/headers.hpp>
#include <beast/http/parser_v1.hpp>
#include <beast/http/read.hpp>
#include <beast/http/write.hpp>
#include <beast/test/string_stream.hpp>
#include <beast/unit_test/suite.hpp>
#include <boost/lexical_cast.hpp>
namespace beast {
namespace http {
class streambuf_body_test : public beast::unit_test::suite
{
boost::asio::io_service ios_;
public:
void run() override
{
std::string const s =
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Content-Length: 3\r\n"
"\r\n"
"xyz";
test::string_stream ss(ios_, s);
parser_v1<false, streambuf_body, headers> p;
streambuf sb;
parse(ss, sb, p);
BEAST_EXPECT(to_string(p.get().body.data()) == "xyz");
BEAST_EXPECT(boost::lexical_cast<std::string>(p.get()) == s);
}
};
BEAST_DEFINE_TESTSUITE(streambuf_body,http,beast);
} // http
} // beast

View File

@@ -0,0 +1,9 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/http/string_body.hpp>

View File

@@ -0,0 +1,650 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/http/write.hpp>
#include <beast/http/headers.hpp>
#include <beast/http/message.hpp>
#include <beast/http/empty_body.hpp>
#include <beast/http/string_body.hpp>
#include <beast/http/write.hpp>
#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
, public test::enable_yield_to
{
public:
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)
{
error_code ec;
auto const n = write_some(buffers, ec);
if(ec)
throw system_error{ec};
return n;
}
template<class ConstBufferSequence>
std::size_t
write_some(
ConstBufferSequence const& buffers, error_code& ec)
{
auto const n = buffer_size(buffers);
using boost::asio::buffer_size;
using boost::asio::buffer_cast;
str.reserve(str.size() + n);
for(auto const& buffer : buffers)
str.append(buffer_cast<char const*>(buffer),
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 unsized_body
{
using value_type = std::string;
class writer
{
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
writer(message<isRequest, fail_body, Allocator> const& msg)
: body_(msg.body)
{
}
void
init(error_code& ec)
{
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&& rc, error_code& ec, Write&& write)
{
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();
}
};
};
template<bool isRequest, class Body, class Headers>
std::string
str(message_v1<isRequest, Body, Headers> const& m)
{
string_write_stream ss(ios_);
write(ss, m);
return ss.str;
}
void
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(BEAST_EXPECTS(! ec, ec.message()))
BEAST_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(BEAST_EXPECTS(! ec, ec.message()))
BEAST_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);
BEAST_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&)
{
}
}
BEAST_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)
{
BEAST_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;
}
}
BEAST_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)
{
BEAST_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;
}
}
BEAST_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)
{
BEAST_EXPECT(fs.next_layer().str ==
"GET / HTTP/1.0\r\n"
"User-Agent: test\r\n"
"Content-Length: 5\r\n"
"\r\n"
"*****"
);
break;
}
}
BEAST_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)
{
BEAST_EXPECT(fs.next_layer().str ==
"GET / HTTP/1.0\r\n"
"User-Agent: test\r\n"
"Content-Length: 5\r\n"
"\r\n"
"*****"
);
break;
}
}
BEAST_EXPECT(n < limit);
}
void
testOutput()
{
// auto content-length HTTP/1.0
{
message_v1<true, string_body, headers> m;
m.method = "GET";
m.url = "/";
m.version = 10;
m.headers.insert("User-Agent", "test");
m.body = "*";
prepare(m);
BEAST_EXPECT(str(m) ==
"GET / HTTP/1.0\r\n"
"User-Agent: test\r\n"
"Content-Length: 1\r\n"
"\r\n"
"*"
);
}
// keep-alive HTTP/1.0
{
message_v1<true, string_body, headers> m;
m.method = "GET";
m.url = "/";
m.version = 10;
m.headers.insert("User-Agent", "test");
m.body = "*";
prepare(m, connection::keep_alive);
BEAST_EXPECT(str(m) ==
"GET / HTTP/1.0\r\n"
"User-Agent: test\r\n"
"Content-Length: 1\r\n"
"Connection: keep-alive\r\n"
"\r\n"
"*"
);
}
// upgrade HTTP/1.0
{
message_v1<true, string_body, headers> m;
m.method = "GET";
m.url = "/";
m.version = 10;
m.headers.insert("User-Agent", "test");
m.body = "*";
try
{
prepare(m, connection::upgrade);
fail();
}
catch(std::exception const&)
{
pass();
}
}
// no content-length HTTP/1.0
{
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_write_stream ss(ios_);
error_code ec;
write(ss, m, ec);
BEAST_EXPECT(ec == boost::asio::error::eof);
BEAST_EXPECT(ss.str ==
"GET / HTTP/1.0\r\n"
"User-Agent: test\r\n"
"\r\n"
"*"
);
}
// auto content-length HTTP/1.1
{
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);
BEAST_EXPECT(str(m) ==
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"Content-Length: 1\r\n"
"\r\n"
"*"
);
}
// close HTTP/1.1
{
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, connection::close);
string_write_stream ss(ios_);
error_code ec;
write(ss, m, ec);
BEAST_EXPECT(ec == boost::asio::error::eof);
BEAST_EXPECT(ss.str ==
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"Content-Length: 1\r\n"
"Connection: close\r\n"
"\r\n"
"*"
);
}
// upgrade HTTP/1.1
{
message_v1<true, empty_body, headers> m;
m.method = "GET";
m.url = "/";
m.version = 11;
m.headers.insert("User-Agent", "test");
prepare(m, connection::upgrade);
BEAST_EXPECT(str(m) ==
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"Connection: upgrade\r\n"
"\r\n"
);
}
// no content-length HTTP/1.1
{
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_write_stream ss(ios_);
error_code ec;
write(ss, m, ec);
BEAST_EXPECT(ss.str ==
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"1\r\n"
"*\r\n"
"0\r\n\r\n"
);
}
}
void testConvert()
{
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);
BEAST_EXPECT(boost::lexical_cast<std::string>(m) ==
"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
{
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();
}
};
BEAST_DEFINE_TESTSUITE(write,http,beast);
} // http
} // beast

View File

@@ -0,0 +1,9 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/version.hpp>

View File

@@ -0,0 +1,9 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/websocket.hpp>

View File

@@ -0,0 +1,38 @@
# Part of Beast
GroupSources(extras/beast extras)
GroupSources(include/beast beast)
GroupSources(test/websocket "/")
add_executable (websocket-tests
${BEAST_INCLUDES}
${EXTRAS_INCLUDES}
../../extras/beast/unit_test/main.cpp
websocket_async_echo_server.hpp
websocket_sync_echo_server.hpp
error.cpp
option.cpp
rfc6455.cpp
stream.cpp
teardown.cpp
frame.cpp
mask.cpp
stream_base.cpp
utf8_checker.cpp
)
if (NOT WIN32)
target_link_libraries(websocket-tests ${Boost_LIBRARIES} Threads::Threads)
endif()
add_executable (websocket-echo
${BEAST_INCLUDES}
${EXTRAS_INCLUDES}
websocket_async_echo_server.hpp
websocket_sync_echo_server.hpp
websocket_echo.cpp
)
if (NOT WIN32)
target_link_libraries(websocket-echo ${Boost_LIBRARIES} Threads::Threads)
endif()

View File

@@ -0,0 +1,52 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/websocket/error.hpp>
#include <beast/unit_test/suite.hpp>
#include <memory>
namespace beast {
namespace websocket {
class error_test : public unit_test::suite
{
public:
void check(char const* name, error ev)
{
auto const ec = make_error_code(ev);
BEAST_EXPECT(std::string{ec.category().name()} == name);
BEAST_EXPECT(! ec.message().empty());
BEAST_EXPECT(std::addressof(ec.category()) ==
std::addressof(detail::get_error_category()));
BEAST_EXPECT(detail::get_error_category().equivalent(static_cast<int>(ev),
ec.category().default_error_condition(static_cast<int>(ev))));
BEAST_EXPECT(detail::get_error_category().equivalent(
ec, static_cast<int>(ev)));
}
void run() override
{
check("websocket", error::closed);
check("websocket", error::failed);
check("websocket", error::handshake_failed);
check("websocket", error::keep_alive);
check("websocket", error::response_malformed);
check("websocket", error::response_failed);
check("websocket", error::response_denied);
check("websocket", error::request_malformed);
check("websocket", error::request_invalid);
check("websocket", error::request_denied);
check("websocket", error::general);
}
};
BEAST_DEFINE_TESTSUITE(error,websocket,beast);
} // http
} // beast

View File

@@ -0,0 +1,236 @@
//
// 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/websocket/detail/frame.hpp>
#include <beast/unit_test/suite.hpp>
#include <initializer_list>
#include <climits>
namespace beast {
namespace websocket {
namespace detail {
static
bool
operator==(frame_header const& lhs, frame_header const& rhs)
{
return
lhs.op == rhs.op &&
lhs.fin == rhs.fin &&
lhs.mask == rhs.mask &&
lhs.rsv1 == rhs.rsv1 &&
lhs.rsv2 == rhs.rsv2 &&
lhs.rsv3 == rhs.rsv3 &&
lhs.len == rhs.len &&
lhs.key == rhs.key;
}
class frame_test : public beast::unit_test::suite
{
public:
void testCloseCodes()
{
BEAST_EXPECT(! is_valid(0));
BEAST_EXPECT(! is_valid(1));
BEAST_EXPECT(! is_valid(999));
BEAST_EXPECT(! is_valid(1004));
BEAST_EXPECT(! is_valid(1005));
BEAST_EXPECT(! is_valid(1006));
BEAST_EXPECT(! is_valid(1016));
BEAST_EXPECT(! is_valid(2000));
BEAST_EXPECT(! is_valid(2999));
BEAST_EXPECT(is_valid(1000));
BEAST_EXPECT(is_valid(1002));
BEAST_EXPECT(is_valid(3000));
BEAST_EXPECT(is_valid(4000));
BEAST_EXPECT(is_valid(5000));
}
struct test_fh : frame_header
{
test_fh()
{
op = opcode::text;
fin = false;
mask = false;
rsv1 = false;
rsv2 = false;
rsv3 = false;
len = 0;
key = 0;
}
};
void testFrameHeader()
{
// good frame headers
{
role_type role = role_type::client;
auto check =
[&](frame_header const& fh)
{
fh_streambuf sb;
write(sb, fh);
frame_header fh1;
close_code::value code;
auto const n = read_fh1(
fh1, sb, role, code);
if(! BEAST_EXPECT(! code))
return;
if(! BEAST_EXPECT(sb.size() == n))
return;
read_fh2(fh1, sb, role, code);
if(! BEAST_EXPECT(! code))
return;
if(! BEAST_EXPECT(sb.size() == 0))
return;
BEAST_EXPECT(fh1 == fh);
};
test_fh fh;
check(fh);
role = role_type::server;
fh.mask = true;
fh.key = 1;
check(fh);
fh.len = 1;
check(fh);
fh.len = 126;
check(fh);
fh.len = 65535;
check(fh);
fh.len = 65536;
check(fh);
fh.len = std::numeric_limits<std::uint64_t>::max();
check(fh);
}
// bad frame headers
{
role_type role = role_type::client;
auto check =
[&](frame_header const& fh)
{
fh_streambuf sb;
write(sb, fh);
frame_header fh1;
close_code::value code;
auto const n = read_fh1(
fh1, sb, role, code);
if(code)
{
pass();
return;
}
if(! BEAST_EXPECT(sb.size() == n))
return;
read_fh2(fh1, sb, role, code);
if(! BEAST_EXPECT(code))
return;
if(! BEAST_EXPECT(sb.size() == 0))
return;
};
test_fh fh;
fh.op = opcode::close;
fh.fin = true;
fh.len = 126;
check(fh);
fh.len = 0;
fh.rsv1 = true;
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;
fh.op = opcode::ping;
fh.fin = false;
check(fh);
fh.fin = true;
fh.mask = true;
check(fh);
role = role_type::server;
fh.mask = false;
check(fh);
}
}
void bad(std::initializer_list<std::uint8_t> bs)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
static role_type constexpr role =
role_type::client;
std::vector<std::uint8_t> v{bs};
fh_streambuf sb;
sb.commit(buffer_copy(
sb.prepare(v.size()), buffer(v)));
frame_header fh;
close_code::value code;
auto const n = read_fh1(fh, sb, role, code);
if(code)
{
pass();
return;
}
if(! BEAST_EXPECT(sb.size() == n))
return;
read_fh2(fh, sb, role, code);
if(! BEAST_EXPECT(code))
return;
if(! BEAST_EXPECT(sb.size() == 0))
return;
}
void testBadFrameHeaders()
{
// bad frame headers
//
// can't be created by the library
// so we produce them manually.
bad({0, 126, 0, 125});
bad({0, 127, 0, 0, 0, 0, 0, 0, 255, 255});
}
void run() override
{
testCloseCodes();
testFrameHeader();
testBadFrameHeaders();
}
};
BEAST_DEFINE_TESTSUITE(frame,websocket,beast);
} // detail
} // websocket
} // beast

View File

@@ -0,0 +1,50 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/websocket/detail/mask.hpp>
#include <beast/unit_test/suite.hpp>
namespace beast {
namespace websocket {
namespace detail {
class mask_test : public beast::unit_test::suite
{
public:
struct test_generator
{
using result_type = std::uint32_t;
result_type n = 0;
void
seed(std::seed_seq const&)
{
}
std::uint32_t
operator()()
{
return n++;
}
};
void run() override
{
maskgen_t<test_generator> mg;
BEAST_EXPECT(mg() != 0);
}
};
BEAST_DEFINE_TESTSUITE(mask,websocket,beast);
} // detail
} // websocket
} // beast

View File

@@ -0,0 +1,9 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/websocket/option.hpp>

View File

@@ -0,0 +1,9 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/websocket/rfc6455.hpp>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,40 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/websocket/detail/stream_base.hpp>
#include <beast/unit_test/suite.hpp>
#include <initializer_list>
#include <climits>
namespace beast {
namespace websocket {
namespace detail {
class stream_base_test : public beast::unit_test::suite
{
public:
void testClamp()
{
BEAST_EXPECT(detail::clamp(
std::numeric_limits<std::uint64_t>::max()) ==
std::numeric_limits<std::size_t>::max());
}
void run() override
{
testClamp();
}
};
BEAST_DEFINE_TESTSUITE(stream_base,websocket,beast);
} // detail
} // websocket
} // beast

View File

@@ -0,0 +1,9 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/websocket/teardown.hpp>

View File

@@ -0,0 +1,281 @@
//
// 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)
//
// Test that header file is self-contained.
#include <beast/websocket/detail/utf8_checker.hpp>
#include <beast/core/consuming_buffers.hpp>
#include <beast/core/streambuf.hpp>
#include <beast/unit_test/suite.hpp>
#include <array>
namespace beast {
namespace websocket {
namespace detail {
class utf8_checker_test : public beast::unit_test::suite
{
public:
void
testOneByteSequence()
{
utf8_checker utf8;
std::array<std::uint8_t, 256> const buf =
([]()
{
std::array<std::uint8_t, 256> values;
std::uint8_t i = 0;
for(auto& c : values)
c = i++;
return values;
})();
// Valid range 0-127
BEAST_EXPECT(utf8.write(buf.data(), 128));
BEAST_EXPECT(utf8.finish());
// Invalid range 128-193
for(auto it = std::next(buf.begin(), 128);
it != std::next(buf.begin(), 194); ++it)
BEAST_EXPECT(! utf8.write(&(*it), 1));
// Invalid range 245-255
for(auto it = std::next(buf.begin(), 245);
it != buf.end(); ++it)
BEAST_EXPECT(! utf8.write(&(*it), 1));
}
void
testTwoByteSequence()
{
utf8_checker utf8;
std::uint8_t buf[2];
for(auto i = 194; i <= 223; ++i)
{
// First byte valid range 194-223
buf[0] = static_cast<std::uint8_t>(i);
for(auto j = 128; j <= 191; ++j)
{
// Second byte valid range 128-191
buf[1] = static_cast<std::uint8_t>(j);
BEAST_EXPECT(utf8.write(buf, 2));
BEAST_EXPECT(utf8.finish());
}
for(auto j = 0; j <= 127; ++j)
{
// Second byte invalid range 0-127
buf[1] = static_cast<std::uint8_t>(j);
BEAST_EXPECT(! utf8.write(buf, 2));
}
for(auto j = 192; j <= 255; ++j)
{
// Second byte invalid range 192-255
buf[1] = static_cast<std::uint8_t>(j);
BEAST_EXPECT(! utf8.write(buf, 2));
}
}
}
void
testThreeByteSequence()
{
utf8_checker utf8;
std::uint8_t buf[3];
for(auto i = 224; i <= 239; ++i)
{
// First byte valid range 224-239
buf[0] = static_cast<std::uint8_t>(i);
std::int32_t const b = (i == 224 ? 160 : 128);
std::int32_t const e = (i == 237 ? 159 : 191);
for(auto j = b; j <= e; ++j)
{
// Second byte valid range 128-191 or 160-191 or 128-159
buf[1] = static_cast<std::uint8_t>(j);
for(auto k = 128; k <= 191; ++k)
{
// Third byte valid range 128-191
buf[2] = static_cast<std::uint8_t>(k);
BEAST_EXPECT(utf8.write(buf, 3));
BEAST_EXPECT(utf8.finish());
}
for(auto k = 0; k <= 127; ++k)
{
// Third byte invalid range 0-127
buf[2] = static_cast<std::uint8_t>(k);
BEAST_EXPECT(! utf8.write(buf, 3));
}
for(auto k = 192; k <= 255; ++k)
{
// Third byte invalid range 192-255
buf[2] = static_cast<std::uint8_t>(k);
BEAST_EXPECT(! utf8.write(buf, 3));
}
}
for(auto j = 0; j < b; ++j)
{
// Second byte invalid range 0-127 or 0-159
buf[1] = static_cast<std::uint8_t>(j);
BEAST_EXPECT(! utf8.write(buf, 3));
}
for(auto j = e + 1; j <= 255; ++j)
{
// Second byte invalid range 160-255 or 192-255
buf[1] = static_cast<std::uint8_t>(j);
BEAST_EXPECT(! utf8.write(buf, 3));
}
}
}
void
testFourByteSequence()
{
using boost::asio::const_buffers_1;
utf8_checker utf8;
std::uint8_t buf[4];
for(auto i = 240; i <= 244; ++i)
{
// First byte valid range 240-244
buf[0] = static_cast<std::uint8_t>(i);
std::int32_t const b = (i == 240 ? 144 : 128);
std::int32_t const e = (i == 244 ? 143 : 191);
for(auto j = b; j <= e; ++j)
{
// Second byte valid range 128-191 or 144-191 or 128-143
buf[1] = static_cast<std::uint8_t>(j);
for(auto k = 128; k <= 191; ++k)
{
// Third byte valid range 128-191
buf[2] = static_cast<std::uint8_t>(k);
for(auto n = 128; n <= 191; ++n)
{
// Fourth byte valid range 128-191
buf[3] = static_cast<std::uint8_t>(n);
BEAST_EXPECT(utf8.write(const_buffers_1{buf, 4}));
BEAST_EXPECT(utf8.finish());
}
for(auto n = 0; n <= 127; ++n)
{
// Fourth byte invalid range 0-127
buf[3] = static_cast<std::uint8_t>(n);
BEAST_EXPECT(! utf8.write(const_buffers_1{buf, 4}));
}
for(auto n = 192; n <= 255; ++n)
{
// Fourth byte invalid range 192-255
buf[3] = static_cast<std::uint8_t>(n);
BEAST_EXPECT(! utf8.write(buf, 4));
}
}
for(auto k = 0; k <= 127; ++k)
{
// Third byte invalid range 0-127
buf[2] = static_cast<std::uint8_t>(k);
BEAST_EXPECT(! utf8.write(buf, 4));
}
for(auto k = 192; k <= 255; ++k)
{
// Third byte invalid range 192-255
buf[2] = static_cast<std::uint8_t>(k);
BEAST_EXPECT(! utf8.write(buf, 4));
}
}
for(auto j = 0; j < b; ++j)
{
// Second byte invalid range 0-127 or 0-143
buf[1] = static_cast<std::uint8_t>(j);
BEAST_EXPECT(! utf8.write(buf, 3));
}
for(auto j = e + 1; j <= 255; ++j)
{
// Second byte invalid range 144-255 or 192-255
buf[1] = static_cast<std::uint8_t>(j);
BEAST_EXPECT(! utf8.write(buf, 3));
}
}
}
void
testWithStreamBuffer()
{
using namespace boost::asio;
{
// Valid UTF8 encoded text
std::vector<std::vector<std::uint8_t>> const data{{
0x48,0x65,0x69,0x7A,0xC3,0xB6,0x6C,0x72,0xC3,0xBC,0x63,0x6B,
0x73,0x74,0x6F,0xC3,0x9F,0x61,0x62,0x64,0xC3,0xA4,0x6D,0x70,
0x66,0x75,0x6E,0x67
}, {
0xCE,0x93,0xCE,0xB1,0xCE,0xB6,0xCE,0xAD,0xCE,0xB5,0xCF,0x82,
0x20,0xCE,0xBA,0xCE,0xB1,0xE1,0xBD,0xB6,0x20,0xCE,0xBC,0xCF,
0x85,0xCF,0x81,0xCF,0x84,0xCE,0xB9,0xE1,0xBD,0xB2,0xCF,0x82,
0x20,0xCE,0xB4,0xE1,0xBD,0xB2,0xCE,0xBD,0x20,0xCE,0xB8,0xE1,
0xBD,0xB0,0x20,0xCE,0xB2,0xCF,0x81,0xE1,0xBF,0xB6,0x20,0xCF,
0x80,0xCE,0xB9,0xE1,0xBD,0xB0,0x20,0xCF,0x83,0xCF,0x84,0xE1,
0xBD,0xB8,0x20,0xCF,0x87,0xCF,0x81,0xCF,0x85,0xCF,0x83,0xCE,
0xB1,0xCF,0x86,0xE1,0xBD,0xB6,0x20,0xCE,0xBE,0xCE,0xAD,0xCF,
0x86,0xCF,0x89,0xCF,0x84,0xCE,0xBF
}, {
0xC3,0x81,0x72,0x76,0xC3,0xAD,0x7A,0x74,0xC5,0xB1,0x72,0xC5,
0x91,0x20,0x74,0xC3,0xBC,0x6B,0xC3,0xB6,0x72,0x66,0xC3,0xBA,
0x72,0xC3,0xB3,0x67,0xC3,0xA9,0x70
}
};
utf8_checker utf8;
for(auto const& s : data)
{
static std::size_t constexpr size = 8;
std::size_t n = s.size();
auto cb = consumed_buffers(
boost::asio::const_buffers_1(
s.data(), n), 0);
streambuf sb(size);
while(n)
{
auto const amount = std::min(n, size);
sb.commit(buffer_copy(sb.prepare(amount), cb));
cb.consume(amount);
n -= amount;
}
BEAST_EXPECT(utf8.write(sb.data()));
BEAST_EXPECT(utf8.finish());
}
}
}
void run() override
{
testOneByteSequence();
testTwoByteSequence();
testThreeByteSequence();
testFourByteSequence();
testWithStreamBuffer();
}
};
BEAST_DEFINE_TESTSUITE(utf8_checker,websocket,beast);
} // detail
} // websocket
} // beast

View File

@@ -0,0 +1,337 @@
//
// 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
#include <beast/core/placeholders.hpp>
#include <beast/core/streambuf.hpp>
#include <beast/websocket.hpp>
#include <boost/optional.hpp>
#include <functional>
#include <iostream>
#include <memory>
#include <thread>
namespace beast {
namespace websocket {
// Asynchronous WebSocket echo client/server
//
class async_echo_server
{
public:
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
private:
bool log_ = false;
boost::asio::io_service ios_;
socket_type sock_;
boost::asio::ip::tcp::acceptor acceptor_;
std::vector<std::thread> thread_;
public:
async_echo_server(bool server,
endpoint_type const& ep, std::size_t threads)
: sock_(ios_)
, acceptor_(ios_)
{
if(server)
{
error_code ec;
acceptor_.open(ep.protocol(), ec);
maybe_throw(ec, "open");
acceptor_.set_option(
boost::asio::socket_base::reuse_address{true});
acceptor_.bind(ep, ec);
maybe_throw(ec, "bind");
acceptor_.listen(
boost::asio::socket_base::max_connections, ec);
maybe_throw(ec, "listen");
acceptor_.async_accept(sock_,
std::bind(&async_echo_server::on_accept, this,
beast::asio::placeholders::error));
}
else
{
Peer{log_, std::move(sock_), ep};
}
thread_.reserve(threads);
for(std::size_t i = 0; i < threads; ++i)
thread_.emplace_back(
[&]{ ios_.run(); });
}
~async_echo_server()
{
error_code ec;
ios_.dispatch(
[&]{ acceptor_.close(ec); });
for(auto& t : thread_)
t.join();
}
endpoint_type
local_endpoint() const
{
return acceptor_.local_endpoint();
}
private:
class Peer
{
struct data
{
bool log;
int state = 0;
boost::optional<endpoint_type> ep;
stream<socket_type> ws;
boost::asio::io_service::strand strand;
opcode op;
beast::streambuf db;
int id;
data(bool log_, socket_type&& sock_)
: log(log_)
, ws(std::move(sock_))
, strand(ws.get_io_service())
, id([]
{
static int n = 0;
return ++n;
}())
{
}
data(bool log_, socket_type&& sock_,
endpoint_type const& ep_)
: log(log_)
, ep(ep_)
, ws(std::move(sock_))
, strand(ws.get_io_service())
, id([]
{
static int n = 0;
return ++n;
}())
{
}
};
std::shared_ptr<data> d_;
public:
Peer(Peer&&) = default;
Peer(Peer const&) = default;
Peer& operator=(Peer&&) = delete;
Peer& operator=(Peer const&) = delete;
struct identity
{
template<class Body, class Headers>
void
operator()(http::message<true, Body, Headers>& req)
{
req.headers.replace("User-Agent", "async_echo_client");
}
template<class Body, class Headers>
void
operator()(http::message<false, Body, Headers>& resp)
{
resp.headers.replace("Server", "async_echo_server");
}
};
template<class... Args>
explicit
Peer(bool log, socket_type&& sock, Args&&... args)
: d_(std::make_shared<data>(log,
std::forward<socket_type>(sock),
std::forward<Args>(args)...))
{
auto& d = *d_;
d.ws.set_option(decorate(identity{}));
d.ws.set_option(read_message_max(64 * 1024 * 1024));
run();
}
void run()
{
auto& d = *d_;
if(! d.ep)
{
d.ws.async_accept(std::move(*this));
}
else
{
d.state = 4;
d.ws.next_layer().async_connect(
*d.ep, std::move(*this));
}
}
template<class DynamicBuffer, std::size_t N>
static
bool
match(DynamicBuffer& db, char const(&s)[N])
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
if(db.size() < N-1)
return false;
static_string<N-1> t;
t.resize(N-1);
buffer_copy(buffer(t.data(), t.size()),
db.data());
if(t != s)
return false;
db.consume(N-1);
return true;
}
void operator()(error_code ec, std::size_t)
{
(*this)(ec);
}
void operator()(error_code ec)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
auto& d = *d_;
switch(d.state)
{
// did accept
case 0:
if(ec)
return fail(ec, "async_accept");
// start
case 1:
if(ec)
return fail(ec, "async_handshake");
d.db.consume(d.db.size());
// read message
d.state = 2;
d.ws.async_read(d.op, d.db,
d.strand.wrap(std::move(*this)));
return;
// got message
case 2:
if(ec == error::closed)
return;
if(ec)
return fail(ec, "async_read");
if(match(d.db, "RAW"))
{
d.state = 1;
boost::asio::async_write(d.ws.next_layer(),
d.db.data(), d.strand.wrap(std::move(*this)));
return;
}
else if(match(d.db, "TEXT"))
{
d.state = 1;
d.ws.set_option(message_type{opcode::text});
d.ws.async_write(
d.db.data(), d.strand.wrap(std::move(*this)));
return;
}
else if(match(d.db, "PING"))
{
ping_data payload;
d.db.consume(buffer_copy(
buffer(payload.data(), payload.size()),
d.db.data()));
d.state = 1;
d.ws.async_ping(payload,
d.strand.wrap(std::move(*this)));
return;
}
else if(match(d.db, "CLOSE"))
{
d.state = 1;
d.ws.async_close({},
d.strand.wrap(std::move(*this)));
return;
}
// write message
d.state = 1;
d.ws.set_option(message_type(d.op));
d.ws.async_write(d.db.data(),
d.strand.wrap(std::move(*this)));
return;
// connected
case 4:
if(ec)
return fail(ec, "async_connect");
d.state = 1;
d.ws.async_handshake(
d.ep->address().to_string() + ":" +
std::to_string(d.ep->port()),
"/", d.strand.wrap(std::move(*this)));
return;
}
}
private:
void
fail(error_code ec, std::string what)
{
auto& d = *d_;
if(d.log)
{
if(ec != error::closed)
std::cerr << "#" << d_->id << " " <<
what << ": " << ec.message() << std::endl;
}
}
};
void
fail(error_code ec, std::string what)
{
if(log_)
std::cerr << what << ": " <<
ec.message() << std::endl;
}
void
maybe_throw(error_code ec, std::string what)
{
if(ec)
{
fail(ec, what);
throw ec;
}
}
void
on_accept(error_code ec)
{
if(! acceptor_.is_open())
return;
if(ec == boost::asio::error::operation_aborted)
return;
maybe_throw(ec, "accept");
socket_type sock(std::move(sock_));
acceptor_.async_accept(sock_,
std::bind(&async_echo_server::on_accept, this,
beast::asio::placeholders::error));
Peer{false, std::move(sock)};
}
};
} // websocket
} // beast
#endif

View File

@@ -0,0 +1,24 @@
//
// 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_server.hpp"
#include "websocket_sync_echo_server.hpp"
#include <beast/test/sig_wait.hpp>
int main()
{
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
beast::websocket::async_echo_server s1(true, endpoint_type{
address_type::from_string("127.0.0.1"), 6000 }, 4);
beast::websocket::sync_echo_server s2(true, endpoint_type{
address_type::from_string("127.0.0.1"), 6001 });
beast::test::sig_wait();
}

View File

@@ -0,0 +1,238 @@
//
// 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
#include <beast/core/streambuf.hpp>
#include <beast/websocket.hpp>
#include <boost/optional.hpp>
#include <functional>
#include <iostream>
#include <memory>
#include <thread>
namespace beast {
namespace websocket {
// Synchronous WebSocket echo client/server
//
class sync_echo_server
{
public:
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
private:
bool log_ = false;
boost::asio::io_service ios_;
socket_type sock_;
boost::asio::ip::tcp::acceptor acceptor_;
std::thread thread_;
public:
sync_echo_server(bool server, endpoint_type ep)
: sock_(ios_)
, acceptor_(ios_)
{
error_code ec;
acceptor_.open(ep.protocol(), ec);
maybe_throw(ec, "open");
acceptor_.set_option(
boost::asio::socket_base::reuse_address{true});
acceptor_.bind(ep, ec);
maybe_throw(ec, "bind");
acceptor_.listen(
boost::asio::socket_base::max_connections, ec);
maybe_throw(ec, "listen");
acceptor_.async_accept(sock_,
std::bind(&sync_echo_server::on_accept, this,
beast::asio::placeholders::error));
thread_ = std::thread{[&]{ ios_.run(); }};
}
~sync_echo_server()
{
error_code ec;
ios_.dispatch(
[&]{ acceptor_.close(ec); });
thread_.join();
}
endpoint_type
local_endpoint() const
{
return acceptor_.local_endpoint();
}
private:
void
fail(error_code ec, std::string what)
{
if(log_)
std::cerr <<
what << ": " << ec.message() << std::endl;
}
void
fail(int id, error_code ec, std::string what)
{
if(log_)
std::cerr << "#" << std::to_string(id) << " " <<
what << ": " << ec.message() << std::endl;
}
void
maybe_throw(error_code ec, std::string what)
{
if(ec)
{
fail(ec, what);
throw ec;
}
}
struct lambda
{
int id;
sync_echo_server& self;
boost::asio::io_service::work work;
// Must be destroyed before work otherwise the
// io_service could be destroyed before the socket.
socket_type sock;
lambda(int id_, sync_echo_server& self_,
socket_type&& sock_)
: id(id_)
, self(self_)
, work(sock_.get_io_service())
, sock(std::move(sock_))
{
}
void operator()()
{
self.do_peer(id, std::move(sock));
}
};
void
on_accept(error_code ec)
{
if(ec == boost::asio::error::operation_aborted)
return;
maybe_throw(ec, "accept");
static int id_ = 0;
std::thread{lambda{++id_, *this, std::move(sock_)}}.detach();
acceptor_.async_accept(sock_,
std::bind(&sync_echo_server::on_accept, this,
beast::asio::placeholders::error));
}
struct identity
{
template<class Body, class Headers>
void
operator()(http::message<true, Body, Headers>& req)
{
req.headers.replace("User-Agent", "sync_echo_client");
}
template<class Body, class Headers>
void
operator()(http::message<false, Body, Headers>& resp)
{
resp.headers.replace("Server", "sync_echo_server");
}
};
template<class DynamicBuffer, std::size_t N>
static
bool
match(DynamicBuffer& db, char const(&s)[N])
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
if(db.size() < N-1)
return false;
static_string<N-1> t;
t.resize(N-1);
buffer_copy(buffer(t.data(), t.size()),
db.data());
if(t != s)
return false;
db.consume(N-1);
return true;
}
void
do_peer(int id, socket_type&& sock)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
stream<socket_type> ws(std::move(sock));
ws.set_option(decorate(identity{}));
ws.set_option(read_message_max(64 * 1024 * 1024));
error_code ec;
ws.accept(ec);
if(ec)
{
fail(id, ec, "accept");
return;
}
for(;;)
{
opcode op;
beast::streambuf sb;
ws.read(op, sb, ec);
if(ec)
{
auto const s = ec.message();
break;
}
ws.set_option(message_type(op));
if(match(sb, "RAW"))
{
boost::asio::write(
ws.next_layer(), sb.data(), ec);
}
else if(match(sb, "TEXT"))
{
ws.set_option(message_type{opcode::text});
ws.write(sb.data(), ec);
}
else if(match(sb, "PING"))
{
ping_data payload;
sb.consume(buffer_copy(
buffer(payload.data(), payload.size()),
sb.data()));
ws.ping(payload, ec);
}
else if(match(sb, "CLOSE"))
{
ws.close({}, ec);
}
else
{
ws.write(sb.data(), ec);
}
if(ec)
break;
}
if(ec && ec != error::closed)
{
fail(id, ec, "read");
}
}
};
} // websocket
} // beast
#endif