WebSocket refactoring and tests:

websocket:

* Move echo server to test/
* Fix warnings
* Fix maskgen being uncopyable
* Simplify utf8_checker special member declarations
* Fix stream move assignable when owning the next layer
* Add javadocs for stream special members
* Add stream unit tests
* Move throwing member definitions to the .ipp file
* Use get_lowest_layer in stream declaration
* Perform type checks at each call site instead of constructor
* Demote close_code to a non-class enum:
    Otherwise, application specific close codes
    cannot be assigned without using static_cast.

core:

* Add streambuf_readstream special members tests
* Add move assignment operator to streambuf_readstream
* Add detail/get_lowest_layer trait
* Add static_string tests
* Move static_string from websocket to core
This commit is contained in:
Vinnie Falco
2016-04-30 13:00:33 -04:00
parent a0b04bdff2
commit 4104eca1f1
38 changed files with 1792 additions and 567 deletions

View File

@ -8,6 +8,7 @@ set_property (GLOBAL PROPERTY USE_FOLDERS ON)
if (WIN32)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /W4 /wd4100 /D_SCL_SECURE_NO_WARNINGS=1 /D_CRT_SECURE_NO_WARNINGS=1")
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO")
else()
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)

View File

@ -51,6 +51,7 @@ project beast
<include>./include
#<use>/boost//headers
<library>/boost/system//boost_system
<library>/boost/coroutine//boost_coroutine
<library>/boost/filesystem//boost_filesystem
<library>/boost/program_options//boost_program_options
# <library>ssl

View File

@ -1,28 +1,39 @@
* Replace Jamroot with Jamfile
* Complete allocator testing in basic_streambuf, basic_headers
* Tidy up type_checks
- Derive from std::integral_constant
* Check DOXYGEN, GENERATIC_DOCS directives in source
- See if we can include them now that xsl is fixed
* Make buffers_debug a detail
* Define Parser concept in HTTP
* melpon sandbox?
* invokable unit test
* trim public interface of rfc2616.h to essentials only
* Fix index in docs
* Figure out why namespace rfc2616 is included in docs
(currently disabled via GENERATING_DOCS macro)
* Include Example program listings in the docs
* Update for rfc7230
* HTTP parser size limit with test (configurable?)
* HTTP parser trailers with test
* URL parser, strong URL checking in HTTP parser
* More fine grained parser errors
* Fix bidirectional buffers iterators operator->()
* http type_check, e.g. is_WritableBody
* add bool should_close(message_v1 const&) to replace the use
of eof return value from write and async_write
General:
* Use SFINAE on return values (search for "class =")
Boost.Http
* Use enum instead of bool in isRequest
* move version to a subclass of message
Docs:
* Include Example program listings in the docs
* Fix index in docs
* Figure out why namespace rfc2616 is included in docs
(currently disabled via GENERATING_DOCS macro)
* melpon sandbox?
* Check DOXYGEN, GENERATIC_DOCS directives in source
- See if we can include them now that xsl is fixed
Core:
* Replace Jamroot with Jamfile
* Fix bidirectional buffers iterators operator->()
* Tidy up type_checks
- Derive from std::integral_constant
* Complete allocator testing in basic_streambuf, basic_headers
WebSocket:
* optimized versions of key/masking, choose prepared_key size
* invokable unit test
* Finish up all of static_string including tests
HTTP:
* Define Parser concept in HTTP
* trim public interface of rfc2616.h to essentials only
* add bool should_close(message_v1 const&) to replace the use
of eof return value from write and async_write
* http type_check, e.g. is_WritableBody
* More fine grained parser errors
* HTTP parser size limit with test (configurable?)
* HTTP parser trailers with test
* URL parser, strong URL checking in HTTP parser
* Update for rfc7230

View File

@ -138,13 +138,14 @@
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.is_AsyncReadStream">is_AsyncReadStream</link></member>
<member><link linkend="beast.ref.is_AsyncWriteStream">is_AsyncWriteStream</link></member>
<member><link linkend="beast.ref.is_AsyncStream">is_AsyncStream</link></member>
<member><link linkend="beast.ref.is_BufferSequence">is_BufferSequence</link></member>
<member><link linkend="beast.ref.is_ConstBufferSequence">is_ConstBufferSequence</link></member>
<member><link linkend="beast.ref.is_Handler">is_Handler</link></member>
<member><link linkend="beast.ref.is_MutableBufferSequence">is_MutableBufferSequence</link></member>
<member><link linkend="beast.ref.is_Stream">is_Stream</link></member>
<member><link linkend="beast.ref.is_Streambuf">is_Streambuf</link></member>
<member><link linkend="beast.ref.is_SyncReadStream">is_SyncReadStream</link></member>
<member><link linkend="beast.ref.is_SyncStream">is_SyncStream</link></member>
<member><link linkend="beast.ref.is_SyncWriteStream">is_SyncWriteStream</link></member>
</simplelist>
</entry>

View File

