Improvements to increase code coverage:

* Don't include the test code in coverage reports
* Add test code for missing coverage

Other:

* Improve the README.md
* Fix warning in sha1_context
* Tidy up the examples use of namespaces
* Various fixes to documentation and javadocs
This commit is contained in:
Vinnie Falco
2016-05-04 17:27:50 -04:00
parent 231988622c
commit 133e7a3b16
49 changed files with 1054 additions and 447 deletions

View File

@@ -1,4 +1,8 @@
# Beast [![Build Status](https://travis-ci.org/vinniefalco/Beast.svg?branch=master)](https://travis-ci.org/vinniefalco/Beast) [![codecov](https://codecov.io/gh/sublimator/Beast/branch/master/graph/badge.svg)](https://codecov.io/gh/sublimator/Beast) # Beast
[![Build Status](https://travis-ci.org/vinniefalco/Beast.svg?branch=master)](https://travis-ci.org/vinniefalco/Beast) [![codecov]
(https://codecov.io/gh/vinniefalco/Beast/branch/master/graph/badge.svg)](https://codecov.io/gh/vinniefalco/Beast) [![Documentation]
(https://img.shields.io/badge/documentation-master-brightgreen.svg)](http://vinniefalco.github.io/beast/)
Beast provides implementations of the HTTP and WebSocket protocols Beast provides implementations of the HTTP and WebSocket protocols
built on top of Boost.Asio and other parts of boost. built on top of Boost.Asio and other parts of boost.
@@ -9,10 +13,69 @@ Requirements:
* C++11 or greater * C++11 or greater
* OpenSSL (optional) * OpenSSL (optional)
Linking applications with beast: Example WebSocket program:
```C++
#include <beast/to_string.hpp>
#include <beast/websocket.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <string>
You need to include the .cpp file `src/beast_http_nodejs_parser.cpp` int main()
in the build script or Makefile for your program in order to link. {
// Normal boost::asio setup
std::string const host = "echo.websocket.org";
boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r(ios);
boost::asio::ip::tcp::socket sock(ios);
boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"}));
// WebSocket connect and send message using beast
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws(sock);
ws.handshake(host, "/");
ws.write(boost::asio::buffer("Hello, world!"));
// Receive WebSocket message, print and close using beast
beast::streambuf sb;
beast::websocket::opcode op;
ws.read(op, sb);
ws.close(beast::websocket::close_code::normal);
std::cout << to_string(sb.data()) << "\n";
}
```
Example HTTP program:
```C++
#include <beast/http.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <string>
int main()
{
// Normal boost::asio setup
std::string const host = "boost.org";
boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r(ios);
boost::asio::ip::tcp::socket sock(ios);
boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
// Send HTTP request using beast
beast::http::request_v1<beast::http::empty_body> req({"GET", "/", 11});
req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port()));
req.headers.replace("User-Agent", "Beast");
beast::http::prepare(req);
beast::http::write(sock, req);
// Receive and print HTTP response using beast
beast::streambuf sb;
beast::http::response_v1<beast::http::streambuf_body> resp;
beast::http::read(sock, sb, resp);
std::cout << resp;
}
```
Links: Links:

View File

@@ -2,6 +2,7 @@
General: General:
* Use SFINAE on return values (search for "class =") * Use SFINAE on return values (search for "class =")
* Remove http,websocket error_code types and use the one in <beast/error.hpp>
Boost.Http Boost.Http
* Use enum instead of bool in isRequest * Use enum instead of bool in isRequest
@@ -17,6 +18,8 @@ Docs:
- See if we can include them now that xsl is fixed - See if we can include them now that xsl is fixed
* Implement cleanup-param to remove spaces around template arguments * Implement cleanup-param to remove spaces around template arguments
e.g. in basic_streambuf move constructor members e.g. in basic_streambuf move constructor members
* Don't put using namespace at file scope in examples,
do something like "using ba = boost::asio" instead.
Core: Core:
* Replace Jamroot with Jamfile * Replace Jamroot with Jamfile
@@ -26,13 +29,13 @@ Core:
WebSocket: WebSocket:
* optimized versions of key/masking, choose prepared_key size * optimized versions of key/masking, choose prepared_key size
* invokable unit test * invokable unit test
* Finish up all of static_string including tests * Don't try to read requests into empty_body
* Don't rely on default constructible mutable buffers
type in read_frame_op (smb_type). To see the error, use
boost::asio::streambuf in websocket_async_echo_peer
HTTP: HTTP:
* Define Parser concept in HTTP * Define Parser concept in HTTP
- Need parse version of read() so caller can set parser options
like maximum size of headers, maximum body size, etc
* trim public interface of rfc2616.h to essentials only * trim public interface of rfc2616.h to essentials only
* add bool should_close(message_v1 const&) to replace the use * add bool should_close(message_v1 const&) to replace the use
of eof return value from write and async_write of eof return value from write and async_write
@@ -40,7 +43,7 @@ HTTP:
* More fine grained parser errors * More fine grained parser errors
* HTTP parser size limit with test (configurable?) * HTTP parser size limit with test (configurable?)
* HTTP parser trailers with test * HTTP parser trailers with test
* URL parser, strong URL checking in HTTP parser * URL parser, strong URL character checking in HTTP parser
* Update for rfc7230 * Update for rfc7230
* Consider rename to MessageBody concept * Consider rename to MessageBody concept
* Fix prepare() calling content_length() without init() * Fix prepare() calling content_length() without init()
@@ -48,3 +51,7 @@ HTTP:
* Complete allocator testing in basic_streambuf, basic_headers * Complete allocator testing in basic_streambuf, basic_headers
* Fix http::async_write op, case 3 should break at the end. * Fix http::async_write op, case 3 should break at the end.
* Add tests for writer using the resume function / coros * Add tests for writer using the resume function / coros
* Custom HTTP error codes for various situations
* Make empty_body write-only, remove reader nested type
* Add concepts WritableBody ReadableBody with type checks,
check them in read and write functions

View File

@@ -112,19 +112,17 @@ int main()
boost::asio::connect(sock, boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"})); r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
using namespace beast::http;
// Send HTTP request using beast // Send HTTP request using beast
request<empty_body> req({"GET", "/", 11}); beast::http::request_v1<beast::http::empty_body> req({"GET", "/", 11});
req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port())); req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port()));
req.headers.replace("User-Agent", "Beast"); req.headers.replace("User-Agent", "Beast");
prepare(req); beast::http::prepare(req);
write(sock, req); beast::http::write(sock, req);
// Receive and print HTTP response using beast // Receive and print HTTP response using beast
beast::streambuf sb; beast::streambuf sb;
response<streambuf_body> resp; beast::http::response_v1<beast::http::streambuf_body> resp;
read(sock, sb, resp); beast::http::read(sock, sb, resp);
std::cout << resp; std::cout << resp;
} }
``` ```
@@ -147,18 +145,16 @@ int main()
boost::asio::connect(sock, boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"})); r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"}));
using namespace beast::websocket;
// WebSocket connect and send message using beast // WebSocket connect and send message using beast
stream<boost::asio::ip::tcp::socket&> ws(sock); beast::websocket::stream<boost::asio::ip::tcp::socket&> ws(sock);
ws.handshake(host, "/"); ws.handshake(host, "/");
ws.write(boost::asio::buffer("Hello, world!")); ws.write(boost::asio::buffer("Hello, world!"));
// Receive WebSocket message, print and close using beast // Receive WebSocket message, print and close using beast
beast::streambuf sb; beast::streambuf sb;
opcode op; beast::websocket::opcode op;
ws.read(op, sb); ws.read(op, sb);
ws.close(close_code::normal); ws.close(beast::websocket::close_code::normal);
std::cout << to_string(sb.data()) << "\n"; std::cout << to_string(sb.data()) << "\n";
} }
``` ```

View File

@@ -44,7 +44,7 @@ libraries. It is not designed to be end-user facing. There is no convenient
class that implements the core of a web server, nor is there a convenient class that implements the core of a web server, nor is there a convenient
class to quickly perform common operations such as fetching a file or class to quickly perform common operations such as fetching a file or
connecting and retrieving a document from a secure connection. These connecting and retrieving a document from a secure connection. These
use-cases are important. But this library does not try to do that. Instead, use-cases are important, but this library does not try to do that. Instead,
it offers primitives that can be used to build those user-facing algorithms. it offers primitives that can be used to build those user-facing algorithms.
A HTTP message (referred to hereafter as "message") contains request or A HTTP message (referred to hereafter as "message") contains request or

View File