@ -2,6 +2,7 @@
GroupSources(include/beast)
GroupSources(examples)
GroupSources(test)
add_executable (http-crawl
${BEAST_INCLUDES}
@ -21,7 +22,7 @@ add_executable (http-server
http_stream.hpp
http_stream.ipp
http_sync_server.hpp
sig_wait.hpp
../test/sig_wait.hpp
http_server.cpp
)
@ -38,18 +39,6 @@ if (NOT WIN32)
target_link_libraries(http-example ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
endif()
add_executable (websocket-echo
${BEAST_INCLUDES}
sig_wait.hpp
websocket_async_echo_peer.hpp
websocket_sync_echo_peer.hpp
websocket_echo.cpp
)
if (NOT WIN32)
target_link_libraries(websocket-echo ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
endif()
add_executable (websocket-example
${BEAST_INCLUDES}
websocket_example.cpp

View File

@ -7,24 +7,20 @@
import os ;
exe http_crawl :
exe http-crawl :
http_crawl.cpp
urls_large_data.cpp
;
exe http_server :
exe http-server :
http_server.cpp
;
exe http_example :
exe http-example :
http_example.cpp
;
exe websocket_echo :
websocket_echo.cpp
;
exe websocket_example :
exe websocket-example :
websocket_example.cpp
;

View File

@ -19,7 +19,7 @@
#include "http_async_server.hpp"
#include "http_sync_server.hpp"
#include "sig_wait.hpp"
#include "../test/sig_wait.hpp"
#include <boost/program_options.hpp>
@ -69,13 +69,8 @@ int main(int ac, char const* av[])
endpoint_type ep{address_type::from_string(ip), port};
if(sync)
{
http_sync_server server(ep, root);
sig_wait();
}
else
{
http_async_server server(ep, threads, root);
sig_wait();
}
sig_wait();
}

View File

@ -0,0 +1,53 @@
//
// 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_DETAIL_GET_LOWEST_LAYER_HPP
#define BEAST_DETAIL_GET_LOWEST_LAYER_HPP
#include <type_traits>
namespace beast {
namespace detail {
template<class T>
class has_lowest_layer
{
template<class U, class R =
typename U::lowest_layer_type>
static std::true_type check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<T>(0));
public:
static bool constexpr value = type::value;
};
template<class T, bool B>
struct maybe_get_lowest_layer
{
using type = T;
};
template<class T>
struct maybe_get_lowest_layer<T, true>
{
using type = typename T::lowest_layer_type;
};
// If T has a nested type lowest_layer_type,
// returns that, else returns T.
template<class T>
struct get_lowest_layer
{
using type = typename maybe_get_lowest_layer<T,
has_lowest_layer<T>::value>::type;
};
} // detail
} // beast
#endif

View File

@ -10,7 +10,6 @@
#include <beast/bind_handler.hpp>
#include <beast/handler_alloc.hpp>
#include <beast/type_check.hpp>
namespace beast {
@ -158,8 +157,6 @@ streambuf_readstream<Stream, Streambuf>::
streambuf_readstream(Args&&... args)
: next_layer_(std::forward<Args>(args)...)
{
static_assert(is_Stream<next_layer_type>::value,
"Stream requirements not met");
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
}
@ -173,6 +170,8 @@ async_write_some(ConstBufferSequence const& buffers,
typename async_completion<
WriteHandler, void(error_code)>::result_type
{
static_assert(is_AsyncWriteStream<next_layer_type>::value,
"AsyncWriteStream requirements not met");
static_assert(is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
@ -190,6 +189,8 @@ streambuf_readstream<Stream, Streambuf>::
read_some(
MutableBufferSequence const& buffers)
{
static_assert(is_SyncReadStream<next_layer_type>::value,
"SyncReadStream requirements not met");
static_assert(is_MutableBufferSequence<
MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
@ -207,6 +208,8 @@ streambuf_readstream<Stream, Streambuf>::
read_some(MutableBufferSequence const& buffers,
error_code& ec)
{
static_assert(is_SyncReadStream<next_layer_type>::value,
"SyncReadStream requirements not met");
static_assert(is_MutableBufferSequence<
MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
@ -239,6 +242,8 @@ async_read_some(
typename async_completion<
ReadHandler, void(error_code)>::result_type
{
static_assert(is_AsyncReadStream<next_layer_type>::value,
"Stream requirements not met");
static_assert(is_MutableBufferSequence<
MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");

View File

@ -0,0 +1,696 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_WEBSOCKET_STATIC_STRING_HPP
#define BEAST_WEBSOCKET_STATIC_STRING_HPP
#include <algorithm>
#include <array>
#include <cstdint>
#include <iterator>
#include <stdexcept>
#include <string>
namespace beast {
namespace websocket {
/** A string with a fixed-size storage area.
`static_string` objects behave like `std::string` except that
the storage is not dynamically allocated but rather fixed in
size.
These strings offer performance advantages when a protocol
imposes a natural small upper limit on the size of a value.
@note The stored string is always null-terminated.
*/
template<
std::size_t N,
class CharT = char,
class Traits = std::char_traits<CharT>>
class static_string
{
template<std::size_t, class, class>
friend class static_string;
std::size_t n_;
std::array<CharT, N+1> s_;
public:
using traits_type = Traits;
using value_type = typename Traits::char_type;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
using const_pointer = value_type const*;
using const_reference = value_type const&;
using iterator = value_type*;
using const_iterator = value_type const*;
using reverse_iterator =
std::reverse_iterator<iterator>;
using const_reverse_iterator =
std::reverse_iterator<const_iterator>;
/** Default constructor.
The string is initially empty, and null terminated.
*/
static_string();
/// Copy constructor.
static_string(static_string const& s);
/// Copy constructor.
template<std::size_t M>
static_string(static_string<M, CharT, Traits> const& s);
/// Copy assignment.
static_string&
operator=(static_string const& s);
/// Copy assignment.
template<std::size_t M>
static_string&
operator=(static_string<M, CharT, Traits> const& s);
/// Construct from string literal.
template<std::size_t M>
static_string(const CharT (&s)[M]);
/// Assign from string literal.
template<std::size_t M>
static_string& operator=(const CharT (&s)[M]);
/// Access specified character with bounds checking.
reference
at(size_type pos);
/// Access specified character with bounds checking.
const_reference
at(size_type pos) const;
/// Access specified character.
reference
operator[](size_type pos)
{
return s_[pos];
}
/// Access specified character.
const_reference
operator[](size_type pos) const
{
return s_[pos];
}
/// Accesses the first character.
CharT&
front()
{
return s_[0];
}
/// Accesses the first character.
CharT const&
front() const
{
return s_[0];
}
/// Accesses the last character.
CharT&
back()
{
return s_[n_-1];
}
/// Accesses the last character.
CharT const&
back() const
{
return s_[n_-1];
}
/// Returns a pointer to the first character of a string.
CharT*
data()
{
return &s_[0];
}
/// Returns a pointer to the first character of a string.
CharT const*
data() const
{
return &s_[0];
}
/// Returns a non-modifiable standard C character array version of the string.
CharT const*
c_str() const
{
return &s_[0];
}
/// Returns an iterator to the beginning.
iterator
begin()
{
return &s_[0];
}
/// Returns an iterator to the beginning.
const_iterator
begin() const
{
return &s_[0];
}
/// Returns an iterator to the beginning.
const_iterator
cbegin() const
{
return &s_[0];
}
/// Returns an iterator to the end.
iterator
end()
{
return &s_[n_];
}
/// Returns an iterator to the end.
const_iterator
end() const
{
return &s_[n_];
}
/// Returns an iterator to the end.
const_iterator
cend() const
{
return &s_[n_];
}
/// Returns a reverse iterator to the beginning.
reverse_iterator
rbegin()
{
return reverse_iterator{end()};
}
/// Returns a reverse iterator to the beginning.
const_reverse_iterator
rbegin() const
{
return const_reverse_iterator{cend()};
}
/// Returns a reverse iterator to the beginning.
const_reverse_iterator
crbegin() const
{
return const_reverse_iterator{cend()};
}
/// Returns a reverse iterator to the end.
reverse_iterator
rend()
{
return reverse_iterator{begin()};
}
/// Returns a reverse iterator to the end.
const_reverse_iterator
rend() const
{
return const_reverse_iterator{cbegin()};
}
/// Returns a reverse iterator to the end.
const_reverse_iterator
crend() const
{
return const_reverse_iterator{cbegin()};
}
/// Returns `true` if the string is empty.
bool
empty() const
{
return n_ == 0;
}
/// Returns the number of characters, excluding the null terminator.
size_type
size() const
{
return n_;
}
/// Returns the maximum number of characters that can be stored, excluding the null terminator.
size_type constexpr
max_size() const
{
return N;
}
/// Returns the number of characters that can be held in currently allocated storage.
size_type
capacity() const
{
return N;
}
/// Reduces memory usage by freeing unused memory.
void
shrink_to_fit()
{
// no-op
}
/// Clears the contents.
void
clear()
{
resize(0);
}
/** Changes the number of characters stored.
@note No value-initialization is performed.
*/
void
resize(std::size_t n);
/** Changes the number of characters stored.
If the resulting string is larger, the new
characters are initialized to the value of `c`.
*/
void
resize(std::size_t n, CharT c);
/// Compare two character sequences.
template<std::size_t M>
int compare(static_string<M, CharT, Traits> const& rhs) const;
/// Return the characters as a `basic_string`.
std::basic_string<CharT, Traits>
to_string() const
{
return std::basic_string<
CharT, Traits>{&s_[0], n_};
}
private:
void
assign(CharT const* s);
};
template<std::size_t N, class CharT, class Traits>
static_string<N, CharT, Traits>::
static_string()
: n_(0)
{
s_[0] = 0;
}
template<std::size_t N, class CharT, class Traits>
static_string<N, CharT, Traits>::
static_string(static_string const& s)
: n_(s.n_)
{
Traits::copy(&s_[0], &s.s_[0], n_ + 1);
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
static_string<N, CharT, Traits>::
static_string(static_string<M, CharT, Traits> const& s)
{
if(s.size() > N)
throw std::length_error("static_string overflow");
n_ = s.size();
Traits::copy(&s_[0], &s.s_[0], n_ + 1);
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
operator=(static_string const& s) ->
static_string&
{
n_ = s.n_;
Traits::copy(&s_[0], &s.s_[0], n_ + 1);
return *this;
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
auto
static_string<N, CharT, Traits>::
operator=(static_string<M, CharT, Traits> const& s) ->
static_string&
{
if(s.size() > N)
throw std::length_error("static_string overflow");
n_ = s.size();
Traits::copy(&s_[0], &s.s_[0], n_ + 1);
return *this;
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
static_string<N, CharT, Traits>::
static_string(const CharT (&s)[M])
: n_(M-1)
{
static_assert(M-1 <= N,
"static_string overflow");
Traits::copy(&s_[0], &s[0], M);
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
auto
static_string<N, CharT, Traits>::
operator=(const CharT (&s)[M]) ->
static_string&
{
static_assert(M-1 <= N,
"static_string overflow");
n_ = M-1;
std::copy(&s[0], &s[M], &s_[0]);
return *this;
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
at(size_type pos) ->
reference
{
if(pos >= n_)
throw std::out_of_range("static_string::at");
return s_[pos];
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
at(size_type pos) const ->
const_reference
{
if(pos >= n_)
throw std::out_of_range("static_string::at");
return s_[pos];
}
template<std::size_t N, class CharT, class Traits>
void
static_string<N, CharT, Traits>::
resize(std::size_t n)
{
if(n > N)
throw std::length_error("static_string overflow");
n_ = n;
s_[n_] = 0;
}
template<std::size_t N, class CharT, class Traits>
void
static_string<N, CharT, Traits>::
resize(std::size_t n, CharT c)
{
if(n > N)
throw std::length_error("static_string overflow");
if(n > n_)
Traits::assign(&s_[n_], n - n_, c);
n_ = n;
s_[n_] = 0;
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
int
static_string<N, CharT, Traits>::
compare(static_string<M, CharT, Traits> const& rhs) const
{
if(size() < rhs.size())
{
auto const v = Traits::compare(
data(), rhs.data(), size());
if(v == 0)
return -1;
return v;
}
else if(size() > rhs.size())
{
auto const v = Traits::compare(
data(), rhs.data(), rhs.size());
if(v == 0)
return 1;
return v;
}
return Traits::compare(data(), rhs.data(), size());
}
template<std::size_t N, class CharT, class Traits>
void
static_string<N, CharT, Traits>::
assign(CharT const* s)
{
auto const n = Traits::length(s);
if(n > N)
throw std::out_of_range("too large");
n_ = n;
Traits::copy(&s_[0], s, n_ + 1);
}
namespace detail {
template<std::size_t N, std::size_t M, class CharT, class Traits>
int compare(
static_string<N, CharT, Traits> const& lhs,
const CharT (&s)[M])
{
if(lhs.size() < M-1)
{
auto const v = Traits::compare(
lhs.data(), &s[0], lhs.size());
if(v == 0)
return -1;
return v;
}
else if(lhs.size() > M-1)
{
auto const v = Traits::compare(
lhs.data(), &s[0], M-1);
if(v == 0)
return 1;
return v;
}
return Traits::compare(lhs.data(), &s[0], lhs.size());
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
int compare(
const CharT (&s)[M],
static_string<N, CharT, Traits> const& rhs)
{
if(M-1 < rhs.size())
{
auto const v = Traits::compare(
&s[0], rhs.data(), M-1);
if(v == 0)
return -1;
return v;
}
else if(M-1 > rhs.size())
{
auto const v = Traits::compare(
&s[0], rhs.data(), rhs.size());
if(v == 0)
return 1;
return v;
}
return Traits::compare(&s[0], rhs.data(), M-1);
}
} // detail
#if ! GENERATING_DOCS
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator==(
static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs)
{
return lhs.compare(rhs) == 0;
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator!=(
static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs)
{
return lhs.compare(rhs) != 0;
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator<(
static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs)
{
return lhs.compare(rhs) < 0;
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator<=(
static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs)
{
return lhs.compare(rhs) <= 0;
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator>(
static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs)
{
return lhs.compare(rhs) > 0;
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator>=(
static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs)
{
return lhs.compare(rhs) >= 0;
}
//---
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator==(
const CharT (&s)[N],
static_string<M, CharT, Traits> const& rhs)
{
return detail::compare(s, rhs) == 0;
}
template<std::size_t N, class CharT, class Traits, std::size_t M>
bool operator==(
static_string<N, CharT, Traits> const& lhs,
const CharT (&s)[M])
{
return detail::compare(lhs, s) == 0;
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator!=(
const CharT (&s)[N],
static_string<M, CharT, Traits> const& rhs)
{
return detail::compare(s, rhs) != 0;
}
template<std::size_t N, class CharT, class Traits, std::size_t M>
bool operator!=(
static_string<N, CharT, Traits> const& lhs,
const CharT (&s)[M])
{
return detail::compare(lhs, s) != 0;
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator<(
const CharT (&s)[N],
static_string<M, CharT, Traits> const& rhs)
{
return detail::compare(s, rhs) < 0;
}
template<std::size_t N, class CharT, class Traits, std::size_t M>
bool operator<(
static_string<N, CharT, Traits> const& lhs,
const CharT (&s)[M])
{
return detail::compare(lhs, s) < 0;
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator<=(
const CharT (&s)[N],
static_string<M, CharT, Traits> const& rhs)
{
return detail::compare(s, rhs) <= 0;
}
template<std::size_t N, class CharT, class Traits, std::size_t M>
bool operator<=(
static_string<N, CharT, Traits> const& lhs,
const CharT (&s)[M])
{
return detail::compare(lhs, s) <= 0;
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator>(
const CharT (&s)[N],
static_string<M, CharT, Traits> const& rhs)
{
return detail::compare(s, rhs) > 0;
}
template<std::size_t N, class CharT, class Traits, std::size_t M>
bool operator>(
static_string<N, CharT, Traits> const& lhs,
const CharT (&s)[M])
{
return detail::compare(lhs, s) > 0;
}
template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator>=(
const CharT (&s)[N],
static_string<M, CharT, Traits> const& rhs)
{
return detail::compare(s, rhs) >= 0;
}
template<std::size_t N, class CharT, class Traits, std::size_t M>
bool operator>=(
static_string<N, CharT, Traits> const& lhs,
const CharT (&s)[M])
{
return detail::compare(lhs, s) >= 0;
}
#endif
} // websocket
} // beast
#endif

View File

@ -10,6 +10,8 @@
#include <beast/async_completion.hpp>
#include <beast/streambuf.hpp>
#include <beast/type_check.hpp>
#include <beast/detail/get_lowest_layer.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/system/error_code.hpp>
@ -83,8 +85,7 @@ namespace beast {
@tparam Streambuf The type of stream buffer to use.
*/
template<class Stream,
class Streambuf = streambuf>
template<class Stream, class Streambuf>
class streambuf_readstream
{
using error_code = boost::system::error_code;
@ -106,11 +107,23 @@ public:
/// The type of the lowest layer.
using lowest_layer_type =
typename next_layer_type::lowest_layer_type;
typename detail::get_lowest_layer<
next_layer_type>::type;
/// Move constructor.
/** Move constructor.
@note The behavior of move assignment on or from streams
with active or pending operations is undefined.
*/
streambuf_readstream(streambuf_readstream&&) = default;
/** Move assignment.
@note The behavior of move assignment on or from streams
with active or pending operations is undefined.
*/
streambuf_readstream& operator=(streambuf_readstream&&) = default;
/** Construct the wrapping stream.
@param args Parameters forwarded to the `Stream` constructor.
@ -200,6 +213,8 @@ public:
std::size_t
write_some(ConstBufferSequence const& buffers)
{
static_assert(is_SyncWriteStream<next_layer_type>::value,
"SyncWriteStream requirements not met");
return next_layer_.write_some(buffers);
}
@ -210,6 +225,8 @@ public:
write_some(ConstBufferSequence const& buffers,
error_code& ec)
{
static_assert(is_SyncWriteStream<next_layer_type>::value,
"SyncWriteStream requirements not met");
return next_layer_.write_some(buffers, ec);
}

View File

@ -272,18 +272,26 @@ public:
};
static_assert(! is_SyncWriteStream<int>::value, "");
/// Determine if `T` meets the requirements of `Stream`.
/// Determine if `T` meets the requirements of `SyncStream`.
template<class T>
struct is_Stream
struct is_SyncStream
{
/// `true` if `T` meets the requirements.
/// `true` if `T` meets the requirements.
static bool const value =
is_AsyncReadStream<T>::value &&
is_AsyncWriteStream<T>::value &&
is_SyncReadStream<T>::value &&
is_SyncWriteStream<T>::value;
};
/// Determine if `T` meets the requirements of `SyncStream`.
template<class T>
struct is_AsyncStream
{
/// `true` if `T` meets the requirements.
static bool const value =
is_AsyncReadStream<T>::value &&
is_AsyncWriteStream<T>::value;
};
/// Determine if `T` meets the requirements of `Streambuf`.
template<class T>
class is_Streambuf

View File

@ -24,7 +24,6 @@
#include <beast/websocket/option.hpp>
#include <beast/websocket/rfc6455.hpp>
#include <beast/websocket/stream.hpp>
#include <beast/websocket/static_string.hpp>
#include <beast/websocket/teardown.hpp>
#endif

View File

@ -9,11 +9,11 @@
#define BEAST_WEBSOCKET_DETAIL_FRAME_HPP
#include <beast/websocket/rfc6455.hpp>
#include <beast/websocket/static_string.hpp>
#include <beast/websocket/detail/endian.hpp>
#include <beast/websocket/detail/utf8_checker.hpp>
#include <beast/consuming_buffers.hpp>
#include <beast/static_streambuf.hpp>
#include <beast/static_string.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/endian/buffers.hpp>
#include <cassert>
@ -70,10 +70,9 @@ is_control(opcode op)
// Returns `true` if a close code is valid
inline
bool
is_valid(close_code code)
is_valid(close_code::value code)
{
auto const v = static_cast<
std::uint16_t>(code);
auto const v = code;
switch(v)
{
case 1000:
@ -154,7 +153,7 @@ write(Streambuf& sb, frame_header const& fh)
template<class Streambuf>
std::size_t
read_fh1(frame_header& fh, Streambuf& sb,
role_type role, close_code& code)
role_type role, close_code::value& code)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
@ -171,7 +170,8 @@ read_fh1(frame_header& fh, Streambuf& sb,
default:
need = 0;
}
if((fh.mask = (b[1] & 0x80) != 0))
fh.mask = (b[1] & 0x80) != 0;
if(fh.mask)
need += 4;
fh.op = static_cast<opcode>(b[0] & 0x0f);
fh.fin = (b[0] & 0x80) != 0;
@ -230,7 +230,7 @@ read_fh1(frame_header& fh, Streambuf& sb,
template<class Streambuf>
void
read_fh2(frame_header& fh, Streambuf& sb,
role_type role, close_code& code)
role_type role, close_code::value& code)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
@ -301,7 +301,7 @@ read_fh2(frame_header& fh, Streambuf& sb,
template<class Buffers>
void
read(ping_payload_type& data,
Buffers const& bs, close_code& code)
Buffers const& bs, close_code::value& code)
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
@ -318,7 +318,7 @@ read(ping_payload_type& data,
template<class Buffers>
void
read(close_reason& cr,
Buffers const& bs, close_code& code)
Buffers const& bs, close_code::value& code)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
@ -341,15 +341,7 @@ read(close_reason& cr,
{
std::uint8_t b[2];
buffer_copy(buffer(b), cb);
#if 0
// Causes strict-aliasing warning in gcc
cr.code = static_cast<close_code>(
reinterpret_cast<
big_uint16_buf_t const*>(&b[0])->value());
#else
cr.code = static_cast<close_code>(
big_uint16_to_native(&b[0]));
#endif
cr.code = big_uint16_to_native(&b[0]);
cb.consume(2);
n -= 2;
if(! is_valid(cr.code))

View File

@ -85,8 +85,6 @@ public:
#endif
invokable() = default;
invokable(invokable const&) = delete;
invokable& operator=(invokable const&) = delete;
invokable(invokable&& other)
{

View File

@ -41,9 +41,6 @@ class maskgen_t
public:
using result_type = typename std::mt19937::result_type;
maskgen_t(maskgen_t const&) = delete;
maskgen_t& operator=(maskgen_t const&) = delete;
maskgen_t();
result_type

View File

@ -108,7 +108,7 @@ protected:
template<class = void>
void
prepare_fh(close_code& code);
prepare_fh(close_code::value& code);
template<class Streambuf>
void

View File

@ -73,12 +73,6 @@ class utf8_checker_t
std::uint32_t codepoint_ = 0;
public:
utf8_checker_t() = default;
utf8_checker_t(utf8_checker_t&&) = default;
utf8_checker_t(utf8_checker_t const&) = default;
utf8_checker_t& operator=(utf8_checker_t&&) = default;
utf8_checker_t& operator=(utf8_checker_t const&) = default;
void
reset();

View File

@ -124,7 +124,7 @@ operator()(error_code const& ec,
case 0:
// read message
d.state = 1;
http::async_read(d.ws.next_layer_,
http::async_read(d.ws.next_layer(),
d.ws.stream_.buffer(), d.req,
std::move(*this));
return;

View File

@ -134,7 +134,7 @@ stream<NextLayer>::handshake_op<
case 1:
// read http response
d.state = 2;
http::async_read(d.ws.next_layer_,
http::async_read(d.ws.next_layer(),
d.ws.stream_.buffer(), d.resp,
std::move(*this));
return;

View File

@ -132,7 +132,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
close_code code;
close_code::value code = close_code::none;
while(! ec && d.state != 99)
{
switch(d.state)
@ -195,7 +195,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
d.state = 4;
break;
}
// call handler
case 4:
d.state = 99;
@ -397,7 +397,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
case 11:
d.state = 12;
wsproto_helpers::call_async_teardown(
d.ws.next_layer_, std::move(*this));
d.ws.next_layer(), std::move(*this));
return;
case 12:
@ -483,7 +483,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
case 19:
d.state = 20;
wsproto_helpers::call_async_teardown(
d.ws.next_layer_, std::move(*this));
d.ws.next_layer(), std::move(*this));
return;
case 20:

View File

@ -113,7 +113,7 @@ operator()(error_code ec, bool again)
case 0:
// send response
d.state = 1;
http::async_write(d.ws.next_layer_,
http::async_write(d.ws.next_layer(),
d.resp, std::move(*this));
return;

View File

@ -41,7 +41,7 @@ namespace detail {
template<class _>
void
stream_base::prepare_fh(close_code& code)
stream_base::prepare_fh(close_code::value& code)
{
// continuation without an active message
if(! rd_cont_ && rd_fh_.op == opcode::cont)
@ -170,11 +170,20 @@ template<class NextLayer>
template<class... Args>
stream<NextLayer>::
stream(Args&&... args)
: next_layer_(std::forward<Args>(args)...)
, stream_(next_layer_)
: stream_(std::forward<Args>(args)...)
{
static_assert(is_Stream<next_layer_type>::value,
"Stream requirements not met");
}
template<class NextLayer>
void
stream<NextLayer>::
accept()
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
error_code ec;
accept(boost::asio::null_buffers{}, ec);
detail::maybe_throw(ec, "accept");
}
template<class NextLayer>
@ -182,6 +191,8 @@ void
stream<NextLayer>::
accept(error_code& ec)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
accept(boost::asio::null_buffers{}, ec);
}
@ -192,6 +203,8 @@ typename async_completion<
stream<NextLayer>::
async_accept(AcceptHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements requirements not met");
return async_accept(boost::asio::null_buffers{},
std::forward<AcceptHandler>(handler));
}
@ -202,6 +215,8 @@ void
stream<NextLayer>::
accept(ConstBufferSequence const& buffers)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
@ -216,6 +231,8 @@ void
stream<NextLayer>::
accept(ConstBufferSequence const& buffers, error_code& ec)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(beast::is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
@ -225,7 +242,7 @@ accept(ConstBufferSequence const& buffers, error_code& ec)
stream_.buffer().prepare(
buffer_size(buffers)), buffers));
http::request<http::empty_body> m;
http::read(next_layer_, stream_.buffer(), m, ec);
http::read(next_layer(), stream_.buffer(), m, ec);
if(ec)
return;
accept(m, ec);
@ -238,6 +255,8 @@ typename async_completion<
stream<NextLayer>::
async_accept(ConstBufferSequence const& bs, AcceptHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements requirements not met");
static_assert(beast::is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
@ -255,6 +274,8 @@ void
stream<NextLayer>::
accept(http::message<true, Body, Headers> const& request)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
error_code ec;
accept(request, ec);
detail::maybe_throw(ec, "accept");
@ -267,6 +288,8 @@ stream<NextLayer>::
accept(http::message<true, Body, Headers> const& req,
error_code& ec)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
auto resp = build_response(req);
http::write(stream_, resp, ec);
if(resp.status != 101)
@ -287,6 +310,8 @@ stream<NextLayer>::
async_accept(http::message<true, Body, Headers> const& req,
AcceptHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements requirements not met");
beast::async_completion<
AcceptHandler, void(error_code)
> completion(handler);
@ -297,19 +322,34 @@ async_accept(http::message<true, Body, Headers> const& req,
return completion.result.get();
}
template<class NextLayer>
void
stream<NextLayer>::
handshake(boost::string_ref const& host,
boost::string_ref const& resource)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
error_code ec;
handshake(host, resource, ec);
detail::maybe_throw(ec, "upgrade");
}
template<class NextLayer>
void
stream<NextLayer>::
handshake(boost::string_ref const& host,
boost::string_ref const& resource, error_code& ec)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
std::string key;
http::write(stream_,
build_request(host, resource, key), ec);
if(ec)
return;
http::response<http::string_body> resp;
http::read(next_layer_, stream_.buffer(), resp, ec);
http::read(next_layer(), stream_.buffer(), resp, ec);
if(ec)
return;
do_response(resp, key, ec);
@ -323,6 +363,8 @@ stream<NextLayer>::
async_handshake(boost::string_ref const& host,
boost::string_ref const& resource, HandshakeHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements not met");
beast::async_completion<
HandshakeHandler, void(error_code)
> completion(handler);
@ -331,11 +373,25 @@ async_handshake(boost::string_ref const& host,
return completion.result.get();
}
template<class NextLayer>
void
stream<NextLayer>::
close(close_reason const& cr)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
error_code ec;
close(cr, ec);
detail::maybe_throw(ec, "close");
}
template<class NextLayer>
void
stream<NextLayer>::
close(close_reason const& cr, error_code& ec)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
assert(! wr_close_);
wr_close_ = true;
detail::frame_streambuf fb;
@ -351,6 +407,8 @@ typename async_completion<
stream<NextLayer>::
async_close(close_reason const& cr, CloseHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements not met");
beast::async_completion<
CloseHandler, void(error_code)
> completion(handler);
@ -359,12 +417,27 @@ async_close(close_reason const& cr, CloseHandler&& handler)
return completion.result.get();
}
template<class NextLayer>
template<class Streambuf>
void
stream<NextLayer>::
read(opcode& op, Streambuf& streambuf)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
error_code ec;
read(op, streambuf, ec);
detail::maybe_throw(ec, "read");
}
template<class NextLayer>
template<class Streambuf>
void
stream<NextLayer>::
read(opcode& op, Streambuf& streambuf, error_code& ec)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
frame_info fi;
for(;;)
{
@ -385,6 +458,8 @@ stream<NextLayer>::
async_read(opcode& op,
Streambuf& streambuf, ReadHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements requirements not met");
static_assert(beast::is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
beast::async_completion<
@ -395,13 +470,28 @@ async_read(opcode& op,
return completion.result.get();
}
template<class NextLayer>
template<class Streambuf>
void
stream<NextLayer>::
read_frame(frame_info& fi, Streambuf& streambuf)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
error_code ec;
read_frame(fi, streambuf, ec);
detail::maybe_throw(ec, "read_some");
}
template<class NextLayer>
template<class Streambuf>
void
stream<NextLayer>::
read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
{
close_code code{};
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
close_code::value code{};
for(;;)
{
if(rd_need_ == 0)
@ -409,7 +499,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
// read header
detail::frame_streambuf fb;
do_read_fh(fb, code, ec);
if((error_ = ec != 0))
error_ = ec != 0;
if(error_)
return;
if(code != close_code::none)
break;
@ -421,7 +512,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
auto const mb = fb.prepare(
static_cast<std::size_t>(rd_fh_.len));
fb.commit(boost::asio::read(stream_, mb, ec));
if((error_ = ec != 0))
error_ = ec != 0;
if(error_)
return;
if(rd_fh_.mask)
detail::mask_inplace(mb, rd_key_);
@ -437,7 +529,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
write_ping<static_streambuf>(
fb, opcode::pong, data);
boost::asio::write(stream_, fb.data(), ec);
if((error_ = ec != 0))
error_ = ec != 0;
if(error_)
return;
continue;
}
@ -445,7 +538,7 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
{
ping_payload_type data;
detail::read(data, fb.data(), code);
if((error_ = ec != 0))
if(code != close_code::none)
break;
// VFALCO How to notify callers using
// the synchronous interface?
@ -466,7 +559,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
wr_close_ = true;
write_close<static_streambuf>(fb, cr);
boost::asio::write(stream_, fb.data(), ec);
if((error_ = ec != 0))
error_ = ec != 0;
if(error_)
return;
}
break;
@ -483,7 +577,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
detail::clamp(rd_need_));
auto const bytes_transferred =
stream_.read_some(smb, ec);
if((error_ = ec != 0))
error_ = ec != 0;
if(error_)
return;
rd_need_ -= bytes_transferred;
auto const pb = prepare_buffers(
@ -514,18 +609,20 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
detail::frame_streambuf fb;
write_close<static_streambuf>(fb, code);
boost::asio::write(stream_, fb.data(), ec);
if((error_ = ec != 0))
error_ = ec != 0;
if(error_)
return;
}
wsproto_helpers::call_teardown(next_layer_, ec);
if((error_ = ec != 0))
wsproto_helpers::call_teardown(next_layer(), ec);
error_ = ec != 0;
if(error_)
return;
ec = error::failed;
error_ = true;
return;
}
if(! ec)
wsproto_helpers::call_teardown(next_layer_, ec);
wsproto_helpers::call_teardown(next_layer(), ec);
if(! ec)
ec = error::closed;
error_ = ec != 0;
@ -539,6 +636,8 @@ stream<NextLayer>::
async_read_frame(frame_info& fi,
Streambuf& streambuf, ReadHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements requirements not met");
static_assert(beast::is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
beast::async_completion<
@ -548,12 +647,27 @@ async_read_frame(frame_info& fi,
return completion.result.get();
}
template<class NextLayer>
template<class ConstBufferSequence>
void
stream<NextLayer>::
write(ConstBufferSequence const& buffers)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
error_code ec;
write(buffers, ec);
detail::maybe_throw(ec, "write");
}
template<class NextLayer>
template<class ConstBufferSequence>
void
stream<NextLayer>::
write(ConstBufferSequence const& bs, error_code& ec)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(beast::is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
@ -582,6 +696,8 @@ typename async_completion<
stream<NextLayer>::
async_write(ConstBufferSequence const& bs, WriteHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements not met");
static_assert(beast::is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
@ -592,12 +708,27 @@ async_write(ConstBufferSequence const& bs, WriteHandler&& handler)
return completion.result.get();
}
template<class NextLayer>
template<class ConstBufferSequence>
void
stream<NextLayer>::
write_frame(bool fin, ConstBufferSequence const& buffers)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
error_code ec;
write_frame(fin, buffers, ec);
detail::maybe_throw(ec, "write");
}
template<class NextLayer>
template<class ConstBufferSequence>
void
stream<NextLayer>::
write_frame(bool fin, ConstBufferSequence const& bs, error_code& ec)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(beast::is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
@ -678,6 +809,8 @@ stream<NextLayer>::
async_write_frame(bool fin,
ConstBufferSequence const& bs, WriteHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
"AsyncStream requirements not met");
static_assert(beast::is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
@ -792,7 +925,7 @@ template<class NextLayer>
void
stream<NextLayer>::
do_read_fh(detail::frame_streambuf& fb,
close_code& code, error_code& ec)
close_code::value& code, error_code& ec)
{
fb.commit(boost::asio::read(
stream_, fb.prepare(2), ec));

View File

@ -61,7 +61,8 @@ class stream<NextLayer>::write_frame_op
fh.rsv2 = 0;
fh.rsv3 = 0;
fh.len = boost::asio::buffer_size(cb);
if((fh.mask = (ws.role_ == role_type::client)))
fh.mask = ws.role_ == role_type::client;
if(fh.mask)
{
fh.key = ws.maskgen_();
detail::prepare_key(key, fh.key);

View File

@ -8,7 +8,7 @@
#ifndef BEAST_WEBSOCKET_RFC6455_HPP
#define BEAST_WEBSOCKET_RFC6455_HPP
#include <beast/websocket/static_string.hpp>
#include <beast/static_string.hpp>
#include <boost/optional.hpp>
#include <array>
#include <cstdint>
@ -44,7 +44,9 @@ enum class opcode : std::uint8_t
@see RFC 6455 7.4.1 Defined Status Codes
https://tools.ietf.org/html/rfc6455#section-7.4.1
*/
enum class close_code : std::uint16_t
namespace close_code {
using value = std::uint16_t;
enum
{
// used internally to mean "no error"
none = 0,
@ -69,6 +71,7 @@ enum class close_code : std::uint16_t
last = 5000 // satisfy warnings
};
} // close_code
#if ! GENERATING_DOCS
@ -89,7 +92,7 @@ using ping_payload_type =
struct close_reason
{
/// The close code.
close_code code = close_code::none;
close_code::value code = close_code::none;
/// The optional utf8-encoded reason string.
reason_string_type reason;
@ -102,7 +105,7 @@ struct close_reason
close_reason() = default;
/// Construct from a code.
close_reason(close_code code_)
close_reason(close_code::value code_)
: code(code_)
{
}
@ -117,7 +120,7 @@ struct close_reason
/// Construct from a code and reason.
template<class CharT>
close_reason(close_code code_,
close_reason(close_code::value code_,
CharT const* reason_)
: code(code_)
, reason(reason_)

View File

@ -1,337 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_WEBSOCKET_STATIC_STRING_HPP
#define BEAST_WEBSOCKET_STATIC_STRING_HPP
#include <algorithm>
#include <array>
#include <cstdint>
#include <iterator>
#include <stdexcept>
#include <string>
namespace beast {
namespace websocket {
/** A string with a fixed-size storage area.
`static_string` objects behave like `std::string` except that
the storage is not dynamically allocated but rather fixed in
size.
These strings offer performance advantages when a protocol
imposes a natural small upper limit on the size of a value.
*/
template<std::size_t N, class CharT,
class Traits = std::char_traits<CharT>>
class static_string
{
std::size_t n_;
std::array<CharT, N+1> s_;
public:
using traits_type = Traits;
using value_type = typename Traits::char_type;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
using const_pointer = value_type const*;
using const_reference = value_type const&;
using iterator = value_type*;
using const_iterator = value_type const*;
using reverse_iterator =
std::reverse_iterator<iterator>;
using const_reverse_iterator =
std::reverse_iterator<const_iterator>;
static_string()
{
resize(0);
}
static_string(static_string const& s)
: n_(s.n_)
{
std::copy(&s.s_[0], &s.s_[0]+s.n_+1, &s_[0]);
}
static_string&
operator=(static_string const& s)
{
n_ = s.n_;
std::copy(&s.s_[0], &s.s_[0]+s.n_+1, &s_[0]);
return *this;
}
static_string(CharT const* s)
{
assign(s);
}
static_string& operator=(CharT const* s)
{
assign(s);
return *this;
}
reference
at(size_type pos)
{
if(pos >= n_)
throw std::out_of_range("static_string::at");
return s_[pos];
}
const_reference
at(size_type pos) const
{
if(pos >= n_)
throw std::out_of_range("static_string::at");
return s_[pos];
}
reference
operator[](size_type pos);
const_reference
operator[](size_type pos) const;
CharT&
front()
{
return s_[0];
}
CharT const&
front() const
{
return s_[0];
}
CharT&
back()
{
return s_[n_-1];
}
CharT const&
back() const
{
return s_[n_-1];
}
CharT*
data()
{
return &s_[0];
}
CharT const*
data() const
{
return &s_[0];
}
CharT const*
c_str() const
{
s_[n_] = 0;
return &s_[0];
}
iterator
begin()
{
return &s_[0];
}
const_iterator
begin() const
{
return &s_[0];
}
const_iterator
cbegin() const
{
return &s_[0];
}
iterator
end()
{
return &s_[n_];
}
const_iterator
end() const
{
return &s_[n_];
}
const_iterator
cend() const
{
return &s_[n_];
}
reverse_iterator
rbegin()
{
return reverse_iterator{end()};
}
const_reverse_iterator
rbegin() const
{
return reverse_iterator{end()};
}
const_reverse_iterator
crbegin() const
{
return reverse_iterator{cend()};
}
reverse_iterator
rend()
{
return reverse_iterator{begin()};
}
const_reverse_iterator
rend() const
{
return reverse_iterator{begin()};
}
const_reverse_iterator
crend() const
{
return reverse_iterator{cbegin()};
}
bool
empty() const
{
return n_ == 0;
}
size_type
size() const
{
return n_;
}
size_type constexpr
max_size() const
{
return N;
}
size_type
capacity() const
{
return N;
}
void
shrink_to_fit()
{
// no-op
}
void
clear()
{
resize(0);
}
// Does not perform value-initialization
void
resize(std::size_t n);
std::basic_string<CharT, Traits>
to_string() const
{
return std::basic_string<
CharT, Traits>{&s_[0], n_};
}
private:
void
assign(CharT const* s);
};
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
operator[](size_type pos) ->
reference
{
static CharT null{0};
if(pos == n_)
return null;
return s_[pos];
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
operator[](size_type pos) const ->
const_reference
{
static CharT constexpr null{0};
if(pos == n_)
return null;
return s_[pos];
}
template<std::size_t N, class CharT, class Traits>
void
static_string<N, CharT, Traits>::
resize(std::size_t n)
{
if(n > N)
throw std::length_error(
"static_string overflow");
n_ = n;
s_[n_] = 0;
}
template<std::size_t N, class CharT, class Traits>
void
static_string<N, CharT, Traits>::
assign(CharT const* s)
{
size_type n = 0;
for(auto p = s; *p; ++p)
++n;
if(n > N)
throw std::out_of_range(
"too large");
std::copy(s, s+n, s_.begin());
}
} // websocket
} // beast
#endif

View File

@ -12,6 +12,7 @@
#include <beast/websocket/detail/stream_base.hpp>
#include <beast/streambuf_readstream.hpp>
#include <beast/async_completion.hpp>
#include <beast/detail/get_lowest_layer.hpp>
#include <beast/http/message.hpp>
#include <beast/http/string_body.hpp>
#include <boost/asio.hpp>
@ -80,8 +81,7 @@ class stream : public detail::stream_base
{
friend class ws_test;
NextLayer next_layer_;
streambuf_readstream<NextLayer&, streambuf> stream_;
streambuf_readstream<NextLayer, streambuf> stream_;
public:
/// The type of the next layer.
@ -90,26 +90,26 @@ public:
/// The type of the lowest layer.
using lowest_layer_type =
typename next_layer_type::lowest_layer_type;
typename beast::detail::get_lowest_layer<
next_layer_type>::type;
/// The type of endpoint of the lowest layer.
using endpoint_type =
typename lowest_layer_type::endpoint_type;
/** Move-construct a stream.
/// The protocol of the next layer.
using protocol_type =
typename lowest_layer_type::protocol_type;
If @c NextLayer is move constructible, this function
will move-construct a new stream from the existing stream.
/// The type of resolver of the next layer.
using resolver_type =
typename protocol_type::resolver;
/// Move constructor.
@note The behavior of move assignment on or from streams
with active or pending operations is undefined.
*/
stream(stream&&) = default;
/** Move assignment.
Undefined behavior if operations are active or pending.
If @c NextLayer is move constructible, this function
will move-construct a new stream from the existing stream.
@note The behavior of move assignment on or from streams
with active or pending operations is undefined.
*/
stream& operator=(stream&&) = default;
@ -217,7 +217,7 @@ public:
boost::asio::io_service&
get_io_service()
{
return next_layer_.lowest_layer().get_io_service();
return stream_.get_io_service();
}
/** Get a reference to the next layer.
@ -231,7 +231,7 @@ public:
next_layer_type&
next_layer()
{
return next_layer_;
return stream_.next_layer();
}
/** Get a reference to the next layer.
@ -245,7 +245,7 @@ public:
next_layer_type const&
next_layer() const
{
return next_layer_;
return stream_.next_layer();
}
/** Get a reference to the lowest layer.
@ -259,7 +259,7 @@ public:
lowest_layer_type&
lowest_layer()
{
return next_layer_.lowest_layer();
return stream_.lowest_layer();
}
/** Get a reference to the lowest layer.
@ -273,7 +273,7 @@ public:
lowest_layer_type const&
lowest_layer() const
{
return next_layer_.lowest_layer();
return stream_.lowest_layer();
}
/** Returns the close reason received from the peer.
@ -309,12 +309,7 @@ public:
@throws boost::system::system_error Thrown on failure.
*/
void
accept()
{
error_code ec;
accept(boost::asio::null_buffers{}, ec);
detail::maybe_throw(ec, "accept");
}
accept();
/** Read and respond to a WebSocket HTTP Upgrade request.
@ -603,12 +598,7 @@ public:
*/
void
handshake(boost::string_ref const& host,
boost::string_ref const& resource)
{
error_code ec;
handshake(host, resource, ec);
detail::maybe_throw(ec, "upgrade");
}
boost::string_ref const& resource);
/** Send a WebSocket Upgrade request.
@ -692,12 +682,7 @@ public:
@param cr The reason for the close.
*/
void
close(close_reason const& cr)
{
error_code ec;
close(cr, ec);
detail::maybe_throw(ec, "close");
}
close(close_reason const& cr);
/** Perform a WebSocket close.
@ -773,12 +758,7 @@ public:
*/
template<class Streambuf>
void
read(opcode& op, Streambuf& streambuf)
{
error_code ec;
read(op, streambuf, ec);
detail::maybe_throw(ec, "read");
}
read(opcode& op, Streambuf& streambuf);
/** Read a message.
@ -868,12 +848,7 @@ public:
*/
template<class Streambuf>
void
read_frame(frame_info& fi, Streambuf& streambuf)
{
error_code ec;
read_frame(fi, streambuf, ec);
detail::maybe_throw(ec, "read_some");
}
read_frame(frame_info& fi, Streambuf& streambuf);
/** Read a message frame.
@ -978,12 +953,7 @@ public:
*/
template<class ConstBufferSequence>
void
write(ConstBufferSequence const& buffers)
{
error_code ec;
write(buffers, ec);
detail::maybe_throw(ec, "write");
}
write(ConstBufferSequence const& buffers);
/** Send a message.
@ -1081,12 +1051,7 @@ public:
*/
template<class ConstBufferSequence>
void
write_frame(bool fin, ConstBufferSequence const& buffers)
{
error_code ec;
write_frame(fin, buffers, ec);
detail::maybe_throw(ec, "write");
}
write_frame(bool fin, ConstBufferSequence const& buffers);
/** Send a frame.
@ -1180,7 +1145,7 @@ private:
void
do_read_fh(detail::frame_streambuf& fb,
close_code& code, error_code& ec);
close_code::value& code, error_code& ec);
};
} // websocket

View File

@ -7,5 +7,3 @@ echo "using toolset: $CC"
echo "using variant: $VARIANT"
$BOOST_ROOT/bjam toolset=$CC variant=$VARIANT
`find . -name "core_tests"`
`find . -name "http_tests"`

View File

@ -16,6 +16,7 @@ add_executable (core-tests
placeholders.cpp
prepare_buffers.cpp
static_streambuf.cpp
static_string.cpp
streambuf.cpp
streambuf_readstream.cpp
to_string.cpp
@ -54,21 +55,6 @@ if (NOT WIN32)
target_link_libraries(http-tests ${Boost_LIBRARIES})
endif()
add_executable (websocket-tests
${BEAST_INCLUDES}
main.cpp
websocket/error.cpp
websocket/option.cpp
websocket/rfc6455.cpp
websocket/static_string.cpp
websocket/teardown.cpp
websocket/utf8_checker.cpp
)
if (NOT WIN32)
target_link_libraries(websocket-tests ${Boost_LIBRARIES})
endif()
add_executable (parser-bench
${BEAST_INCLUDES}
main.cpp
@ -80,3 +66,31 @@ if (NOT WIN32)
target_link_libraries(parser-bench ${Boost_LIBRARIES})
endif()
add_executable (websocket-tests
${BEAST_INCLUDES}
websocket/websocket_async_echo_peer.hpp
websocket/websocket_sync_echo_peer.hpp
main.cpp
websocket/error.cpp
websocket/option.cpp
websocket/rfc6455.cpp
websocket/stream.cpp
websocket/teardown.cpp
websocket/utf8_checker.cpp
)
if (NOT WIN32)
target_link_libraries(websocket-tests ${Boost_LIBRARIES})
endif()
add_executable (websocket-echo
${BEAST_INCLUDES}
sig_wait.hpp
websocket/websocket_async_echo_peer.hpp
websocket/websocket_sync_echo_peer.hpp
websocket/websocket_echo.cpp
)
if (NOT WIN32)
target_link_libraries(websocket-echo ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
endif()

View File

@ -19,6 +19,7 @@ unit-test core-tests :
placeholders.cpp
prepare_buffers.cpp
static_streambuf.cpp
static_string.cpp
streambuf.cpp
streambuf_readstream.cpp
to_string.cpp
@ -48,18 +49,23 @@ unit-test http-tests :
http/write.cpp
;
unit-test websocket-tests :
main.cpp
websocket/error.cpp
websocket/option.cpp
websocket/rfc6455.cpp
websocket/static_string.cpp
websocket/teardown.cpp
websocket/utf8_checker.cpp
;
unit-test parser-bench :
main.cpp
http/nodejs_parser.cpp
http/parser_bench.cpp
;
unit-test websocket-tests :
main.cpp
websocket/error.cpp
websocket/option.cpp
websocket/rfc6455.cpp
websocket/stream.cpp
websocket/teardown.cpp
websocket/utf8_checker.cpp
;
exe websocket-echo :
websocket/websocket_echo.cpp
;

191
test/static_string.cpp Normal file
View File

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

View File

@ -7,3 +7,40 @@
// Test that header file is self-contained.
#include <beast/streambuf_readstream.hpp>
#include <beast/streambuf.hpp>
#include <beast/detail/unit_test/suite.hpp>
#include <boost/asio.hpp>
namespace beast {
class streambuf_readstream_test : public beast::detail::unit_test::suite
{
public:
void testSpecial()
{
using socket_type = boost::asio::ip::tcp::socket;
boost::asio::io_service ios;
{
streambuf_readstream<socket_type, streambuf> srs(ios);
streambuf_readstream<socket_type, streambuf> srs2(std::move(srs));
srs = std::move(srs2);
}
{
socket_type sock(ios);
streambuf_readstream<socket_type&, streambuf> srs(sock);
streambuf_readstream<socket_type&, streambuf> srs2(std::move(srs));
}
pass();
}
void run() override
{
testSpecial();
}
};
BEAST_DEFINE_TESTSUITE(streambuf_readstream,core,beast);
} // beast

View File

@ -1,9 +0,0 @@
//
// 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/static_string.hpp>

454
test/websocket/stream.cpp Normal file
View File

@ -0,0 +1,454 @@
//
// 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/stream.hpp>
#include "websocket_async_echo_peer.hpp"
#include "websocket_sync_echo_peer.hpp"
#include <beast/bind_handler.hpp>
#include <beast/streambuf.hpp>
#include <beast/detail/unit_test/suite.hpp>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/optional.hpp>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <thread>
#include <beast/http/parser.hpp>
namespace beast {
namespace websocket {
class stream_test : public beast::detail::unit_test::suite
{
boost::asio::io_service ios_;
boost::optional<boost::asio::io_service::work> work_;
std::thread thread_;
std::mutex m_;
std::condition_variable cv_;
bool running_ = false;;
public:
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
// meets the requirements of AsyncStream, SyncStream
class string_Stream
{
std::string s_;
boost::asio::io_service& ios_;
public:
string_Stream(boost::asio::io_service& ios,
std::string s)
: s_(s)
, ios_(ios)
{
}
boost::asio::io_service&
get_io_service()
{
return ios_;
}
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers)
{
error_code ec;
auto const n = read_some(buffers, ec);
if(ec)
throw boost::system::system_error{ec};
return n;
}
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers,
error_code& ec)
{
auto const n = boost::asio::buffer_copy(
buffers, boost::asio::buffer(s_));
s_.erase(0, n);
return n;
}
template<class MutableBufferSequence, class ReadHandler>
typename async_completion<ReadHandler,
void(std::size_t, error_code)>::result_type
async_read_some(MutableBufferSequence const& buffers,
ReadHandler&& handler)
{
auto const n = boost::asio::buffer_copy(
buffers, boost::asio::buffer(s_));
s_.erase(0, n);
async_completion<ReadHandler,
void(error_code, std::size_t)> completion(handler);
ios_.post(bind_handler(
completion.handler, error_code{}, n));
return completion.result.get();
}
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers)
{
error_code ec;
auto const n = write_some(buffers, ec);
if(ec)
throw boost::system::system_error{ec};
return n;
}
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers,
error_code&)
{
return boost::asio::buffer_size(buffers);
}
template<class ConstBuffeSequence, class WriteHandler>
typename async_completion<WriteHandler,
void(std::size_t, error_code)>::result_type
async_write_some(ConstBuffeSequence const& buffers,
WriteHandler&& handler)
{
async_completion<WriteHandler,
void(error_code, std::size_t)> completion(handler);
ios_.post(bind_handler(completion.handler,
error_code{}, boost::asio::buffer_size(buffers)));
return completion.result.get();
}
};
stream_test()
: work_(ios_)
, thread_([&]{ ios_.run(); })
{
}
~stream_test()
{
work_ = boost::none;
thread_.join();
}
template<class Function>
void exec(Function&& f)
{
{
std::lock_guard<std::mutex> lock(m_);
running_ = true;
}
boost::asio::spawn(ios_,
[&](boost::asio::yield_context do_yield)
{
f(do_yield);
std::lock_guard<std::mutex> lock(m_);
running_ = false;
cv_.notify_all();
}
, boost::coroutines::attributes(2 * 1024 * 1024));
std::unique_lock<std::mutex> lock(m_);
cv_.wait(lock, [&]{ return ! running_; });
}
void testSpecialMembers()
{
stream<socket_type> ws(ios_);
{
stream<socket_type> ws2(std::move(ws));
}
{
stream<socket_type> ws2(ios_);
ws = std::move(ws2);
}
pass();
}
void testOptions()
{
stream<socket_type> ws(ios_);
ws.set_option(message_type(opcode::binary));
ws.set_option(read_buffer_size(8192));
ws.set_option(read_message_max(1 * 1024 * 1024));
ws.set_option(write_buffer_size(2048));
pass();
}
template<std::size_t N>
static
boost::asio::const_buffers_1
strbuf(const char (&s)[N])
{
return boost::asio::const_buffers_1(&s[0], N-1);
}
void testAccept(boost::asio::yield_context do_yield)
{
{
stream<string_Stream> ws(ios_,
"GET / HTTP/1.1\r\n"
"Host: localhost:80\r\n"
"Upgrade: WebSocket\r\n"
"Connection: upgrade\r\n"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n");
try
{
ws.accept();
pass();
}
catch(...)
{
fail();
}
}
{
stream<string_Stream> ws(ios_,
"GET / HTTP/1.1\r\n"
"\r\n");
try
{
ws.accept();
fail();
}
catch(...)
{
pass();
}
}
{
stream<string_Stream> ws(ios_,
"GET / HTTP/1.1\r\n"
"Host: localhost:80\r\n"
"Upgrade: WebSocket\r\n"
"Connection: upgrade\r\n"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n");
error_code ec;
ws.accept(ec);
expect(! ec, ec.message());
}
{
stream<string_Stream> ws(ios_,
"GET / HTTP/1.1\r\n"
"\r\n");
error_code ec;
ws.accept(ec);
expect(ec);
}
{
stream<string_Stream> ws(ios_,
"GET / HTTP/1.1\r\n"
"Host: localhost:80\r\n"
"Upgrade: WebSocket\r\n"
"Connection: upgrade\r\n"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n");
error_code ec;
ws.async_accept(do_yield[ec]);
expect(! ec, ec.message());
}
{
stream<string_Stream> ws(ios_,
"GET / HTTP/1.1\r\n"
"\r\n");
error_code ec;
ws.async_accept(do_yield[ec]);
expect(ec);
}
{
stream<string_Stream> ws(ios_,
"Host: localhost:80\r\n"
"Upgrade: WebSocket\r\n"
"Connection: upgrade\r\n"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n");
try
{
ws.accept(strbuf(
"GET / HTTP/1.1\r\n"));
pass();
}
catch(...)
{
fail();
}
}
{
stream<string_Stream> ws(ios_,
"\r\n");
try
{
ws.accept(strbuf(
"GET / HTTP/1.1\r\n"));
fail();
}
catch(...)
{
pass();
}
}
{
stream<string_Stream> ws(ios_,
"Host: localhost:80\r\n"
"Upgrade: WebSocket\r\n"
"Connection: upgrade\r\n"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n");
error_code ec;
ws.accept(strbuf(
"GET / HTTP/1.1\r\n"), ec);
expect(! ec, ec.message());
}
{
stream<string_Stream> ws(ios_,
"GET / HTTP/1.1\r\n"
"\r\n");
error_code ec;
ws.accept(ec);
expect(ec);
}
{
stream<string_Stream> ws(ios_,
"Host: localhost:80\r\n"
"Upgrade: WebSocket\r\n"
"Connection: upgrade\r\n"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n");
error_code ec;
ws.async_accept(strbuf(
"GET / HTTP/1.1\r\n"), do_yield[ec]);
expect(! ec, ec.message());
}
{
stream<string_Stream> ws(ios_,
"\r\n");
error_code ec;
ws.async_accept(strbuf(
"GET / HTTP/1.1\r\n"), do_yield[ec]);
expect(ec);
}
}
void testHandshake(endpoint_type const& ep,
boost::asio::yield_context do_yield)
{
{
// disconnected socket
socket_type sock(ios_);
stream<decltype(sock)&> ws(sock);
try
{
ws.handshake("localhost", "/");
fail();
}
catch(boost::system::system_error const&)
{
pass();
}
catch(...)
{
fail();
}
error_code ec;
ws.handshake("localhost", "/", ec);
if(! expect(ec))
return;
ws.async_handshake("localhost", "/", do_yield[ec]);
if(! expect(ec))
return;
}
{
error_code ec;
socket_type sock(ios_);
sock.connect(ep, ec);
if(! expect(! ec, ec.message()))
return;
stream<decltype(sock)&> ws(sock);
ws.handshake("localhost", "/", ec);
if(! expect(! ec, ec.message()))
return;
ws.close({}, ec);
if(! expect(! ec, ec.message()))
return;
streambuf sb;
opcode op;
ws.read(op, sb, ec);
if(! expect(ec == error::closed, ec.message()))
return;
expect(ws.reason().code == close_code::normal);
}
{
error_code ec;
socket_type sock(ios_);
sock.connect(ep, ec);
if(! expect(! ec, ec.message()))
return;
stream<decltype(sock)&> ws(sock);
ws.async_handshake("localhost", "/", do_yield[ec]);
if(! expect(! ec, ec.message()))
return;
ws.async_close({}, do_yield[ec]);
if(! expect(! ec, ec.message()))
return;
streambuf sb;
opcode op;
ws.async_read(op, sb, do_yield[ec]);
if(! expect(ec == error::closed, ec.message()))
return;
expect(ws.reason().code == close_code::normal);
}
}
void run() override
{
testSpecialMembers();
testOptions();
exec(std::bind(&stream_test::testAccept,
this, std::placeholders::_1));
auto const any = endpoint_type{
address_type::from_string("127.0.0.1"), 0};
{
sync_echo_peer server(true, any);
exec(std::bind(&stream_test::testHandshake,
this, server.local_endpoint(),
std::placeholders::_1));
}
{
async_echo_peer server(true, any, 1);
exec(std::bind(&stream_test::testHandshake,
this, server.local_endpoint(),
std::placeholders::_1));
}
pass();
}
};
BEAST_DEFINE_TESTSUITE(stream,websocket,beast);
} // websocket
} // beast

View File

@ -59,6 +59,8 @@ public:
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(
@ -87,6 +89,12 @@ public:
t.join();
}
endpoint_type
local_endpoint() const
{
return acceptor_.local_endpoint();
}
private:
class Peer
{

View File

@ -19,7 +19,7 @@
#include "websocket_async_echo_peer.hpp"
#include "websocket_sync_echo_peer.hpp"
#include "sig_wait.hpp"
#include "../sig_wait.hpp"
int main()
{

View File

@ -17,8 +17,8 @@
*/
//==============================================================================
#ifndef BEAST_WSPROTO_SYNC_ECHO_PEER_H_INCLUDED
#define BEAST_WSPROTO_SYNC_ECHO_PEER_H_INCLUDED
#ifndef BEAST_WEBSOCKET_SYNC_ECHO_PEER_H_INCLUDED
#define BEAST_WEBSOCKET_SYNC_ECHO_PEER_H_INCLUDED
#include <beast/streambuf.hpp>
#include <beast/websocket.hpp>
@ -55,6 +55,8 @@ public:
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(
@ -74,6 +76,12 @@ public:
thread_.join();
}
endpoint_type
local_endpoint() const
{
return acceptor_.local_endpoint();
}
private:
static
void