@@ -28,7 +28,7 @@ using namespace boost::asio;
template<class String> template<class String>
void void
err(error_code const& ec, String const& what) err(beast::error_code const& ec, String const& what)
{ {
std::cerr << what << ": " << ec.message() << std::endl; std::cerr << what << ": " << ec.message() << std::endl;
} }

View File

@@ -20,18 +20,16 @@ int main()
boost::asio::connect(sock, boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"})); r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
using namespace beast::http;
// Send HTTP request using beast // Send HTTP request using beast
request_v1<empty_body> req({"GET", "/", 11}); beast::http::request_v1<beast::http::empty_body> req({"GET", "/", 11});
req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port())); req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port()));
req.headers.replace("User-Agent", "Beast"); req.headers.replace("User-Agent", "Beast");
prepare(req); beast::http::prepare(req);
write(sock, req); beast::http::write(sock, req);
// Receive and print HTTP response using beast // Receive and print HTTP response using beast
beast::streambuf sb; beast::streambuf sb;
response_v1<streambuf_body> resp; beast::http::response_v1<beast::http::streambuf_body> resp;
read(sock, sb, resp); beast::http::read(sock, sb, resp);
std::cout << resp; std::cout << resp;
} }

View File

@@ -21,17 +21,15 @@ int main()
boost::asio::connect(sock, boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"})); r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"}));
using namespace beast::websocket;
// WebSocket connect and send message using beast // WebSocket connect and send message using beast
stream<boost::asio::ip::tcp::socket&> ws(sock); beast::websocket::stream<boost::asio::ip::tcp::socket&> ws(sock);
ws.handshake(host, "/"); ws.handshake(host, "/");
ws.write(boost::asio::buffer("Hello, world!")); ws.write(boost::asio::buffer("Hello, world!"));
// Receive WebSocket message, print and close using beast // Receive WebSocket message, print and close using beast
beast::streambuf sb; beast::streambuf sb;
opcode op; beast::websocket::opcode op;
ws.read(op, sb); ws.read(op, sb);
ws.close(close_code::normal); ws.close(beast::websocket::close_code::normal);
std::cout << to_string(sb.data()) << "\n"; std::cout << to_string(sb.data()) << "\n";
} }

View File

@@ -31,7 +31,6 @@ namespace beast {
template<class MutableBufferSequence> template<class MutableBufferSequence>
class buffers_adapter class buffers_adapter
{ {
private:
static_assert(is_MutableBufferSequence<MutableBufferSequence>::value, static_assert(is_MutableBufferSequence<MutableBufferSequence>::value,
"MutableBufferSequence requirements not met"); "MutableBufferSequence requirements not met");

View File

@@ -5,18 +5,20 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// //
#ifndef BEAST_HTTP_ERROR_HPP #ifndef BEAST_ERROR_HPP
#define BEAST_HTTP_ERROR_HPP #define BEAST_ERROR_HPP
#include <boost/system/error_code.hpp> #include <boost/system/error_code.hpp>
#include <boost/system/system_error.hpp> #include <boost/system/system_error.hpp>
namespace beast { namespace beast {
namespace http {
/// The type of error code used by the library
using error_code = boost::system::error_code; using error_code = boost::system::error_code;
} // http /// The type of system error thrown by the library
using system_error = boost::system::system_error;
} // beast } // beast
#endif #endif

View File

@@ -12,7 +12,6 @@
#include <beast/http/basic_parser_v1.hpp> #include <beast/http/basic_parser_v1.hpp>
#include <beast/http/body_type.hpp> #include <beast/http/body_type.hpp>
#include <beast/http/empty_body.hpp> #include <beast/http/empty_body.hpp>
#include <beast/http/error.hpp>
#include <beast/http/headers.hpp> #include <beast/http/headers.hpp>
#include <beast/http/message.hpp> #include <beast/http/message.hpp>
#include <beast/http/message_v1.hpp> #include <beast/http/message_v1.hpp>

View File

@@ -11,7 +11,7 @@
// Convenience header to include everything // Convenience header to include everything
// needed when declarating a user defined Body type. // needed when declarating a user defined Body type.
#include <beast/http/error.hpp> #include <beast/error.hpp>
#include <beast/http/message.hpp> #include <beast/http/message.hpp>
#include <beast/http/resume_context.hpp> #include <beast/http/resume_context.hpp>
#include <boost/logic/tribool.hpp> #include <boost/logic/tribool.hpp>

View File

@@ -149,7 +149,9 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
case s_req_url_start: case s_req_url_start:
if(ch == ' ') if(ch == ' ')
return err(parse_error::bad_uri); return err(parse_error::bad_uri);
// VFALCO TODO Require valid URL character // VFALCO TODO Better checking for valid URL characters
if(! is_text(ch))
return err(parse_error::bad_uri);
if(cb(&self::call_on_uri)) if(cb(&self::call_on_uri))
return used(); return used();
s_ = s_req_url; s_ = s_req_url;
@@ -163,7 +165,9 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
s_ = s_req_http_start; s_ = s_req_http_start;
break; break;
} }
// VFALCO TODO Require valid URL character // VFALCO TODO Better checking for valid URL characters
if(! is_text(ch))
return err(parse_error::bad_uri);
break; break;
case s_req_http_start: case s_req_http_start:

View File

@@ -212,6 +212,18 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class SyncReadStream, class Streambuf,
bool isRequest, class Body, class Headers>
void
read(SyncReadStream& stream, Streambuf& streambuf,
message_v1<isRequest, Body, Headers>& msg)
{
error_code ec;
read(stream, streambuf, msg, ec);
if(ec)
throw boost::system::system_error{ec};
}
template<class SyncReadStream, class Streambuf, template<class SyncReadStream, class Streambuf,
bool isRequest, class Body, class Headers> bool isRequest, class Body, class Headers>
void void

View File

@@ -8,8 +8,7 @@
#ifndef BEAST_HTTP_PARSE_ERROR_HPP #ifndef BEAST_HTTP_PARSE_ERROR_HPP
#define BEAST_HTTP_PARSE_ERROR_HPP #define BEAST_HTTP_PARSE_ERROR_HPP
#include <beast/http/error.hpp> #include <beast/error.hpp>
#include <boost/system/error_code.hpp>
namespace beast { namespace beast {
namespace http { namespace http {

View File

@@ -8,8 +8,8 @@
#ifndef BEAST_HTTP_PARSER_V1_HPP #ifndef BEAST_HTTP_PARSER_V1_HPP
#define BEAST_HTTP_PARSER_V1_HPP #define BEAST_HTTP_PARSER_V1_HPP
#include <beast/error.hpp>
#include <beast/http/basic_parser_v1.hpp> #include <beast/http/basic_parser_v1.hpp>
#include <beast/http/error.hpp>
#include <beast/http/message_v1.hpp> #include <beast/http/message_v1.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <functional> #include <functional>

View File

@@ -8,7 +8,7 @@
#ifndef BEAST_HTTP_READ_HPP #ifndef BEAST_HTTP_READ_HPP
#define BEAST_HTTP_READ_HPP #define BEAST_HTTP_READ_HPP
#include <beast/http/error.hpp> #include <beast/error.hpp>
#include <beast/http/parser_v1.hpp> #include <beast/http/parser_v1.hpp>
#include <beast/async_completion.hpp> #include <beast/async_completion.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
@@ -47,13 +47,7 @@ template<class SyncReadStream, class Streambuf,
bool isRequest, class Body, class Headers> bool isRequest, class Body, class Headers>
void void
read(SyncReadStream& stream, Streambuf& streambuf, read(SyncReadStream& stream, Streambuf& streambuf,
message_v1<isRequest, Body, Headers>& msg) message_v1<isRequest, Body, Headers>& msg);
{
error_code ec;
read(stream, streambuf, msg, ec);
if(ec)
throw boost::system::system_error{ec};
}
/** Read a HTTP/1 message from a stream. /** Read a HTTP/1 message from a stream.

View File

@@ -8,7 +8,7 @@
#ifndef BEAST_HTTP_TYPE_CHECK_HPP #ifndef BEAST_HTTP_TYPE_CHECK_HPP
#define BEAST_HTTP_TYPE_CHECK_HPP #define BEAST_HTTP_TYPE_CHECK_HPP
#include <beast/http/error.hpp> #include <beast/error.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>

View File

@@ -8,7 +8,7 @@
#ifndef BEAST_HTTP_WRITE_HPP #ifndef BEAST_HTTP_WRITE_HPP
#define BEAST_HTTP_WRITE_HPP #define BEAST_HTTP_WRITE_HPP
#include <beast/http/error.hpp> #include <beast/error.hpp>
#include <beast/http/message_v1.hpp> #include <beast/http/message_v1.hpp>
#include <beast/async_completion.hpp> #include <beast/async_completion.hpp>
#include <boost/system/error_code.hpp> #include <boost/system/error_code.hpp>
@@ -33,7 +33,7 @@ namespace http {
The implementation will automatically perform chunk encoding if The implementation will automatically perform chunk encoding if
the contents of the message indicate that chunk encoding is required. the contents of the message indicate that chunk encoding is required.
If the semantics of the message indicate that the connection should If the semantics of the message indicate that the connection should
be closed after the message is sent, the error returns from this be closed after the message is sent, the error thrown from this
function will be `boost::asio::error::eof`. function will be `boost::asio::error::eof`.
@param stream The stream to which the data is to be written. @param stream The stream to which the data is to be written.
@@ -64,7 +64,7 @@ write(SyncWriteStream& stream,
The implementation will automatically perform chunk encoding if The implementation will automatically perform chunk encoding if
the contents of the message indicate that chunk encoding is required. the contents of the message indicate that chunk encoding is required.
If the semantics of the message indicate that the connection should If the semantics of the message indicate that the connection should
be closed after the message is sent, the error returns from this be closed after the message is sent, the error returned from this
function will be `boost::asio::error::eof`. function will be `boost::asio::error::eof`.
@param stream The stream to which the data is to be written. @param stream The stream to which the data is to be written.

View File

@@ -582,7 +582,7 @@ basic_streambuf<Allocator>::prepare(size_type n) ->
list_.push_back(e); list_.push_back(e);
if(out_ == list_.end()) if(out_ == list_.end())
out_ = list_.iterator_to(e); out_ = list_.iterator_to(e);
if(n > e.size()) if(n >= e.size())
{ {
out_end_ = e.size(); out_end_ = e.size();
n -= e.size(); n -= e.size();

View File

@@ -469,9 +469,8 @@ template<class MutableBufferSequence>
void void
buffers_adapter<MutableBufferSequence>::consume(std::size_t n) buffers_adapter<MutableBufferSequence>::consume(std::size_t n)
{ {
for(;;) using boost::asio::buffer_size;
{ while(begin_ != out_)
if(begin_ != out_)
{ {
auto const avail = auto const avail =
buffer_size(*begin_) - in_pos_; buffer_size(*begin_) - in_pos_;
@@ -479,15 +478,13 @@ buffers_adapter<MutableBufferSequence>::consume(std::size_t n)
{ {
in_size_ -= n; in_size_ -= n;
in_pos_ += n; in_pos_ += n;
break; return;
} }
n -= avail; n -= avail;
in_size_ -= avail; in_size_ -= avail;
in_pos_ = 0; in_pos_ = 0;
++begin_; ++begin_;
} }
else
{
auto const avail = out_pos_ - in_pos_; auto const avail = out_pos_ - in_pos_;
if(n < avail) if(n < avail)
{ {
@@ -497,22 +494,8 @@ buffers_adapter<MutableBufferSequence>::consume(std::size_t n)
else else
{ {
in_size_ -= avail; in_size_ -= avail;
if(out_pos_ != out_end_||
out_ != std::prev(bs_.end()))
{
in_pos_ = out_pos_; in_pos_ = out_pos_;
} }
else
{
// Use the whole buffer now.
in_pos_ = 0;
out_pos_ = 0;
out_end_ = 0;
}
}
break;
}
}
} }
} // beast } // beast

View File

@@ -279,13 +279,6 @@ public:
return N; return N;
} }
/// Reduces memory usage by freeing unused memory.
void
shrink_to_fit()
{
// no-op
}
/// Clears the contents. /// Clears the contents.
void void
clear() clear()
@@ -310,7 +303,8 @@ public:
/// Compare two character sequences. /// Compare two character sequences.
template<std::size_t M> template<std::size_t M>
int compare(static_string<M, CharT, Traits> const& rhs) const; int
compare(static_string<M, CharT, Traits> const& rhs) const;
/// Return the characters as a `basic_string`. /// Return the characters as a `basic_string`.
std::basic_string<CharT, Traits> std::basic_string<CharT, Traits>
@@ -488,7 +482,8 @@ assign(CharT const* s)
namespace detail { namespace detail {
template<std::size_t N, std::size_t M, class CharT, class Traits> template<std::size_t N, std::size_t M, class CharT, class Traits>
int compare( int
compare(
static_string<N, CharT, Traits> const& lhs, static_string<N, CharT, Traits> const& lhs,
const CharT (&s)[M]) const CharT (&s)[M])
{ {
@@ -512,27 +507,13 @@ int compare(
} }
template<std::size_t N, std::size_t M, class CharT, class Traits> template<std::size_t N, std::size_t M, class CharT, class Traits>
int compare( inline
int
compare(
const CharT (&s)[M], const CharT (&s)[M],
static_string<N, CharT, Traits> const& rhs) static_string<N, CharT, Traits> const& rhs)
{ {
if(M-1 < rhs.size()) return -compare(rhs, s);
{
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 } // detail
@@ -540,7 +521,8 @@ int compare(
#if ! GENERATING_DOCS #if ! GENERATING_DOCS
template<std::size_t N, std::size_t M, class CharT, class Traits> template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator==( bool
operator==(
static_string<N, CharT, Traits> const& lhs, static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs) static_string<M, CharT, Traits> const& rhs)
{ {
@@ -548,7 +530,8 @@ bool operator==(
} }
template<std::size_t N, std::size_t M, class CharT, class Traits> template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator!=( bool
operator!=(
static_string<N, CharT, Traits> const& lhs, static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs) static_string<M, CharT, Traits> const& rhs)
{ {
@@ -556,7 +539,8 @@ bool operator!=(
} }
template<std::size_t N, std::size_t M, class CharT, class Traits> template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator<( bool
operator<(
static_string<N, CharT, Traits> const& lhs, static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs) static_string<M, CharT, Traits> const& rhs)
{ {
@@ -564,7 +548,8 @@ bool operator<(
} }
template<std::size_t N, std::size_t M, class CharT, class Traits> template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator<=( bool
operator<=(
static_string<N, CharT, Traits> const& lhs, static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs) static_string<M, CharT, Traits> const& rhs)
{ {
@@ -572,7 +557,8 @@ bool operator<=(
} }
template<std::size_t N, std::size_t M, class CharT, class Traits> template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator>( bool
operator>(
static_string<N, CharT, Traits> const& lhs, static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs) static_string<M, CharT, Traits> const& rhs)
{ {
@@ -580,7 +566,8 @@ bool operator>(
} }
template<std::size_t N, std::size_t M, class CharT, class Traits> template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator>=( bool
operator>=(
static_string<N, CharT, Traits> const& lhs, static_string<N, CharT, Traits> const& lhs,
static_string<M, CharT, Traits> const& rhs) static_string<M, CharT, Traits> const& rhs)
{ {
@@ -590,7 +577,8 @@ bool operator>=(
//--- //---
template<std::size_t N, std::size_t M, class CharT, class Traits> template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator==( bool
operator==(
const CharT (&s)[N], const CharT (&s)[N],
static_string<M, CharT, Traits> const& rhs) static_string<M, CharT, Traits> const& rhs)
{ {
@@ -598,7 +586,8 @@ bool operator==(
} }
template<std::size_t N, class CharT, class Traits, std::size_t M> template<std::size_t N, class CharT, class Traits, std::size_t M>
bool operator==( bool
operator==(
static_string<N, CharT, Traits> const& lhs, static_string<N, CharT, Traits> const& lhs,
const CharT (&s)[M]) const CharT (&s)[M])
{ {
@@ -606,7 +595,8 @@ bool operator==(
} }
template<std::size_t N, std::size_t M, class CharT, class Traits> template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator!=( bool
operator!=(
const CharT (&s)[N], const CharT (&s)[N],
static_string<M, CharT, Traits> const& rhs) static_string<M, CharT, Traits> const& rhs)
{ {
@@ -614,7 +604,8 @@ bool operator!=(
} }
template<std::size_t N, class CharT, class Traits, std::size_t M> template<std::size_t N, class CharT, class Traits, std::size_t M>
bool operator!=( bool
operator!=(
static_string<N, CharT, Traits> const& lhs, static_string<N, CharT, Traits> const& lhs,
const CharT (&s)[M]) const CharT (&s)[M])
{ {
@@ -622,7 +613,8 @@ bool operator!=(
} }
template<std::size_t N, std::size_t M, class CharT, class Traits> template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator<( bool
operator<(
const CharT (&s)[N], const CharT (&s)[N],
static_string<M, CharT, Traits> const& rhs) static_string<M, CharT, Traits> const& rhs)
{ {
@@ -630,7 +622,8 @@ bool operator<(
} }
template<std::size_t N, class CharT, class Traits, std::size_t M> template<std::size_t N, class CharT, class Traits, std::size_t M>
bool operator<( bool
operator<(
static_string<N, CharT, Traits> const& lhs, static_string<N, CharT, Traits> const& lhs,
const CharT (&s)[M]) const CharT (&s)[M])
{ {
@@ -638,7 +631,8 @@ bool operator<(
} }
template<std::size_t N, std::size_t M, class CharT, class Traits> template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator<=( bool
operator<=(
const CharT (&s)[N], const CharT (&s)[N],
static_string<M, CharT, Traits> const& rhs) static_string<M, CharT, Traits> const& rhs)
{ {
@@ -646,7 +640,8 @@ bool operator<=(
} }
template<std::size_t N, class CharT, class Traits, std::size_t M> template<std::size_t N, class CharT, class Traits, std::size_t M>
bool operator<=( bool
operator<=(
static_string<N, CharT, Traits> const& lhs, static_string<N, CharT, Traits> const& lhs,
const CharT (&s)[M]) const CharT (&s)[M])
{ {
@@ -654,7 +649,8 @@ bool operator<=(
} }
template<std::size_t N, std::size_t M, class CharT, class Traits> template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator>( bool
operator>(
const CharT (&s)[N], const CharT (&s)[N],
static_string<M, CharT, Traits> const& rhs) static_string<M, CharT, Traits> const& rhs)
{ {
@@ -662,7 +658,8 @@ bool operator>(
} }
template<std::size_t N, class CharT, class Traits, std::size_t M> template<std::size_t N, class CharT, class Traits, std::size_t M>
bool operator>( bool
operator>(
static_string<N, CharT, Traits> const& lhs, static_string<N, CharT, Traits> const& lhs,
const CharT (&s)[M]) const CharT (&s)[M])
{ {
@@ -670,7 +667,8 @@ bool operator>(
} }
template<std::size_t N, std::size_t M, class CharT, class Traits> template<std::size_t N, std::size_t M, class CharT, class Traits>
bool operator>=( bool
operator>=(
const CharT (&s)[N], const CharT (&s)[N],
static_string<M, CharT, Traits> const& rhs) static_string<M, CharT, Traits> const& rhs)
{ {
@@ -678,7 +676,8 @@ bool operator>=(
} }
template<std::size_t N, class CharT, class Traits, std::size_t M> template<std::size_t N, class CharT, class Traits, std::size_t M>
bool operator>=( bool
operator>=(
static_string<N, CharT, Traits> const& lhs, static_string<N, CharT, Traits> const& lhs,
const CharT (&s)[M]) const CharT (&s)[M])
{ {

View File

@@ -127,7 +127,7 @@ ror(T t, unsigned n = 1)
static_cast<typename std::make_unsigned<T>::type>(t) >> n)); static_cast<typename std::make_unsigned<T>::type>(t) >> n));
} }
// 32-bit Uuoptimized // 32-bit Unoptimized
// //
template<class = void> template<class = void>
void void

View File

@@ -8,15 +8,11 @@
#ifndef BEAST_WEBSOCKET_ERROR_HPP #ifndef BEAST_WEBSOCKET_ERROR_HPP
#define BEAST_WEBSOCKET_ERROR_HPP #define BEAST_WEBSOCKET_ERROR_HPP
#include <boost/system/error_code.hpp> #include <beast/error.hpp>
#include <boost/system/system_error.hpp>
namespace beast { namespace beast {
namespace websocket { namespace websocket {
/// The type of error used by functions and completion handlers.
using error_code = boost::system::error_code;
/// Error codes returned from @ref stream operations. /// Error codes returned from @ref stream operations.
enum class error enum class error
{ {

View File

@@ -617,7 +617,7 @@ public:
If the passed HTTP request is a valid HTTP WebSocket Upgrade If the passed HTTP request is a valid HTTP WebSocket Upgrade
request, a HTTP response is sent back indicating a successful request, a HTTP response is sent back indicating a successful
upgrade. When this asynchronous operaiton completes, the stream is upgrade. When this asynchronous operation completes, the stream is
then ready to send and receive WebSocket protocol frames and messages. then ready to send and receive WebSocket protocol frames and messages.
If the HTTP request is invalid or cannot be satisfied, a HTTP If the HTTP request is invalid or cannot be satisfied, a HTTP

View File

@@ -136,7 +136,7 @@ namespace websocket_helpers {
template<class Socket> template<class Socket>
inline inline
void void
call_teardown(Socket& socket, websocket::error_code& ec) call_teardown(Socket& socket, error_code& ec)
{ {
using websocket::teardown; using websocket::teardown;
teardown(socket, ec); teardown(socket, ec);

View File

@@ -13,6 +13,7 @@ add_executable (core-tests
buffer_concepts.cpp buffer_concepts.cpp
buffers_adapter.cpp buffers_adapter.cpp
consuming_buffers.cpp consuming_buffers.cpp
error.cpp
handler_alloc.cpp handler_alloc.cpp
handler_concepts.cpp handler_concepts.cpp
http.cpp http.cpp
@@ -38,12 +39,14 @@ endif()
add_executable (http-tests add_executable (http-tests
${BEAST_INCLUDES} ${BEAST_INCLUDES}
fail_stream.hpp
string_stream.hpp
yield_to.hpp
main.cpp main.cpp
http/basic_headers.cpp http/basic_headers.cpp
http/basic_parser_v1.cpp http/basic_parser_v1.cpp
http/body_type.cpp http/body_type.cpp
http/empty_body.cpp http/empty_body.cpp
http/error.cpp
http/headers.cpp http/headers.cpp
http/message.cpp http/message.cpp
http/message_v1.cpp http/message_v1.cpp
@@ -79,6 +82,8 @@ endif()
add_executable (websocket-tests add_executable (websocket-tests
${BEAST_INCLUDES} ${BEAST_INCLUDES}
fail_stream.hpp fail_stream.hpp
string_stream.hpp
yield_to.hpp
websocket/websocket_async_echo_peer.hpp websocket/websocket_async_echo_peer.hpp
websocket/websocket_sync_echo_peer.hpp websocket/websocket_sync_echo_peer.hpp
main.cpp main.cpp

View File

@@ -16,6 +16,7 @@ unit-test core-tests :
buffer_concepts.cpp buffer_concepts.cpp
buffers_adapter.cpp buffers_adapter.cpp
consuming_buffers.cpp consuming_buffers.cpp
error.cpp
handler_alloc.cpp handler_alloc.cpp
handler_concepts.cpp handler_concepts.cpp
http.cpp http.cpp
@@ -41,7 +42,6 @@ unit-test http-tests :
http/basic_parser_v1.cpp http/basic_parser_v1.cpp
http/body_type.cpp http/body_type.cpp
http/empty_body.cpp http/empty_body.cpp
http/error.cpp
http/headers.cpp http/headers.cpp
http/message.cpp http/message.cpp
http/message_v1.cpp http/message_v1.cpp

View File

@@ -9,8 +9,10 @@
#include <beast/basic_streambuf.hpp> #include <beast/basic_streambuf.hpp>
#include <beast/streambuf.hpp> #include <beast/streambuf.hpp>
#include <beast/to_string.hpp>
#include <beast/detail/unit_test/suite.hpp> #include <beast/detail/unit_test/suite.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <algorithm>
#include <atomic> #include <atomic>
#include <memory> #include <memory>
#include <string> #include <string>
@@ -137,30 +139,117 @@ public:
class basic_streambuf_test : public beast::detail::unit_test::suite class basic_streambuf_test : public beast::detail::unit_test::suite
{ {
public: public:
template<class ConstBufferSequence> template<class Alloc1, class Alloc2>
static static
std::string bool
to_string(ConstBufferSequence const& bs) eq(basic_streambuf<Alloc1> const& sb1,
basic_streambuf<Alloc2> const& sb2)
{ {
return to_string(sb1.data()) == to_string(sb2.data());
}
void testSpecialMembers()
{
using boost::asio::buffer;
using boost::asio::buffer_cast; using boost::asio::buffer_cast;
using boost::asio::buffer_size; using boost::asio::buffer_size;
std::string s; std::string const s = "Hello, world";
s.reserve(buffer_size(bs)); expect(s.size() == 12);
for(auto const& b : bs) for(std::size_t i = 1; i < 12; ++i) {
s.append(buffer_cast<char const*>(b), for(std::size_t x = 1; x < 4; ++x) {
buffer_size(b)); for(std::size_t y = 1; y < 4; ++y) {
return s; 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)));
expect(to_string(sb.data()) == s);
{
streambuf sb2(sb);
expect(eq(sb, sb2));
}
{
streambuf sb2;
sb2 = sb;
expect(eq(sb, sb2));
}
{
streambuf sb2(std::move(sb));
expect(to_string(sb2.data()) == s);
expect(buffer_size(sb.data()) == 0);
sb = std::move(sb2);
expect(to_string(sb.data()) == s);
expect(buffer_size(sb2.data()) == 0);
}
sb = sb;
sb = std::move(sb);
}
}}}
}
void testAllocator()
{
{
using alloc_type =
test_allocator<char, false, false, false, false>;
using sb_type = basic_streambuf<alloc_type>;
sb_type sb;
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;
expect(sb.get_allocator().id() == 2);
sb_type sb2(sb);
expect(sb2.get_allocator().id() == 2);
sb_type sb3(sb, alloc_type{});
//expect(sb3.get_allocator().id() == 3);
}
} }
void void
testPrepare() testPrepare()
{ {
using boost::asio::buffer_size; using boost::asio::buffer_size;
{
streambuf sb(2); streambuf sb(2);
expect(buffer_size(sb.prepare(5)) == 5); expect(buffer_size(sb.prepare(5)) == 5);
expect(buffer_size(sb.prepare(8)) == 8); expect(buffer_size(sb.prepare(8)) == 8);
expect(buffer_size(sb.prepare(7)) == 7); expect(buffer_size(sb.prepare(7)) == 7);
} }
{
streambuf sb(2);
sb.prepare(2);
{
auto const bs = sb.prepare(5);
expect(std::distance(
bs.begin(), bs.end()) == 2);
}
{
auto const bs = sb.prepare(8);
expect(std::distance(
bs.begin(), bs.end()) == 3);
}
{
auto const bs = sb.prepare(4);
expect(std::distance(
bs.begin(), bs.end()) == 2);
}
}
}
void testCommit()
{
using boost::asio::buffer_size;
streambuf sb(2);
sb.prepare(2);
sb.prepare(5);
sb.commit(1);
expect(buffer_size(sb.data()) == 1);
}
void testStreambuf() void testStreambuf()
{ {
@@ -257,88 +346,67 @@ public:
}}}}} }}}}}
} }
template<class Alloc1, class Alloc2> void testIterators()
static
bool
eq(basic_streambuf<Alloc1> const& sb1,
basic_streambuf<Alloc2> const& sb2)
{ {
return to_string(sb1.data()) == to_string(sb2.data());
}
void testSpecial()
{
using boost::asio::buffer;
using boost::asio::buffer_cast;
using boost::asio::buffer_size; using boost::asio::buffer_size;
std::string const s = "Hello, world"; streambuf sb(1);
expect(s.size() == 12); sb.prepare(1);
for(std::size_t i = 1; i < 12; ++i) { sb.commit(1);
for(std::size_t x = 1; x < 4; ++x) { sb.prepare(2);
for(std::size_t y = 1; y < 4; ++y) { sb.commit(2);
std::size_t z = s.size() - (x + y); expect(buffer_size(sb.data()) == 3);
sb.prepare(1);
expect(buffer_size(sb.prepare(3)) == 3);
expect(read_size_helper(sb, 3) == 3);
sb.commit(2);
try
{ {
streambuf sb(i); streambuf sb0(0);
sb.commit(buffer_copy(sb.prepare(x), buffer(s.data(), x))); fail();
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))); catch(std::exception const&)
expect(to_string(sb.data()) == s);
{ {
streambuf sb2(sb); pass();
expect(eq(sb, sb2));
} }
{ std::size_t n;
streambuf sb2; n = 0;
sb2 = sb; for(auto it = sb.data().begin();
expect(eq(sb, sb2)); it != sb.data().end(); it++)
} ++n;
{ expect(n == 4);
streambuf sb2(std::move(sb)); n = 0;
expect(to_string(sb2.data()) == s); for(auto it = sb.data().begin();
expect(buffer_size(sb.data()) == 0); it != sb.data().end(); ++it)
sb = std::move(sb2); ++n;
expect(to_string(sb.data()) == s); expect(n == 4);
expect(buffer_size(sb2.data()) == 0); n = 0;
} for(auto it = sb.data().end();
} it != sb.data().begin(); it--)
}}} ++n;
expect(n == 4);
n = 0;
for(auto it = sb.data().end();
it != sb.data().begin(); --it)
++n;
expect(n == 4);
} }
void testAllocator() void testOutputStream()
{
{
using alloc_type =
test_allocator<char, false, false, false, false>;
using sb_type = basic_streambuf<alloc_type>;
sb_type sb;
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;
expect(sb.get_allocator().id() == 2);
sb_type sb2(sb);
expect(sb2.get_allocator().id() == 2);
sb_type sb3(sb, alloc_type{});
//expect(sb3.get_allocator().id() == 3);
}
}
void testStream()
{ {
streambuf sb; streambuf sb;
sb << "x"; sb << "x";
expect(to_string(sb.data()) == "x");
} }
void run() override void run() override
{ {
testPrepare(); testSpecialMembers();
testStreambuf();
testSpecial();
testAllocator(); testAllocator();
testStream(); testPrepare();
testCommit();
testStreambuf();
testIterators();
testOutputStream();
} }
}; };

View File

@@ -68,9 +68,64 @@ public:
} }
} }
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);
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
{
expect((buffer_size(*bs.end()) == 0, false));
}
catch(std::exception const&)
{
pass();
}
auto bs2 = bs;
expect(bs.begin() != bs2.begin());
expect(bs.end() != bs2.end());
decltype(bs)::const_iterator it;
decltype(bs2)::const_iterator it2;
expect(it == it2);
}
void run() override void run() override
{ {
testBufferCat(); testBufferCat();
testIterators();
} }
}; };

View File

@@ -8,6 +8,7 @@
// Test that header file is self-contained. // Test that header file is self-contained.
#include <beast/buffers_adapter.hpp> #include <beast/buffers_adapter.hpp>
#include <beast/streambuf.hpp>
#include <beast/detail/unit_test/suite.hpp> #include <beast/detail/unit_test/suite.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/asio/streambuf.hpp> #include <boost/asio/streambuf.hpp>
@@ -149,9 +150,38 @@ public:
} }
}}}}}} }}}}}}
} }
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));
expect(buffer_size(ba.prepare(3)) == 3);
ba.commit(2);
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));
expect(buffer_size(ba.prepare(8)) == 8);
ba.commit(2);
expect(buffer_size(ba.data()) == 2);
ba.consume(1);
ba.commit(6);
ba.consume(2);
expect(buffer_size(ba.data()) == 5);
ba.consume(5);
}
}
void run() override void run() override
{ {
testBuffersAdapter(); testBuffersAdapter();
testCommit();
} }
}; };

View File

@@ -81,10 +81,22 @@ public:
expect(buffer_copy(cb2, cb) == 0); 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;
expect(n == 3);
}
void run() override void run() override
{ {
testBuffers(); testBuffers();
testNullBuffers(); testNullBuffers();
testIterator();
} }
}; };

View File

@@ -7,7 +7,6 @@
#include <beast/detail/sha1.hpp> #include <beast/detail/sha1.hpp>
#include <beast/detail/unit_test/suite.hpp> #include <beast/detail/unit_test/suite.hpp>
#include <boost/algorithm/hex.hpp>
#include <array> #include <array>
namespace beast { namespace beast {
@@ -16,20 +15,45 @@ namespace detail {
class sha1_test : public beast::detail::unit_test::suite class sha1_test : public beast::detail::unit_test::suite
{ {
public: 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 void
check(std::string const& message, std::string const& answer) check(std::string const& message, std::string const& answer)
{ {
using digest_type = std::string digest;
std::array<std::uint8_t, sha1_context::digest_size>; digest = unhex(answer);
digest_type digest;
if(! expect(boost::algorithm::unhex(
answer, digest.begin()) == digest.end()))
return;
sha1_context ctx; sha1_context ctx;
digest_type result; std::string result;
result.resize(sha1_context::digest_size);
init(ctx); init(ctx);
update(ctx, message.data(), message.size()); update(ctx, message.data(), message.size());
finish(ctx, result.data()); finish(ctx, &result[0]);
expect(result == digest); expect(result == digest);
} }

View File

@@ -6,4 +6,4 @@
// //
// Test that header file is self-contained. // Test that header file is self-contained.
#include <beast/http/error.hpp> #include <beast/error.hpp>

View File

@@ -22,10 +22,12 @@
#include <beast/async_completion.hpp> #include <beast/async_completion.hpp>
#include <beast/bind_handler.hpp> #include <beast/bind_handler.hpp>
#include <beast/error.hpp>
#include <beast/websocket/teardown.hpp>
#include <beast/detail/get_lowest_layer.hpp> #include <beast/detail/get_lowest_layer.hpp>
#include <boost/system/error_code.hpp>
namespace beast { namespace beast {
namespace test {
/* A stream wrapper that fails. /* A stream wrapper that fails.
@@ -35,12 +37,6 @@ namespace beast {
template<class NextLayer> template<class NextLayer>
class fail_stream class fail_stream
{ {
using error_code =
boost::system::error_code;
using system_error =
boost::system::system_error;
error_code ec_; error_code ec_;
std::size_t n_ = 0; std::size_t n_ = 0;
NextLayer next_layer_; NextLayer next_layer_;
@@ -204,6 +200,7 @@ async_teardown(fail_stream<NextLayer>& stream,
stream.next_layer(), std::forward<TeardownHandler>(handler)); stream.next_layer(), std::forward<TeardownHandler>(handler));
} }
} // test
} // beast } // beast
#endif #endif

View File

@@ -12,7 +12,6 @@
namespace beast { namespace beast {
namespace http { namespace http {
namespace test {
class basic_headers_test : public beast::detail::unit_test::suite class basic_headers_test : public beast::detail::unit_test::suite
{ {
@@ -48,6 +47,7 @@ public:
bh h3(std::move(h1)); bh h3(std::move(h1));
expect(h3.size() == 2); expect(h3.size() == 2);
expect(h1.size() == 0); expect(h1.size() == 0);
h2 = std::move(h2);
} }
void run() override void run() override
@@ -58,6 +58,5 @@ public:
BEAST_DEFINE_TESTSUITE(basic_headers,http,beast); BEAST_DEFINE_TESTSUITE(basic_headers,http,beast);
} // test
} // asio } // asio
} // beast } // beast

View File

@@ -10,9 +10,9 @@
#include "message_fuzz.hpp" #include "message_fuzz.hpp"
#include <beast/error.hpp>
#include <beast/streambuf.hpp> #include <beast/streambuf.hpp>
#include <beast/write_streambuf.hpp> #include <beast/write_streambuf.hpp>
#include <beast/http/error.hpp>
#include <beast/http/rfc2616.hpp> #include <beast/http/rfc2616.hpp>
#include <beast/detail/ci_char_traits.hpp> #include <beast/detail/ci_char_traits.hpp>
#include <beast/detail/unit_test/suite.hpp> #include <beast/detail/unit_test/suite.hpp>
@@ -29,8 +29,6 @@ namespace http {
class basic_parser_v1_test : public beast::detail::unit_test::suite class basic_parser_v1_test : public beast::detail::unit_test::suite
{ {
std::mt19937 rng_;
public: public:
struct cb_req_checker struct cb_req_checker
{ {
@@ -302,17 +300,6 @@ 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_));
}
//--------------------------------------------------------------------------
// Parse a valid message with expected version // Parse a valid message with expected version
// //
template<bool isRequest> template<bool isRequest>
@@ -481,6 +468,42 @@ public:
parse_ev<true>("GET / HTTP/1.1\r\nf :", parse_error::bad_field); parse_ev<true>("GET / HTTP/1.1\r\nf :", parse_error::bad_field);
} }
void testCorrupt()
{
using boost::asio::buffer;
std::string s;
for(std::size_t n = 0;;++n)
{
// Create a request and set one octet to an invalid char
s =
"GET / HTTP/1.1\r\n"
"Host: localhost\r\n"
"User-Agent: test\r\n"
"Content-Length: 00\r\n"
"\r\n";
auto const len = s.size();
if(n >= s.size())
{
pass();
break;
}
s[n] = 0;
for(std::size_t m = 1; m < len - 1; ++m)
{
null_parser<true> p;
error_code ec;
p.write(buffer(s.data(), m), ec);
if(ec)
{
pass();
continue;
}
p.write(buffer(s.data() + m, len - m), ec);
expect(ec);
}
}
}
void void
testRandomReq(std::size_t N) testRandomReq(std::size_t N)
{ {
@@ -629,6 +652,7 @@ public:
testFlags(); testFlags();
testUpgrade(); testUpgrade();
testBad(); testBad();
testCorrupt();
testRandomReq(100); testRandomReq(100);
testRandomResp(100); testRandomResp(100);
testBody(); testBody();

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/http/body_type.hpp>

View File

@@ -34,7 +34,6 @@ namespace test {
class sync_echo_http_server class sync_echo_http_server
{ {
public: public:
using error_code = boost::system::error_code;
using endpoint_type = boost::asio::ip::tcp::endpoint; using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address; using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket; using socket_type = boost::asio::ip::tcp::socket;
@@ -136,11 +135,46 @@ private:
class message_test : public beast::detail::unit_test::suite class message_test : public beast::detail::unit_test::suite
{ {
public: public:
using error_code = boost::system::error_code;
using endpoint_type = boost::asio::ip::tcp::endpoint; using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address; using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket; using socket_type = boost::asio::ip::tcp::socket;
void testFunctions()
{
request_v1<empty_body> m;
m.version = 10;
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&)
{
}
m.headers.erase("Content-Length");
m.headers.insert("Connection", "keep-alive");
try
{
prepare(m);
fail();
}
catch(std::exception const&)
{
}
}
void void
syncEcho(endpoint_type ep) syncEcho(endpoint_type ep)
{ {
@@ -174,6 +208,7 @@ public:
void run() override void run() override
{ {
testFunctions();
testAsio(); testAsio();
pass(); pass();
} }

View File

@@ -10,7 +10,7 @@
#include "nodejs-parser/http_parser.h" #include "nodejs-parser/http_parser.h"
#include <beast/http/error.hpp> #include <beast/error.hpp>
#include <beast/http/message_v1.hpp> #include <beast/http/message_v1.hpp>
#include <beast/http/rfc2616.hpp> #include <beast/http/rfc2616.hpp>
#include <beast/buffer_concepts.hpp> #include <beast/buffer_concepts.hpp>

View File

@@ -7,3 +7,97 @@
// Test that header file is self-contained. // Test that header file is self-contained.
#include <beast/http/read.hpp> #include <beast/http/read.hpp>
#include "../fail_stream.hpp"
#include "../string_stream.hpp"
#include "../yield_to.hpp"
#include <beast/http/streambuf_body.hpp>
#include <beast/detail/unit_test/suite.hpp>
#include <boost/asio/spawn.hpp>
namespace beast {
namespace http {
class read_test
: public beast::detail::unit_test::suite
, public test::enable_yield_to
{
public:
void testRead(yield_context do_yield)
{
static std::size_t constexpr limit = 100;
std::size_t n;
for(n = 1; n < limit; ++n)
{
streambuf sb;
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
{
read(fs, sb, m);
break;
}
catch(std::exception const&)
{
}
}
expect(n < limit);
for(n = 1; n < limit; ++n)
{
streambuf sb;
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;
read(fs, sb, m, ec);
if(! ec)
break;
}
expect(n < limit);
ios_.post(
[&]{
n = 1;
});
for(n = 1; n < limit; ++n)
{
streambuf sb;
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;
async_read(fs, sb, m, do_yield[ec]);
if(! ec)
break;
}
expect(n < limit);
}
void run() override
{
yield_to(std::bind(&read_test::testRead,
this, std::placeholders::_1));
}
};
BEAST_DEFINE_TESTSUITE(read,http,beast);
} // http
} // beast

View File

@@ -8,7 +8,7 @@
// Test that header file is self-contained. // Test that header file is self-contained.
#include <beast/http/write.hpp> #include <beast/http/write.hpp>
#include <beast/http/error.hpp> #include <beast/error.hpp>
#include <beast/http/headers.hpp> #include <beast/http/headers.hpp>
#include <beast/http/message.hpp> #include <beast/http/message.hpp>
#include <beast/http/empty_body.hpp> #include <beast/http/empty_body.hpp>

View File

@@ -87,10 +87,26 @@ public:
expect(buffer_copy(pbc, cb) == 0); expect(buffer_copy(pbc, cb) == 0);
} }
void testIterator()
{
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)
++n;
expect(n == 2);
}
void run() override void run() override
{ {
testBuffers(); testBuffers();
testNullBuffers(); testNullBuffers();
testIterator();
} }
}; };

View File

@@ -93,6 +93,7 @@ public:
expect(s3.back() == 'x'); expect(s3.back() == 'x');
s2 = "y"; s2 = "y";
expect(s2 == "y"); expect(s2 == "y");
expect(s3 == "x");
s1 = s2; s1 = s2;
expect(s1 == "y"); expect(s1 == "y");
s1.clear(); s1.clear();
@@ -140,6 +141,35 @@ public:
pass(); pass();
} }
} }
{
str1 s1("x");
str2 s2;
s2 = s1;
try
{
s1.resize(2);
fail();
}
catch(std::length_error const&)
{
pass();
}
s1 = "1";
s2 = "2";
expect(s1.compare(s2) < 0);
expect(s2.compare(s1) > 0);
expect(s1 < "10");
expect(s2 > "1");
expect("10" > s1);
expect("1" < s2);
}
pass();
}
void testCompare()
{
using str1 = static_string<1>;
using str2 = static_string<2>;
{ {
str2 s1("x"); str2 s1("x");
str2 s2("x"); str2 s2("x");
@@ -175,12 +205,49 @@ public:
expect(! ("x" > s)); expect(! ("x" > s));
expect(! ("x" != s)); expect(! ("x" != s));
} }
pass(); {
str2 s("x");
expect(s <= "y");
expect(s < "y");
expect(s != "y");
expect(! (s == "y"));
expect(! (s >= "y"));
expect(! (s > "x"));
expect("y" >= s);
expect("y" > s);
expect("y" != s);
expect(! ("y" == s));
expect(! ("y" <= s));
expect(! ("y" < s));
}
{
str1 s1("x");
str2 s2("y");
expect(s1 <= s2);
expect(s1 < s2);
expect(s1 != s2);
expect(! (s1 == s2));
expect(! (s1 >= s2));
expect(! (s1 > s2));
}
{
str1 s1("x");
str2 s2("xx");
expect(s1 < s2);
expect(s2 > s1);
}
{
str1 s1("x");
str2 s2("yy");
expect(s1 < s2);
expect(s2 > s1);
}
} }
void run() override void run() override
{ {
testMembers(); testMembers();
testCompare();
} }
}; };

View File

@@ -25,6 +25,8 @@ public:
streambuf_readstream<socket_type, streambuf> srs(ios); streambuf_readstream<socket_type, streambuf> srs(ios);
streambuf_readstream<socket_type, streambuf> srs2(std::move(srs)); streambuf_readstream<socket_type, streambuf> srs2(std::move(srs));
srs = std::move(srs2); srs = std::move(srs2);
expect(&srs.get_io_service() == &ios);
expect(&srs.get_io_service() == &srs2.get_io_service());
} }
{ {
socket_type sock(ios); socket_type sock(ios);

114
test/string_stream.hpp Normal file
View File

@@ -0,0 +1,114 @@
//
// 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_STRING_STREAM_HPP
#define BEAST_TEST_STRING_STREAM_HPP
#include <beast/bind_handler.hpp>
#include <beast/error.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp>
#include <string>
namespace beast {
namespace test {
// 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_(std::move(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(error_code, std::size_t)>::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(error_code, std::size_t)>::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();
}
};
} // test
} // beast
#endif

View File

@@ -9,164 +9,31 @@
#include <beast/websocket/stream.hpp> #include <beast/websocket/stream.hpp>
#include "../fail_stream.hpp" #include "../fail_stream.hpp"
#include "../string_stream.hpp"
#include "../yield_to.hpp"
#include "websocket_async_echo_peer.hpp" #include "websocket_async_echo_peer.hpp"
#include "websocket_sync_echo_peer.hpp" #include "websocket_sync_echo_peer.hpp"
#include <beast/bind_handler.hpp>
#include <beast/streambuf.hpp> #include <beast/streambuf.hpp>
#include <beast/to_string.hpp> #include <beast/to_string.hpp>
#include <beast/detail/unit_test/suite.hpp> #include <beast/detail/unit_test/suite.hpp>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/asio/spawn.hpp> #include <boost/asio/spawn.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <thread>
#include <beast/http/parser_v1.hpp>
namespace beast { namespace beast {
namespace websocket { namespace websocket {
class stream_test : public beast::detail::unit_test::suite class stream_test
: public beast::detail::unit_test::suite
, public test::enable_yield_to
{ {
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: public:
using endpoint_type = boost::asio::ip::tcp::endpoint; using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address; using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket; 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(error_code, std::size_t)>::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(error_code, std::size_t)>::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() void testSpecialMembers()
{ {
stream<socket_type> ws(ios_); stream<socket_type> ws(ios_);
@@ -177,6 +44,7 @@ public:
stream<socket_type> ws2(ios_); stream<socket_type> ws2(ios_);
ws = std::move(ws2); ws = std::move(ws2);
} }
expect(&ws.get_io_service() == &ios_);
pass(); pass();
} }
@@ -187,8 +55,16 @@ public:
ws.set_option(read_buffer_size(8192)); ws.set_option(read_buffer_size(8192));
ws.set_option(read_message_max(1 * 1024 * 1024)); ws.set_option(read_message_max(1 * 1024 * 1024));
ws.set_option(write_buffer_size(2048)); ws.set_option(write_buffer_size(2048));
try
{
ws.set_option(message_type(opcode::close));
fail();
}
catch(std::exception const&)
{
pass(); pass();
} }
}
template<std::size_t N> template<std::size_t N>
static static
@@ -198,10 +74,10 @@ public:
return boost::asio::const_buffers_1(&s[0], N-1); return boost::asio::const_buffers_1(&s[0], N-1);
} }
void testAccept(boost::asio::yield_context do_yield) void testAccept(yield_context do_yield)
{ {
{ {
stream<string_Stream> ws(ios_, stream<test::string_stream> ws(ios_,
"GET / HTTP/1.1\r\n" "GET / HTTP/1.1\r\n"
"Host: localhost:80\r\n" "Host: localhost:80\r\n"
"Upgrade: WebSocket\r\n" "Upgrade: WebSocket\r\n"
@@ -220,7 +96,7 @@ public:
} }
} }
{ {
stream<string_Stream> ws(ios_, stream<test::string_stream> ws(ios_,
"GET / HTTP/1.1\r\n" "GET / HTTP/1.1\r\n"
"\r\n"); "\r\n");
try try
@@ -234,7 +110,7 @@ public:
} }
} }
{ {
stream<string_Stream> ws(ios_, stream<test::string_stream> ws(ios_,
"GET / HTTP/1.1\r\n" "GET / HTTP/1.1\r\n"
"Host: localhost:80\r\n" "Host: localhost:80\r\n"
"Upgrade: WebSocket\r\n" "Upgrade: WebSocket\r\n"
@@ -247,7 +123,7 @@ public:
expect(! ec, ec.message()); expect(! ec, ec.message());
} }
{ {
stream<string_Stream> ws(ios_, stream<test::string_stream> ws(ios_,
"GET / HTTP/1.1\r\n" "GET / HTTP/1.1\r\n"
"\r\n"); "\r\n");
error_code ec; error_code ec;
@@ -255,7 +131,7 @@ public:
expect(ec); expect(ec);
} }
{ {
stream<string_Stream> ws(ios_, stream<test::string_stream> ws(ios_,
"GET / HTTP/1.1\r\n" "GET / HTTP/1.1\r\n"
"Host: localhost:80\r\n" "Host: localhost:80\r\n"
"Upgrade: WebSocket\r\n" "Upgrade: WebSocket\r\n"
@@ -268,7 +144,7 @@ public:
expect(! ec, ec.message()); expect(! ec, ec.message());
} }
{ {
stream<string_Stream> ws(ios_, stream<test::string_stream> ws(ios_,
"GET / HTTP/1.1\r\n" "GET / HTTP/1.1\r\n"
"\r\n"); "\r\n");
error_code ec; error_code ec;
@@ -276,7 +152,7 @@ public:
expect(ec); expect(ec);
} }
{ {
stream<string_Stream> ws(ios_, stream<test::string_stream> ws(ios_,
"Host: localhost:80\r\n" "Host: localhost:80\r\n"
"Upgrade: WebSocket\r\n" "Upgrade: WebSocket\r\n"
"Connection: upgrade\r\n" "Connection: upgrade\r\n"
@@ -295,7 +171,7 @@ public:
} }
} }
{ {
stream<string_Stream> ws(ios_, stream<test::string_stream> ws(ios_,
"\r\n"); "\r\n");
try try
{ {
@@ -309,7 +185,7 @@ public:
} }
} }
{ {
stream<string_Stream> ws(ios_, stream<test::string_stream> ws(ios_,
"Host: localhost:80\r\n" "Host: localhost:80\r\n"
"Upgrade: WebSocket\r\n" "Upgrade: WebSocket\r\n"
"Connection: upgrade\r\n" "Connection: upgrade\r\n"
@@ -322,7 +198,7 @@ public:
expect(! ec, ec.message()); expect(! ec, ec.message());
} }
{ {
stream<string_Stream> ws(ios_, stream<test::string_stream> ws(ios_,
"GET / HTTP/1.1\r\n" "GET / HTTP/1.1\r\n"
"\r\n"); "\r\n");
error_code ec; error_code ec;
@@ -330,7 +206,7 @@ public:
expect(ec); expect(ec);
} }
{ {
stream<string_Stream> ws(ios_, stream<test::string_stream> ws(ios_,
"Host: localhost:80\r\n" "Host: localhost:80\r\n"
"Upgrade: WebSocket\r\n" "Upgrade: WebSocket\r\n"
"Connection: upgrade\r\n" "Connection: upgrade\r\n"
@@ -343,7 +219,7 @@ public:
expect(! ec, ec.message()); expect(! ec, ec.message());
} }
{ {
stream<string_Stream> ws(ios_, stream<test::string_stream> ws(ios_,
"\r\n"); "\r\n");
error_code ec; error_code ec;
ws.async_accept(strbuf( ws.async_accept(strbuf(
@@ -353,7 +229,7 @@ public:
} }
void testHandshake(endpoint_type const& ep, void testHandshake(endpoint_type const& ep,
boost::asio::yield_context do_yield) yield_context do_yield)
{ {
{ {
// disconnected socket // disconnected socket
@@ -423,7 +299,7 @@ public:
} }
void testErrorHandling(endpoint_type const& ep, void testErrorHandling(endpoint_type const& ep,
boost::asio::yield_context do_yield) yield_context do_yield)
{ {
static std::size_t constexpr limit = 100; static std::size_t constexpr limit = 100;
std::size_t n; std::size_t n;
@@ -436,7 +312,7 @@ public:
sock.connect(ep, ec); sock.connect(ep, ec);
if(! expect(! ec, ec.message())) if(! expect(! ec, ec.message()))
break; break;
stream<fail_stream<socket_type&>> ws(n, sock); stream<test::fail_stream<socket_type&>> ws(n, sock);
try try
{ {
ws.handshake("localhost", "/"); ws.handshake("localhost", "/");
@@ -475,7 +351,7 @@ public:
sock.connect(ep, ec); sock.connect(ep, ec);
if(! expect(! ec, ec.message())) if(! expect(! ec, ec.message()))
break; break;
stream<fail_stream<socket_type&>> ws(n, sock); stream<test::fail_stream<socket_type&>> ws(n, sock);
ws.handshake("localhost", "/", ec); ws.handshake("localhost", "/", ec);
if(ec) if(ec)
continue; continue;
@@ -510,7 +386,7 @@ public:
sock.connect(ep, ec); sock.connect(ep, ec);
if(! expect(! ec, ec.message())) if(! expect(! ec, ec.message()))
break; break;
stream<fail_stream<socket_type&>> ws(n, sock); stream<test::fail_stream<socket_type&>> ws(n, sock);
ws.async_handshake("localhost", "/", do_yield[ec]); ws.async_handshake("localhost", "/", do_yield[ec]);
if(ec) if(ec)
break; break;
@@ -538,13 +414,70 @@ public:
expect(n < limit); expect(n < limit);
} }
void testMask(endpoint_type const& ep,
yield_context do_yield)
{
{
std::vector<char> v;
for(char n = 0; n < 20; ++n)
{
error_code ec;
socket_type sock(ios_);
sock.connect(ep, ec);
if(! expect(! ec, ec.message()))
break;
stream<socket_type&> ws(sock);
ws.handshake("localhost", "/", ec);
if(! expect(! ec, ec.message()))
break;
ws.write(boost::asio::buffer(v), ec);
if(! expect(! ec, ec.message()))
break;
opcode op;
streambuf sb;
ws.read(op, sb, ec);
if(! expect(! ec, ec.message()))
break;
expect(to_string(sb.data()) ==
std::string{v.data(), v.size()});
v.push_back(n+1);
}
}
{
std::vector<char> v;
for(char n = 0; n < 20; ++n)
{
error_code ec;
socket_type sock(ios_);
sock.connect(ep, ec);
if(! expect(! ec, ec.message()))
break;
stream<socket_type&> ws(sock);
ws.handshake("localhost", "/", ec);
if(! expect(! ec, ec.message()))
break;
ws.async_write(boost::asio::buffer(v), do_yield[ec]);
if(! expect(! ec, ec.message()))
break;
opcode op;
streambuf sb;
ws.async_read(op, sb, do_yield[ec]);
if(! expect(! ec, ec.message()))
break;
expect(to_string(sb.data()) ==
std::string{v.data(), v.size()});
v.push_back(n+1);
}
}
}
void run() override void run() override
{ {
testSpecialMembers(); testSpecialMembers();
testOptions(); testOptions();
exec(std::bind(&stream_test::testAccept, yield_to(std::bind(&stream_test::testAccept,
this, std::placeholders::_1)); this, std::placeholders::_1));
auto const any = endpoint_type{ auto const any = endpoint_type{
@@ -552,22 +485,30 @@ public:
{ {
sync_echo_peer server(true, any); sync_echo_peer server(true, any);
exec(std::bind(&stream_test::testHandshake, yield_to(std::bind(&stream_test::testHandshake,
this, server.local_endpoint(), this, server.local_endpoint(),
std::placeholders::_1)); std::placeholders::_1));
exec(std::bind(&stream_test::testErrorHandling, yield_to(std::bind(&stream_test::testErrorHandling,
this, server.local_endpoint(),
std::placeholders::_1));
yield_to(std::bind(&stream_test::testMask,
this, server.local_endpoint(), this, server.local_endpoint(),
std::placeholders::_1)); std::placeholders::_1));
} }
{ {
async_echo_peer server(true, any, 1); async_echo_peer server(true, any, 1);
exec(std::bind(&stream_test::testHandshake, yield_to(std::bind(&stream_test::testHandshake,
this, server.local_endpoint(), this, server.local_endpoint(),
std::placeholders::_1)); std::placeholders::_1));
exec(std::bind(&stream_test::testErrorHandling, yield_to(std::bind(&stream_test::testErrorHandling,
this, server.local_endpoint(),
std::placeholders::_1));
yield_to(std::bind(&stream_test::testMask,
this, server.local_endpoint(), this, server.local_endpoint(),
std::placeholders::_1)); std::placeholders::_1));
} }

View File

@@ -37,7 +37,6 @@ namespace websocket {
class async_echo_peer class async_echo_peer
{ {
public: public:
using error_code = boost::system::error_code;
using endpoint_type = boost::asio::ip::tcp::endpoint; using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address; using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket; using socket_type = boost::asio::ip::tcp::socket;

View File

@@ -36,7 +36,6 @@ namespace websocket {
class sync_echo_peer class sync_echo_peer
{ {
public: public:
using error_code = boost::system::error_code;
using endpoint_type = boost::asio::ip::tcp::endpoint; using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address; using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket; using socket_type = boost::asio::ip::tcp::socket;

86
test/yield_to.hpp 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)
//
#ifndef BEAST_TEST_ENABLE_YIELD_TO_HPP
#define BEAST_TEST_ENABLE_YIELD_TO_HPP
#include <boost/asio/io_service.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/optional.hpp>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <thread>
namespace beast {
namespace test {
// Mix-in to support tests using coroutines
class enable_yield_to
{
protected:
boost::asio::io_service ios_;
private:
boost::optional<boost::asio::io_service::work> work_;
std::thread thread_;
std::mutex m_;
std::condition_variable cv_;
bool running_ = false;;
protected:
using yield_context =
boost::asio::yield_context;
enable_yield_to()
: work_(ios_)
, thread_([&]
{
ios_.run();
}
)
{
}
~enable_yield_to()
{
work_ = boost::none;
thread_.join();
}
// Run a function in a coroutine
template<class Function>
void
yield_to(Function&& f);
};
template<class Function>
void
enable_yield_to::yield_to(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_; });
}
} // test
} // beast
#endif