Refactor beast core, http, tests, and examples:

* Fix warnings
* Port cmake scripts to linux
* Add command line options for running test suites
* Add examples to CMakeLists
* Return std::uint64_t from writer::content_length
* basic_parser::write takes asio::const_buffer instead of pointer and size
* Turn message test back on now that it passes
* Rename to http::headers, use std::allocator, remove http_headers
* http::message::method is now a string
* Refactor to_string for ConstBufferSequence
* Remove chunk_encode from the public interface
* Initialize members for default constructed iterators
* Disallow default construction for dependent buffer sequences

Refactor http::message serialization:

* Serialization no longer creates a copy of the
  headers and modifies them
* New function prepare(), sets Connection, Transfer-Encoding,
  Content-Length based on the body attributes and caller options.
  Callers can use prepare() to have the fields set automatically,
  or they can set the fields manually.
* Use write for operator<<
* Tests for serialization
This commit is contained in:
Vinnie Falco
2016-04-29 06:04:40 -04:00
parent 43a81ff1a7
commit a0b04bdff2
69 changed files with 1315 additions and 953 deletions

View File

@ -1,14 +1,25 @@
# Part of Beast
cmake_minimum_required (VERSION 3.5)
cmake_minimum_required (VERSION 3.2)
project (Beast)
set_property (GLOBAL PROPERTY USE_FOLDERS ON)
if (WIN32)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /wd4100 /D_SCL_SECURE_NO_WARNINGS=1 /D_CRT_SECURE_NO_WARNINGS=1")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /W4 /wd4100 /D_SCL_SECURE_NO_WARNINGS=1 /D_CRT_SECURE_NO_WARNINGS=1")
else()
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)
find_package(Boost REQUIRED COMPONENTS filesystem program_options system)
include_directories(${Boost_INCLUDE_DIRS})
link_directories(${Boost_LIBRARY_DIR})
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads)
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -g -std=c++11 -Wall /Wextra /Wpedantic /Wconversion -Wno-unused-variable")
endif()
message ("cxx Flags: " ${CMAKE_CXX_FLAGS})

View File

@ -1,24 +1,15 @@
* Change build options to C++11 only
* Replace Jamroot with Jamfile
* Fix failing test/message.cpp
* 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
* Go over each header and split header material into detail and impl files
* Make buffers_debug a detail
* Roll header-only http parser
* Define Parser concept in HTTP
* melpon sandbox?
* invokable unit test
* trim public interface of rfc2616.h to essentials only
* Use new http routines in JSONRPCClient
* Remove or change http::headers alias
* Do something about the methods.hpp and fields.hpp type headers
* Fix index in docs
* Fix integer warning in file_body.hpp
* Use make_error_code in websocket to set the category right
* Figure out why namespace rfc2616 is included in docs
(currently disabled via GENERATING_DOCS macro)
* Include Example program listings in the docs
@ -26,7 +17,12 @@
* HTTP parser size limit with test (configurable?)
* HTTP parser trailers with test
* URL parser, strong URL checking in HTTP parser
* Fix method, use string instead of enum
* More fine grained parser errors
* Fix all the warnings in all projects/build configs
* 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
Boost.Http
* Use enum instead of bool in isRequest
* move version to a subclass of message

View File

@ -108,7 +108,6 @@ INPUT = \
../include/beast/bind_handler.hpp \
../include/beast/buffer_cat.hpp \
../include/beast/buffers_adapter.hpp \
../include/beast/buffers_debug.hpp \
../include/beast/consuming_buffers.hpp \
../include/beast/handler_alloc.hpp \
../include/beast/http.hpp \
@ -117,6 +116,7 @@ INPUT = \
../include/beast/static_streambuf.hpp \
../include/beast/streambuf.hpp \
../include/beast/streambuf_readstream.hpp \
../include/beast/to_string.hpp \
../include/beast/type_check.hpp \
../include/beast/websocket.hpp \
../include/beast/write_streambuf.hpp \

View File

@ -115,9 +115,10 @@ int main()
using namespace beast::http;
// Send HTTP request using beast
request<empty_body> req({method_t::http_get, "/", 11});
request<empty_body> req({"GET", "/", 11});
req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port()));
req.headers.replace("User-Agent", "Beast");
prepare(req);
write(sock, req);
// Receive and print HTTP response using beast
@ -130,8 +131,8 @@ int main()
Establish a WebSocket connection, send a message and receive the reply:
```
#include <beast/to_string.hpp>
#include <beast/websocket.hpp>
#include <beast/buffers_debug.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <string>
@ -158,8 +159,7 @@ int main()
opcode op;
ws.read(op, sb);
ws.close(close_code::normal);
std::cout <<
beast::debug::buffers_to_string(sb.data()) << "\n";
std::cout << to_string(sb.data()) << "\n";
}
```

View File

@ -34,6 +34,7 @@
<member><link linkend="beast.ref.http__basic_streambuf_body">basic_streambuf_body</link></member>
<member><link linkend="beast.ref.http__empty_body">empty_body</link></member>
<member><link linkend="beast.ref.http__error_code">error_code</link></member>
<member><link linkend="beast.ref.http__headers">headers</link></member>
<member><link linkend="beast.ref.http__message">message</link></member>
<member><link linkend="beast.ref.http__resume_context">resume_context</link></member>
<member><link linkend="beast.ref.http__streambuf_body">streambuf_body</link></member>
@ -49,8 +50,6 @@
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__async_read">async_read</link></member>
<member><link linkend="beast.ref.http__async_write">async_write</link></member>
<member><link linkend="beast.ref.http__chunk_encode">chunk_encode</link></member>
<member><link linkend="beast.ref.http__chunk_encode_final">chunk_encode_final</link></member>
<member><link linkend="beast.ref.http__read">read</link></member>
<member><link linkend="beast.ref.http__write">write</link></member>
</simplelist>

View File

@ -252,7 +252,7 @@ In this table:
]
[
[`a.content_length()`]
[`std::size_t`]
[`std::uint64_t`]
[
If this member is present, it is called after initialization
and before calls to provide buffers. The serialized message will

View File

@ -5,26 +5,56 @@ GroupSources(examples)
add_executable (http-crawl
${BEAST_INCLUDES}
http_crawl.cpp
urls_large_data.hpp
urls_large_data.cpp
http_crawl.cpp
)
if (NOT WIN32)
target_link_libraries(http-crawl ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
endif()
add_executable (http-server
${BEAST_INCLUDES}
file_body.hpp
http_async_server.hpp
http_stream.hpp
http_stream.ipp
http_sync_server.hpp
sig_wait.hpp
http_server.cpp
)
if (NOT WIN32)
target_link_libraries(http-server ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
endif()
add_executable (http-example
${BEAST_INCLUDES}
http_example.cpp
)
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
)
if (NOT WIN32)
target_link_libraries(websocket-example ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
endif()

View File

@ -36,8 +36,8 @@ struct file_body
class writer
{
std::size_t size_;
std::size_t offset_ = 0;
std::uint64_t size_;
std::uint64_t offset_ = 0;
std::string const& path_;
FILE* file_ = nullptr;
char buf_[4096];
@ -69,7 +69,7 @@ struct file_body
size_ = boost::filesystem::file_size(path_);
}
std::size_t
std::uint64_t
content_length() const
{
return size_;
@ -79,7 +79,11 @@ struct file_body
boost::tribool
operator()(resume_context&&, error_code&, Write&& write)
{
buf_len_ = std::min(size_ - offset_, sizeof(buf_));
if(size_ - offset_ < sizeof(buf_))
buf_len_ = static_cast<std::size_t>(
size_ - offset_);
else
buf_len_ = sizeof(buf_);
auto const nread = fread(buf_, 1, sizeof(buf_), file_);
(void)nread;
offset_ += buf_len_;

View File

@ -20,8 +20,8 @@
#ifndef BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
#define BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
#include "file_body.h"
#include "http_stream.h"
#include "file_body.hpp"
#include "http_stream.hpp"
#include <beast/placeholders.hpp>
#include <boost/asio.hpp>
@ -131,6 +131,7 @@ private:
{404, "Not Found", req_.version});
resp.headers.replace("Server", "http_async_server");
resp.body = "The file '" + path + "' was not found";
prepare(resp);
stream_.async_write(std::move(resp),
std::bind(&peer::on_write, shared_from_this(),
asio::placeholders::error));
@ -141,6 +142,7 @@ private:
resp.headers.replace("Server", "http_async_server");
resp.headers.replace("Content-Type", "text/html");
resp.body = path;
prepare(resp);
stream_.async_write(std::move(resp),
std::bind(&peer::on_write, shared_from_this(),
asio::placeholders::error));

View File

@ -17,8 +17,8 @@
*/
//==============================================================================
#include "http_stream.h"
#include "urls_large_data.h"
#include "http_stream.hpp"
#include "urls_large_data.hpp"
#include <boost/asio.hpp>
#include <iostream>
@ -46,10 +46,11 @@ int main(int, char const*[])
stream<ip::tcp::socket> hs(ios);
connect(hs.lowest_layer(), it);
auto ep = hs.lowest_layer().remote_endpoint();
request<empty_body> req({method_t::http_get, "/", 11});
request<empty_body> req({"GET", "/", 11});
req.headers.insert("Host", host +
std::string(":") + std::to_string(ep.port()));
req.headers.insert("User-Agent", "beast/http");
prepare(req);
hs.write(req);
response<string_body> resp;
hs.read(resp);

View File

@ -23,9 +23,10 @@ int main()
using namespace beast::http;
// Send HTTP request using beast
request<empty_body> req({method_t::http_get, "/", 11});
request<empty_body> req({"GET", "/", 11});
req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port()));
req.headers.replace("User-Agent", "Beast");
prepare(req);
write(sock, req);
// Receive and print HTTP response using beast

View File

@ -17,9 +17,9 @@
*/
//==============================================================================
#include "http_async_server.h"
#include "http_sync_server.h"
#include "sig_wait.h"
#include "http_async_server.hpp"
#include "http_sync_server.hpp"
#include "sig_wait.hpp"
#include <boost/program_options.hpp>

View File

@ -20,8 +20,8 @@
#ifndef BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED
#define BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED
#include "file_body.h"
#include "http_stream.h"
#include "file_body.hpp"
#include "http_stream.hpp"
#include <boost/asio.hpp>
#include <cstdint>
@ -159,6 +159,7 @@ public:
{404, "Not Found", req.version});
resp.headers.replace("Server", "http_sync_server");
resp.body = "The file '" + path + "' was not found";
prepare(resp);
hs.write(resp, ec);
if(ec)
break;
@ -168,6 +169,7 @@ public:
resp.headers.replace("Server", "http_sync_server");
resp.headers.replace("Content-Type", "text/html");
resp.body = path;
prepare(resp);
hs.write(resp, ec);
if(ec)
break;

View File

@ -17,7 +17,7 @@
*/
//==============================================================================
#include "urls_large_data.h"
#include "urls_large_data.hpp"
// Data from Alexa top 1 million sites
// http://s3.amazonaws.com/alexa-static/top-1m.csv.zip

View File

@ -17,9 +17,9 @@
*/
//==============================================================================
#include "websocket_async_echo_peer.h"
#include "websocket_sync_echo_peer.h"
#include "sig_wait.h"
#include "websocket_async_echo_peer.hpp"
#include "websocket_sync_echo_peer.hpp"
#include "sig_wait.hpp"
int main()
{

View File

@ -5,8 +5,8 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <beast/to_string.hpp>
#include <beast/websocket.hpp>
#include <beast/buffers_debug.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <string>
@ -33,6 +33,5 @@ int main()
opcode op;
ws.read(op, sb);
ws.close(close_code::normal);
std::cout <<
beast::debug::buffers_to_string(sb.data()) << "\n";
std::cout << to_string(sb.data()) << "\n";
}

View File

@ -1,44 +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)
//
#ifndef BEAST_BUFFERS_DEBUG_HPP
#define BEAST_BUFFERS_DEBUG_HPP
#include <boost/asio/buffer.hpp>
#include <string>
namespace beast {
namespace debug {
/** Diagnostic utility to convert a `ConstBufferSequence` to a string.
@note Carriage returns and linefeeds will have additional escape
representations printed for visibility.
*/
template<class Buffers>
std::string
buffers_to_string(Buffers const& bs)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
std::string s;
s.reserve(buffer_size(bs));
for(auto const& b : bs)
s.append(buffer_cast<char const*>(b),
buffer_size(b));
for(auto i = s.size(); i-- > 0;)
if(s[i] == '\r')
s.replace(i, 1, "\\r");
else if(s[i] == '\n')
s.replace(i, 1, "\\n\n");
return s;
}
} // debug
} // beast
#endif

View File

@ -110,10 +110,24 @@ public:
consume(std::size_t n);
};
/// Returns a consumed buffer
template<class Buffers>
consuming_buffers<Buffers, typename Buffers::value_type>
consumed_buffers(Buffers const& bs, std::size_t n);
/** Returns a new, consumed buffer sequence.
This function returns a new buffer sequence which when iterated,
efficiently represents the portion of the original buffer sequence
with `n` bytes removed from the beginning.
Copies will be made of the buffer sequence passed, but ownership
of the underlying memory is not transferred.
@param buffers The buffer sequence to consume.
@param n The number of bytes to remove from the front. If this is
larger than the size of the buffer sequence, an empty buffer sequence
is returned.
*/
template<class BufferSequence>
consuming_buffers<BufferSequence, typename BufferSequence::value_type>
consumed_buffers(BufferSequence const& buffers, std::size_t n);
} // beast

View File

@ -41,13 +41,6 @@ public:
! is_string_literal<T>::value;
};
template<class Streambuf>
inline
void
write_streambuf(Streambuf&)
{
}
template<class Streambuf>
void
write_streambuf(Streambuf& streambuf,
@ -133,11 +126,11 @@ write_streambuf(Streambuf& streambuf, T const& t)
template<class Streambuf, class T0, class T1, class... TN>
void
write_streambuf(Streambuf& streambuf, T0&& t0, T1&& t1, TN... tn)
write_streambuf(Streambuf& streambuf,
T0 const& t0, T1 const& t1, TN const&... tn)
{
write_streambuf(streambuf, std::forward<T0>(t0));
write_streambuf(streambuf, std::forward<T1>(t1));
write_streambuf(streambuf, std::forward<TN>(tn)...);
write_streambuf(streambuf, t0);
write_streambuf(streambuf, t1, tn...);
}
} // detail

View File

@ -10,7 +10,6 @@
#include <beast/http/basic_headers.hpp>
#include <beast/http/basic_parser.hpp>
#include <beast/http/chunk_encode.hpp>
#include <beast/http/empty_body.hpp>
#include <beast/http/error.hpp>
#include <beast/http/headers.hpp>

View File

@ -13,7 +13,7 @@
#include <beast/http/rfc7230.hpp>
#include <beast/http/detail/basic_parser.hpp>
#include <beast/type_check.hpp>
#include <beast/detail/ci_char_traits.hpp>
#include <boost/asio/buffer.hpp>
#include <array>
#include <cassert>
#include <climits>
@ -345,7 +345,7 @@ public:
return s_ == s_restart;
}
/** Write data to the parser.
/** Write a sequence of buffers to the parser.
@param buffers An object meeting the requirements of
ConstBufferSequence that represents the input sequence.
@ -354,20 +354,23 @@ public:
@return The number of bytes consumed in the input sequence.
*/
template<class ConstBufferSequence>
template<class ConstBufferSequence,
class = typename std::enable_if<
! std::is_convertible<ConstBufferSequence,
boost::asio::const_buffer>::value>::type
>
std::size_t
write(ConstBufferSequence const& buffers, error_code& ec);
/** Write data to the parser.
/** Write a single buffer of data to the parser.
@param data A pointer to a buffer representing the input sequence.
@param size The number of bytes in the buffer pointed to by data.
@param buffer The buffer to write.
@param ec Set to the error, if any error occurred.
@return The number of bytes consumed in the input sequence.
@return The number of bytes consumed in the buffer.
*/
std::size_t
write(void const* data, std::size_t size, error_code& ec);
write(boost::asio::const_buffer const& buffer, error_code& ec);
/** Called to indicate the end of file.

View File

@ -5,8 +5,8 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_CHUNK_ENCODE_HPP
#define BEAST_HTTP_CHUNK_ENCODE_HPP
#ifndef BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP
#define BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP
#include <boost/asio/buffer.hpp>
#include <algorithm>
@ -18,7 +18,6 @@
namespace beast {
namespace http {
namespace detail {
template <class Buffers>
@ -237,9 +236,7 @@ chunk_encoded_buffers<Buffers>::const_iterator::const_iterator(
{
}
} // detail
/** Returns a chunk-encoded BufferSequence.
/* Returns a chunk-encoded BufferSequence.
See:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1
@ -276,6 +273,7 @@ chunk_encode_final()
"0\r\n\r\n", 5);
}
} // detail
} // http
} // beast

View File

@ -9,6 +9,7 @@
#define BEAST_HTTP_DETAIL_WRITE_PREPARATION_HPP
#include <beast/http/error.hpp>
#include <beast/http/rfc2616.hpp>
#include <beast/streambuf.hpp>
#include <beast/write_streambuf.hpp>
@ -54,6 +55,12 @@ struct write_preparation
message<isRequest, Body, Headers> const& msg_)
: msg(msg_)
, w(msg)
, chunked(rfc2616::token_in_list(
msg.headers["Transfer-Encoding"], "chunked"))
, close(rfc2616::token_in_list(
msg.headers["Connection"], "close") ||
(msg.version < 11 && ! msg.headers.exists(
"Content-Length")))
{
}
@ -63,57 +70,10 @@ struct write_preparation
w.init(ec);
if(ec)
return;
// VFALCO TODO This implementation requires making a
// copy of the headers, we can do better.
// VFALCO Should we be using handler_alloc?
headers_type h(msg.headers.begin(), msg.headers.end());
set_content_length(h, has_content_length<
typename Body::writer>{});
// VFALCO TODO Keep-Alive
if(close)
{
if(msg.version >= 11)
h.insert("Connection", "close");
}
else
{
if(msg.version < 11)
h.insert("Connection", "keep-alive");
}
msg.write_firstline(sb);
write_fields(sb, h);
write_fields(sb, msg.headers);
beast::write(sb, "\r\n");
}
private:
void
set_content_length(headers_type& h,
std::true_type)
{
close = false;
chunked = false;
h.insert("Content-Length", w.content_length());
}
void
set_content_length(headers_type& h,
std::false_type)
{
if(msg.version >= 11)
{
close = false;
chunked = true;
h.insert("Transfer-Encoding", "chunked");
}
else
{
close = true;
chunked = false;
}
}
};
} // detail

View File

@ -60,7 +60,7 @@ private:
{
}
std::size_t
std::uint64_t
content_length() const
{
return 0;

View File

@ -14,10 +14,7 @@
namespace beast {
namespace http {
template<class Allocator>
using headers = basic_headers<Allocator>;
using http_headers =
using headers =
basic_headers<std::allocator<char>>;
} // http

View File

@ -32,9 +32,27 @@ keep_alive() const
// Implementation inspired by nodejs/http-parser
template<bool isRequest, class Derived>
template<class ConstBufferSequence, class>
std::size_t
basic_parser<isRequest, Derived>::
write(void const* data, std::size_t size, error_code& ec)
write(ConstBufferSequence const& buffers, error_code& ec)
{
static_assert(is_ConstBufferSequence<ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
std::size_t used = 0;
for(auto const& buffer : buffers)
{
used += write(buffer, ec);
if(ec)
break;
}
return used;
}
template<bool isRequest, class Derived>
std::size_t
basic_parser<isRequest, Derived>::
write(boost::asio::const_buffer const& buffer, error_code& ec)
{
using beast::http::detail::is_digit;
using beast::http::detail::is_token;
@ -42,7 +60,12 @@ write(void const* data, std::size_t size, error_code& ec)
using beast::http::detail::to_field_char;
using beast::http::detail::to_value_char;
using beast::http::detail::unhex;
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
auto const data = buffer_cast<void const*>(buffer);
auto const size = buffer_size(buffer);
if(size == 0 && s_ != s_closed)
return 0;
@ -997,27 +1020,6 @@ write(void const* data, std::size_t size, error_code& ec)
return used();
}
template<bool isRequest, class Derived>
template<class ConstBufferSequence>
std::size_t
basic_parser<isRequest, Derived>::
write(ConstBufferSequence const& buffers, error_code& ec)
{
static_assert(is_ConstBufferSequence<ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
std::size_t used = 0;
for(auto const& buffer : buffers)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
used += write(buffer_cast<void const*>(buffer),
buffer_size(buffer), ec);
if(ec)
break;
}
return used;
}
template<bool isRequest, class Derived>
void
basic_parser<isRequest, Derived>::

View File

@ -8,7 +8,6 @@
#ifndef BEAST_HTTP_IMPL_MESSAGE_IPP
#define BEAST_HTTP_IMPL_MESSAGE_IPP
#include <beast/http/chunk_encode.hpp>
#include <beast/http/resume_context.hpp>
#include <beast/http/rfc2616.hpp>
#include <beast/write_streambuf.hpp>
@ -16,8 +15,10 @@
#include <beast/http/detail/write_preparation.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/logic/tribool.hpp>
#include <boost/optional.hpp>
#include <condition_variable>
#include <mutex>
#include <stdexcept>
namespace beast {
namespace http {
@ -55,7 +56,7 @@ message<isRequest, Body, Headers>::
write_firstline(Streambuf& streambuf,
std::true_type) const
{
write(streambuf, to_string(this->method));
write(streambuf, this->method);
write(streambuf, " ");
write(streambuf, this->url);
switch(version)
@ -105,122 +106,6 @@ write_firstline(Streambuf& streambuf,
write(streambuf, "\r\n");
}
namespace detail {
template<class ConstBufferSequence>
std::string
buffers_to_string(ConstBufferSequence const& buffers)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
std::string s;
s.reserve(buffer_size(buffers));
for(auto const& b : buffers)
s.append(buffer_cast<char const*>(b),
buffer_size(b));
return s;
}
class writef_ostream
{
std::ostream& os_;
bool chunked_;
public:
writef_ostream(std::ostream& os, bool chunked)
: os_(os)
, chunked_(chunked)
{
}
template<class ConstBufferSequence>
void
operator()(ConstBufferSequence const& buffers)
{
if(chunked_)
os_ << buffers_to_string(
chunk_encode(buffers));
else
os_ << buffers_to_string(
buffers);
}
};
} // detail
// Diagnostic output only
template<bool isRequest, class Body, class Headers>
std::ostream&
operator<<(std::ostream& os,
message<isRequest, Body, Headers> const& msg)
{
error_code ec;
detail::write_preparation<isRequest, Body, Headers> wp(msg);
wp.init(ec);
if(ec)
return os;
std::mutex m;
std::condition_variable cv;
bool ready = false;
resume_context resume{
[&]
{
std::lock_guard<std::mutex> lock(m);
ready = true;
cv.notify_one();
}};
auto copy = resume;
os << detail::buffers_to_string(wp.sb.data());
wp.sb.consume(wp.sb.size());
detail::writef_ostream writef(os, wp.chunked);
for(;;)
{
{
auto result = wp.w(std::move(copy), ec, writef);
if(ec)
return os;
if(result)
break;
if(boost::indeterminate(result))
{
copy = resume;
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&]{ return ready; });
ready = false;
}
}
wp.sb.consume(wp.sb.size());
for(;;)
{
auto result = wp.w(std::move(copy), ec, writef);
if(ec)
return os;
if(result)
break;
if(boost::indeterminate(result))
{
copy = resume;
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&]{ return ready; });
ready = false;
}
}
}
if(wp.chunked)
{
// VFALCO Unfortunately the current interface to the
// Writer concept prevents us from using coalescing the
// final body chunk with the final chunk delimiter.
//
// write final chunk
os << detail::buffers_to_string(chunk_encode_final());
if(ec)
return os;
}
os << std::endl;
return os;
}
//------------------------------------------------------------------------------
template<bool isRequest, class Body, class Headers>
@ -310,9 +195,176 @@ is_upgrade(message<isRequest, Body, Headers> const& msg)
return false;
}
namespace detail {
struct prepare_info
{
boost::optional<connection> connection_value;
boost::optional<std::uint64_t> content_length;
};
template<bool isRequest, class Body, class Headers>
inline
void
prepare_options(prepare_info& pi,
message<isRequest, Body, Headers>& msg)
{
}
template<bool isRequest, class Body, class Headers>
void
prepare_option(prepare_info& pi,
message<isRequest, Body, Headers>& msg,
connection value)
{
pi.connection_value = value;
}
template<
bool isRequest, class Body, class Headers,
class Opt, class... Opts>
void
prepare_options(prepare_info& pi,
message<isRequest, Body, Headers>& msg,
Opt&& opt, Opts&&... opts)
{
prepare_option(pi, msg, opt);
prepare_options(pi, msg,
std::forward<Opts>(opts)...);
}
template<bool isRequest, class Body, class Headers>
void
prepare_content_length(prepare_info& pi,
message<isRequest, Body, Headers> const& msg,
std::true_type)
{
typename Body::writer w(msg);
//w.init(ec); // VFALCO This is a design problem!
pi.content_length = w.content_length();
}
template<bool isRequest, class Body, class Headers>
void
prepare_content_length(prepare_info& pi,
message<isRequest, Body, Headers> const& msg,
std::false_type)
{
pi.content_length = boost::none;
}
} // detail
template<bool isRequest, class Body, class Headers>
void
prepare_connection(
message<isRequest, Body, Headers>& msg)
{
if(msg.version >= 11)
{
if(! msg.headers.exists("Content-Length") &&
! rfc2616::token_in_list(
msg.headers["Transfer-Encoding"], "chunked"))
if(! rfc2616::token_in_list(
msg.headers["Connection"], "close"))
msg.headers.insert("Connection", "close");
}
else
{
if(! msg.headers.exists("Content-Length"))
{
// VFALCO We are erasing the whole header when we
// should be removing just the keep-alive.
if(rfc2616::token_in_list(
msg.headers["Connection"], "keep-alive"))
msg.headers.erase("Connection");
}
}
}
template<
bool isRequest, class Body, class Headers,
class... Options>
void
prepare(message<isRequest, Body, Headers>& msg,
Options&&... options)
{
// VFALCO TODO
//static_assert(is_WritableBody<Body>::value,
// "WritableBody requirements not met");
detail::prepare_info pi;
detail::prepare_content_length(pi, msg,
detail::has_content_length<typename Body::writer>{});
detail::prepare_options(pi, msg,
std::forward<Options>(options)...);
if(msg.headers.exists("Connection"))
throw std::invalid_argument(
"prepare called with Connection field set");
if(msg.headers.exists("Content-Length"))
throw std::invalid_argument(
"prepare called with Content-Length field set");
if(rfc2616::token_in_list(
msg.headers["Transfer-Encoding"], "chunked"))
throw std::invalid_argument(
"prepare called with Transfer-Encoding: chunked set");
if(pi.connection_value != connection::upgrade)
{
if(pi.content_length)
{
// VFALCO TODO Use a static string here
msg.headers.insert("Content-Length",
std::to_string(*pi.content_length));
}
else if(msg.version >= 11)
{
msg.headers.insert("Transfer-Encoding", "chunked");
}
}
auto const content_length =
msg.headers.exists("Content-Length");
if(pi.connection_value)
{
switch(*pi.connection_value)
{
case connection::upgrade:
msg.headers.insert("Connection", "upgrade");
break;
case connection::keep_alive:
if(msg.version < 11)
{
if(content_length)
msg.headers.insert("Connection", "keep-alive");
}
break;
case connection::close:
if(msg.version >= 11)
msg.headers.insert("Connection", "close");
break;
}
}
// rfc7230 6.7.
if(msg.version < 11 && rfc2616::token_in_list(
msg.headers["Connection"], "upgrade"))
throw std::invalid_argument(
"invalid version for Connection: upgrade");
// rfc7230 3.3.2
if(msg.headers.exists("Content-Length") &&
msg.headers.exists("Transfer-Encoding"))
throw std::invalid_argument(
"Content-Length and Transfer-Encoding cannot be combined");
}
} // http
} // beast
#include <beast/http/impl/message.ipp>
#endif

View File

@ -8,8 +8,8 @@
#ifndef BEAST_HTTP_IMPL_WRITE_IPP
#define BEAST_HTTP_IMPL_WRITE_IPP
#include <beast/http/chunk_encode.hpp>
#include <beast/http/resume_context.hpp>
#include <beast/http/detail/chunk_encode.hpp>
#include <beast/http/detail/write_preparation.hpp>
#include <beast/buffer_cat.hpp>
#include <beast/bind_handler.hpp>
@ -20,6 +20,8 @@
#include <boost/logic/tribool.hpp>
#include <condition_variable>
#include <mutex>
#include <ostream>
#include <sstream>
#include <type_traits>
namespace beast {
@ -77,7 +79,7 @@ class write_op
if(d.wp.chunked)
boost::asio::async_write(d.s,
buffer_cat(d.wp.sb.data(),
chunk_encode(buffers)),
detail::chunk_encode(buffers)),
std::move(self_));
else
boost::asio::async_write(d.s,
@ -104,7 +106,7 @@ class write_op
// write body
if(d.wp.chunked)
boost::asio::async_write(d.s,
chunk_encode(buffers),
detail::chunk_encode(buffers),
std::move(self_));
else
boost::asio::async_write(d.s,
@ -269,7 +271,7 @@ operator()(error_code ec, std::size_t, bool again)
// write final chunk
d.state = 5;
boost::asio::async_write(d.s,
chunk_encode_final(), std::move(*this));
detail::chunk_encode_final(), std::move(*this));
return;
case 5:
@ -311,7 +313,7 @@ public:
// write headers and body
if(chunked_)
boost::asio::write(stream_, buffer_cat(
sb_.data(), chunk_encode(buffers)), ec_);
sb_.data(), detail::chunk_encode(buffers)), ec_);
else
boost::asio::write(stream_, buffer_cat(
sb_.data(), buffers), ec_);
@ -340,7 +342,7 @@ public:
// write body
if(chunked_)
boost::asio::write(stream_,
chunk_encode(buffers), ec_);
detail::chunk_encode(buffers), ec_);
else
boost::asio::write(stream_, buffers, ec_);
}
@ -357,6 +359,8 @@ write(SyncWriteStream& stream,
message<isRequest, Body, Headers> const& msg,
boost::system::error_code& ec)
{
static_assert(is_SyncWriteStream<SyncWriteStream>::value,
"SyncWriteStream requirements not met");
detail::write_preparation<isRequest, Body, Headers> wp(msg);
wp.init(ec);
if(ec)
@ -420,7 +424,7 @@ write(SyncWriteStream& stream,
// final body chunk with the final chunk delimiter.
//
// write final chunk
boost::asio::write(stream, chunk_encode_final(), ec);
boost::asio::write(stream, detail::chunk_encode_final(), ec);
if(ec)
return;
}
@ -450,6 +454,68 @@ async_write(AsyncWriteStream& stream,
return completion.result.get();
}
namespace detail {
class ostream_SyncStream
{
std::ostream& os_;
public:
ostream_SyncStream(std::ostream& os)
: os_(os)
{
}
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& ec)
{
std::size_t n = 0;
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
for(auto const& buffer : buffers)
{
os_.write(buffer_cast<char const*>(buffer),
buffer_size(buffer));
if(os_.fail())
{
ec = boost::system::errc::make_error_code(
boost::system::errc::no_stream_resources);
break;
}
n += buffer_size(buffer);
}
return n;
}
};
} // detail
template<bool isRequest, class Body, class Headers>
std::ostream&
operator<<(std::ostream& os,
message<isRequest, Body, Headers> const& msg)
{
detail::ostream_SyncStream oss(os);
error_code ec;
write(oss, msg, ec);
if(ec && ec != boost::asio::error::eof)
throw boost::system::system_error{ec};
return os;
}
} // http
} // beast

View File

@ -9,11 +9,8 @@
#define BEAST_HTTP_MESSAGE_HPP
#include <beast/http/basic_headers.hpp>
#include <beast/http/method.hpp>
#include <beast/buffers_debug.hpp>
#include <beast/type_check.hpp>
#include <memory>
#include <ostream>
#include <string>
namespace beast {
@ -23,7 +20,7 @@ namespace detail {
struct request_fields
{
http::method_t method;
std::string method;
std::string url;
};
@ -39,7 +36,7 @@ struct response_fields
struct request_params
{
http::method_t method;
std::string method;
std::string url;
int version;
};
@ -145,12 +142,6 @@ using response = message<false, Body, Headers>;
#endif
// For diagnostic output only
template<bool isRequest, class Body, class Headers>
std::ostream&
operator<<(std::ostream& os,
message<isRequest, Body, Headers> const& m);
/// Write a FieldSequence to a Streambuf.
template<class Streambuf, class FieldSequence>
void
@ -166,6 +157,35 @@ template<bool isRequest, class Body, class Headers>
bool
is_upgrade(message<isRequest, Body, Headers> const& msg);
/** Connection prepare options.
These values are used with prepare().
*/
enum class connection
{
/// Indicates the message should specify Connection: close semantics
close,
/// Indicates the message should specify Connection: keep-alive semantics if possible
keep_alive,
/// Indicates the message should specify a Connection: upgrade
upgrade
};
/** Prepare a message.
This function will adjust the Content-Length, Transfer-Encoding,
and Connection headers of the message based on the properties of
the body and the options passed in.
*/
template<
bool isRequest, class Body, class Headers,
class... Options>
void
prepare(message<isRequest, Body, Headers>& msg,
Options&&... options);
} // http
} // beast

View File

@ -1,179 +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)
//
#ifndef BEAST_HTTP_METHOD_HPP
#define BEAST_HTTP_METHOD_HPP
#include <cassert>
#include <memory>
#include <string>
namespace beast {
namespace http {
enum class method_t
{
http_delete,
http_get,
http_head,
http_post,
http_put,
// pathological
http_connect,
http_options,
http_trace,
// webdav
http_copy,
http_lock,
http_mkcol,
http_move,
http_propfind,
http_proppatch,
http_search,
http_unlock,
http_bind,
http_rebind,
http_unbind,
http_acl,
// subversion
http_report,
http_mkactivity,
http_checkout,
http_merge,
// upnp
http_msearch,
http_notify,
http_subscribe,
http_unsubscribe,
// RFC-5789
http_patch,
http_purge,
// CalDav
http_mkcalendar,
// RFC-2068, section 19.6.1.2
http_link,
http_unlink
};
template<class = void>
std::string
to_string(method_t m)
{
switch(m)
{
case method_t::http_delete: return "DELETE";
case method_t::http_get: return "GET";
case method_t::http_head: return "HEAD";
case method_t::http_post: return "POST";
case method_t::http_put: return "PUT";
case method_t::http_connect: return "CONNECT";
case method_t::http_options: return "OPTIONS";
case method_t::http_trace: return "TRACE";
case method_t::http_copy: return "COPY";
case method_t::http_lock: return "LOCK";
case method_t::http_mkcol: return "MKCOL";
case method_t::http_move: return "MOVE";
case method_t::http_propfind: return "PROPFIND";
case method_t::http_proppatch: return "PROPPATCH";
case method_t::http_search: return "SEARCH";
case method_t::http_unlock: return "UNLOCK";
case method_t::http_report: return "REPORT";
case method_t::http_mkactivity: return "MKACTIVITY";
case method_t::http_checkout: return "CHECKOUT";
case method_t::http_merge: return "MERGE";
case method_t::http_msearch: return "MSEARCH";
case method_t::http_notify: return "NOTIFY";
case method_t::http_subscribe: return "SUBSCRIBE";
case method_t::http_unsubscribe: return "UNSUBSCRIBE";
case method_t::http_patch: return "PATCH";
case method_t::http_purge: return "PURGE";
default:
assert(false);
break;
};
return "GET";
}
template <class Stream>
Stream&
operator<< (Stream& s, method_t m)
{
return s << to_string(m);
}
/** Returns the string corresponding to the numeric HTTP status code. */
template<class = void>
std::string
status_text (int status)
{
switch(status)
{
case 100: return "Continue";
case 101: return "Switching Protocols";
case 200: return "OK";
case 201: return "Created";
case 202: return "Accepted";
case 203: return "Non-Authoritative Information";
case 204: return "No Content";
case 205: return "Reset Content";
case 206: return "Partial Content";
case 300: return "Multiple Choices";
case 301: return "Moved Permanently";
case 302: return "Found";
case 303: return "See Other";
case 304: return "Not Modified";
case 305: return "Use Proxy";
//case 306: return "<reserved>";
case 307: return "Temporary Redirect";
case 400: return "Bad Request";
case 401: return "Unauthorized";
case 402: return "Payment Required";
case 403: return "Forbidden";
case 404: return "Not Found";
case 405: return "Method Not Allowed";
case 406: return "Not Acceptable";
case 407: return "Proxy Authentication Required";
case 408: return "Request Timeout";
case 409: return "Conflict";
case 410: return "Gone";
case 411: return "Length Required";
case 412: return "Precondition Failed";
case 413: return "Request Entity Too Large";
case 414: return "Request-URI Too Long";
case 415: return "Unsupported Media Type";
case 416: return "Requested Range Not Satisfiable";
case 417: return "Expectation Failed";
case 500: return "Internal Server Error";
case 501: return "Not Implemented";
case 502: return "Bad Gateway";
case 503: return "Service Unavailable";
case 504: return "Gateway Timeout";
case 505: return "HTTP Version Not Supported";
default:
break;
}
return "Unknown HTTP status";
}
} // http
} // beast
#endif

View File

@ -107,96 +107,14 @@ private:
void set(std::true_type)
{
// VFALCO This is terrible for setting method
auto m =
[&](char const* s, method_t m)
{
if(this->method_ == s)
{
m_.method = m;
return true;
}
return false;
};
do
{
if(m("DELETE", method_t::http_delete))
break;
if(m("GET", method_t::http_get))
break;
if(m("HEAD", method_t::http_head))
break;
if(m("POST", method_t::http_post))
break;
if(m("PUT", method_t::http_put))
break;
if(m("CONNECT", method_t::http_connect))
break;
if(m("OPTIONS", method_t::http_options))
break;
if(m("TRACE", method_t::http_trace))
break;
if(m("COPY", method_t::http_copy))
break;
if(m("LOCK", method_t::http_lock))
break;
if(m("MKCOL", method_t::http_mkcol))
break;
if(m("MOVE", method_t::http_move))
break;
if(m("PROPFIND", method_t::http_propfind))
break;
if(m("PROPPATCH", method_t::http_proppatch))
break;
if(m("SEARCH", method_t::http_search))
break;
if(m("UNLOCK", method_t::http_unlock))
break;
if(m("BIND", method_t::http_bind))
break;
if(m("REBID", method_t::http_rebind))
break;
if(m("UNBIND", method_t::http_unbind))
break;
if(m("ACL", method_t::http_acl))
break;
if(m("REPORT", method_t::http_report))
break;
if(m("MKACTIVITY", method_t::http_mkactivity))
break;
if(m("CHECKOUT", method_t::http_checkout))
break;
if(m("MERGE", method_t::http_merge))
break;
if(m("MSEARCH", method_t::http_msearch))
break;
if(m("NOTIFY", method_t::http_notify))
break;
if(m("SUBSCRIBE", method_t::http_subscribe))
break;
if(m("UNSUBSCRIBE",method_t::http_unsubscribe))
break;
if(m("PATCH", method_t::http_patch))
break;
if(m("PURGE", method_t::http_purge))
break;
if(m("MKCALENDAR", method_t::http_mkcalendar))
break;
if(m("LINK", method_t::http_link))
break;
if(m("UNLINK", method_t::http_unlink))
break;
}
while(false);
m_.method = std::move(this->method_);
m_.url = std::move(this->uri_);
}
void set(std::false_type)
{
m_.status = this->status_code();
m_.reason = this->reason_;
m_.reason = std::move(this->reason_);
}
int on_headers(error_code&)

View File

@ -0,0 +1,71 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_STATUS_HPP
#define BEAST_HTTP_STATUS_HPP
namespace beast {
namespace http {
/** Returns the string corresponding to the numeric HTTP status code. */
template<class = void>
char const*
status_text(int status)
{
switch(status)
{
case 100: return "Continue";
case 101: return "Switching Protocols";
case 200: return "OK";
case 201: return "Created";
case 202: return "Accepted";
case 203: return "Non-Authoritative Information";
case 204: return "No Content";
case 205: return "Reset Content";
case 206: return "Partial Content";
case 300: return "Multiple Choices";
case 301: return "Moved Permanently";
case 302: return "Found";
case 303: return "See Other";
case 304: return "Not Modified";
case 305: return "Use Proxy";
// case 306: return "<reserved>";
case 307: return "Temporary Redirect";
case 400: return "Bad Request";
case 401: return "Unauthorized";
case 402: return "Payment Required";
case 403: return "Forbidden";
case 404: return "Not Found";
case 405: return "Method Not Allowed";
case 406: return "Not Acceptable";
case 407: return "Proxy Authentication Required";
case 408: return "Request Timeout";
case 409: return "Conflict";
case 410: return "Gone";
case 411: return "Length Required";
case 412: return "Precondition Failed";
case 413: return "Request Entity Too Large";
case 414: return "Request-URI Too Long";
case 415: return "Unsupported Media Type";
case 416: return "Requested Range Not Satisfiable";
case 417: return "Expectation Failed";
case 500: return "Internal Server Error";
case 501: return "Not Implemented";
case 502: return "Bad Gateway";
case 503: return "Service Unavailable";
case 504: return "Gateway Timeout";
case 505: return "HTTP Version Not Supported";
default:
break;
}
return "Unknown HTTP status";
}
} // http
} // beast
#endif

View File

@ -72,7 +72,7 @@ private:
{
}
std::size_t
std::uint64_t
content_length() const
{
return body_.size();

View File

@ -70,7 +70,7 @@ private:
{
}
std::size_t
std::uint64_t
content_length() const
{
return body_.size();

View File

@ -0,0 +1,62 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_TYPE_CHECK_HPP
#define BEAST_HTTP_TYPE_CHECK_HPP
#include <beast/http/error.hpp>
#include <boost/asio/buffer.hpp>
#include <type_traits>
#include <utility>
namespace beast {
namespace http {
/// Determine if `T` meets the requirements of `Parser`.
template<class T>
class is_Parser
{
template<class U, class R =
std::is_convertible<decltype(
std::declval<U>().complete()),
bool>>
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class U, class R =
std::is_convertible<decltype(
std::declval<U>().write(
std::declval<boost::asio::const_buffer const&>(),
std::declval<error_code&>())),
std::size_t>>
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
template<class U, class R =
std::is_convertible<decltype(
std::declval<U>().write_eof(
std::declval<error_code&>())),
std::size_t>>
static R check3(int);
template<class>
static std::false_type check3(...);
using type3 = decltype(check3<T>(0));
public:
/// `true` if `T` meets the requirements.
static bool const value =
type1::value && type2::value && type3::value;
};
} // http
} // beast
#endif

View File

@ -11,7 +11,9 @@
#include <beast/http/error.hpp>
#include <beast/http/message.hpp>
#include <beast/async_completion.hpp>
#include <beast/type_check.hpp>
#include <boost/system/error_code.hpp>
#include <ostream>
#include <type_traits>
namespace beast {
@ -85,6 +87,20 @@ async_write(AsyncWriteStream& stream,
message<isRequest, Body, Headers> const& msg,
WriteHandler&& handler);
/** Serialize a message to an ostream.
The function converts the message to its HTTP/1.* serialized
representation and stores the result in the output stream.
@param os The ostream to write to.
@param msg The message to write.
*/
template<bool isRequest, class Body, class Headers>
std::ostream&
operator<<(std::ostream& os,
message<isRequest, Body, Headers> const& msg);
} // http
} // beast

View File

@ -118,7 +118,7 @@ public:
template<class Allocator>
class basic_streambuf<Allocator>::const_buffers_type
{
basic_streambuf const* sb_ = nullptr;
basic_streambuf const* sb_;
friend class basic_streambuf;
@ -126,12 +126,12 @@ class basic_streambuf<Allocator>::const_buffers_type
const_buffers_type(basic_streambuf const& sb);
public:
/// Why?
// Why?
using value_type = boost::asio::const_buffer;
class const_iterator;
const_buffers_type() = default;
const_buffers_type() = delete;
const_buffers_type(const_buffers_type const&) = default;
const_buffers_type& operator=(const_buffers_type const&) = default;
@ -157,7 +157,7 @@ public:
class const_iterator;
mutable_buffers_type() = default;
mutable_buffers_type() = delete;
mutable_buffers_type(mutable_buffers_type const&) = default;
mutable_buffers_type& operator=(mutable_buffers_type const&) = default;

View File

@ -27,7 +27,7 @@ public:
class const_iterator;
const_buffers_type() = default;
const_buffers_type() = delete;
const_buffers_type(
const_buffers_type const&) = default;
const_buffers_type& operator=(
@ -52,7 +52,7 @@ template<class Buffers>
class buffers_adapter<Buffers>::const_buffers_type::const_iterator
{
iter_type it_;
buffers_adapter const* ba_;
buffers_adapter const* ba_ = nullptr;
public:
using value_type = boost::asio::const_buffer;
@ -167,7 +167,7 @@ public:
class const_iterator;
mutable_buffers_type() = default;
mutable_buffers_type() = delete;
mutable_buffers_type(
mutable_buffers_type const&) = default;
mutable_buffers_type& operator=(
@ -193,7 +193,7 @@ template<class Buffers>
class buffers_adapter<Buffers>::mutable_buffers_type::const_iterator
{
iter_type it_;
buffers_adapter const* ba_;
buffers_adapter const* ba_ = nullptr;
public:
using value_type = boost::asio::mutable_buffer;

View File

@ -27,7 +27,7 @@ class consuming_buffers<Buffers, ValueType>::const_iterator
typename Buffers::const_iterator;
iter_type it_;
consuming_buffers const* b_;
consuming_buffers const* b_ = nullptr;
public:
using value_type =
@ -59,9 +59,8 @@ public:
reference
operator*() const
{
if(it_ == b_->begin_)
return *it_ + b_->skip_;
return *it_;
return it_ == b_->begin_ ?
*it_ + b_->skip_ : *it_;
}
pointer

View File

@ -46,7 +46,7 @@ class prepared_buffers<BufferSequence>::const_iterator
using iter_type =
typename BufferSequence::const_iterator;
prepared_buffers const* b_;
prepared_buffers const* b_ = nullptr;
typename BufferSequence::const_iterator it_;
public:

View File

@ -26,7 +26,7 @@ public:
class const_iterator;
const_buffers_type() = default;
const_buffers_type() = delete;
const_buffers_type(
const_buffers_type const&) = default;
const_buffers_type& operator=(
@ -51,8 +51,8 @@ private:
class static_streambuf::const_buffers_type::const_iterator
{
std::size_t n_;
std::uint8_t const* p_;
std::size_t n_ = 0;
std::uint8_t const* p_ = nullptr;
public:
using value_type = boost::asio::const_buffer;
@ -158,7 +158,7 @@ public:
class const_iterator;
mutable_buffers_type() = default;
mutable_buffers_type() = delete;
mutable_buffers_type(
mutable_buffers_type const&) = default;
mutable_buffers_type& operator=(
@ -183,8 +183,8 @@ private:
class static_streambuf::mutable_buffers_type::const_iterator
{
std::size_t n_;
std::uint8_t* p_;
std::size_t n_ = 0;
std::uint8_t* p_ = nullptr;
public:
using value_type = boost::asio::mutable_buffer;

View File

@ -0,0 +1,51 @@
//
// 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_TO_STRING_HPP
#define BEAST_TO_STRING_HPP
#include <beast/type_check.hpp>
#include <boost/asio/buffer.hpp>
#include <string>
namespace beast {
/** Convert a `ConstBufferSequence` to a `std::string`.
This function will convert the octets in a buffer sequence to a string.
All octets will be inserted into the resulting string, including null
or unprintable characters.
@param buffers The buffer sequence to convert.
@returns A string representing the contents of the input area.
@note This function participates in overload resolution only if
the streambuf parameter meets the requirements of Streambuf.
*/
template<class ConstBufferSequence
#if ! GENERATING_DOCS
,class = std::enable_if<is_ConstBufferSequence<
ConstBufferSequence>::value>
#endif
>
std::string
to_string(ConstBufferSequence const& buffers)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
std::string s;
s.reserve(buffer_size(buffers));
for(auto const& buffer : buffers)
s.append(buffer_cast<char const*>(buffer),
buffer_size(buffer));
return s;
}
} // beast
#endif

View File

@ -53,6 +53,17 @@ little_uint32_to_native(void const* buf)
(static_cast<std::uint64_t>(p[3])<<24);
}
inline
void
native_to_little_uint32(std::uint32_t v, void* buf)
{
auto p = reinterpret_cast<std::uint8_t*>(buf);
p[0] = v & 0xff;
p[1] = (v >> 8) & 0xff;
p[2] = (v >> 16) & 0xff;
p[3] = (v >> 24) & 0xff;
}
} // detail
} // websocket
} // beast

View File

@ -141,9 +141,7 @@ write(Streambuf& sb, frame_header const& fh)
}
if(fh.mask)
{
little_uint32_buf_t key(fh.key);
std::copy(key.data(),
key.data() + 4, &b[n]);
native_to_little_uint32(fh.key, &b[n]);
n += 4;
}
sb.commit(buffer_copy(

View File

@ -34,7 +34,6 @@ class stream<NextLayer>::close_op
close_reason cr;
Handler h;
fb_type fb;
fmb_type fmb;
bool cont;
int state = 0;

View File

@ -42,10 +42,10 @@ class stream<NextLayer>::read_frame_op
stream<NextLayer>& ws;
frame_info& fi;
Streambuf& sb;
smb_type smb;
Handler h;
fb_type fb;
fmb_type fmb;
boost::optional<smb_type> smb;
boost::optional<fmb_type> fmb;
bool cont;
int state = 0;
@ -161,7 +161,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
d.smb = d.sb.prepare(
detail::clamp(d.ws.rd_need_));
d.ws.stream_.async_read_some(
d.smb, std::move(*this));
*d.smb, std::move(*this));
return;
case 2:
@ -176,7 +176,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
{
d.ws.rd_need_ -= bytes_transferred;
auto const pb = prepare_buffers(
bytes_transferred, d.smb);
bytes_transferred, *d.smb);
if(d.ws.rd_fh_.mask)
detail::mask_inplace(pb, d.ws.rd_key_);
if(d.ws.rd_opcode_ == opcode::text)
@ -252,7 +252,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
d.fmb = d.fb.prepare(static_cast<
std::size_t>(d.ws.rd_fh_.len));
boost::asio::async_read(d.ws.stream_,
d.fmb, std::move(*this));
*d.fmb, std::move(*this));
return;
}
d.state = 8;
@ -276,7 +276,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again)
case 7:
if(d.ws.rd_fh_.mask)
detail::mask_inplace(
d.fmb, d.ws.rd_key_);
*d.fmb, d.ws.rd_key_);
d.fb.commit(bytes_transferred);
d.state = 8;
break;

View File

@ -99,7 +99,8 @@ stream_base::write_close(
fh.rsv3 = false;
fh.len = cr.code == close_code::none ?
0 : 2 + cr.reason.size();
if((fh.mask = (role_ == role_type::client)))
fh.mask = role_ == role_type::client;
if(fh.mask)
fh.key = maskgen_();
detail::write(sb, fh);
if(cr.code != close_code::none)
@ -143,7 +144,8 @@ stream_base::write_ping(Streambuf& sb,
fh.rsv2 = false;
fh.rsv3 = false;
fh.len = data.size();
if((fh.mask = (role_ == role_type::client)))
fh.mask = role_ == role_type::client;
if(fh.mask)
fh.key = maskgen_();
detail::write(sb, fh);
if(data.empty())
@ -610,7 +612,8 @@ write_frame(bool fin, ConstBufferSequence const& bs, error_code& ec)
fh.rsv2 = false;
fh.rsv3 = false;
fh.len = buffer_size(bs);
if((fh.mask = (role_ == role_type::client)))
fh.mask = role_ == role_type::client;
if(fh.mask)
fh.key = maskgen_();
detail::fh_streambuf fh_buf;
detail::write<static_streambuf>(fh_buf, fh);
@ -698,14 +701,14 @@ build_request(boost::string_ref const& host,
http::request<http::empty_body> req;
req.url = "/";
req.version = 11;
req.method = http::method_t::http_get;
req.method = "GET";
req.headers.insert("Host", host);
req.headers.insert("Connection", "upgrade");
req.headers.insert("Upgrade", "websocket");
key = detail::make_sec_ws_key(maskgen_);
req.headers.insert("Sec-WebSocket-Key", key);
req.headers.insert("Sec-WebSocket-Version", "13");
(*d_)(req);
http::prepare(req, http::connection::upgrade);
return req;
}
@ -726,7 +729,7 @@ build_response(http::message<true, Body, Headers> const& req)
};
if(req.version < 11)
return err("HTTP version 1.1 required");
if(req.method != http::method_t::http_get)
if(req.method != "GET")
return err("Wrong method");
if(! is_upgrade(req))
return err("Expected Upgrade request");
@ -748,7 +751,6 @@ build_response(http::message<true, Body, Headers> const& req)
http::response<http::string_body> resp(
{101, http::reason_string(101), req.version});
resp.headers.insert("Upgrade", "websocket");
resp.headers.insert("Connection", "upgrade");
{
auto const key =
req.headers["Sec-WebSocket-Key"];
@ -758,6 +760,7 @@ build_response(http::message<true, Body, Headers> const& req)
}
resp.headers.replace("Server", "Beast.WSProto");
(*d_)(resp);
http::prepare(resp, http::connection::upgrade);
return resp;
}

View File

@ -53,9 +53,9 @@ void
#else
typename std::enable_if<is_Streambuf<Streambuf>::value>::type
#endif
write(Streambuf& streambuf, Args&&... args)
write(Streambuf& streambuf, Args const&... args)
{
detail::write_streambuf(streambuf, std::forward<Args>(args)...);
detail::write_streambuf(streambuf, args...);
}
} // beast

View File

@ -3,14 +3,14 @@
GroupSources(include/beast)
GroupSources(test)
set(CORE_TESTS_SRCS
add_executable (core-tests
${BEAST_INCLUDES}
main.cpp
async_completion.cpp
basic_streambuf.cpp
bind_handler.cpp
buffer_cat.cpp
buffers_adapter.cpp
buffers_debug.cpp
consuming_buffers.cpp
handler_alloc.cpp
placeholders.cpp
@ -18,26 +18,25 @@ set(CORE_TESTS_SRCS
static_streambuf.cpp
streambuf.cpp
streambuf_readstream.cpp
to_string.cpp
type_check.cpp
detail/base64.cpp
detail/empty_base_optimization.cpp
)
add_executable (core-tests
${BEAST_INCLUDES}
${CORE_TESTS_SRCS}
)
if (NOT WIN32)
target_link_libraries(core-tests ${Boost_LIBRARIES})
endif()
set(HTTP_TESTS_SRCS
add_executable (http-tests
${BEAST_INCLUDES}
main.cpp
http/basic_headers.cpp
http/basic_parser.cpp
http/chunk_encode.cpp
http/empty_body.cpp
http/error.cpp
http/headers.cpp
http/message.cpp
http/method.cpp
http/parse_error.cpp
http/parser.cpp
http/read.cpp
@ -45,17 +44,18 @@ set(HTTP_TESTS_SRCS
http/resume_context.cpp
http/rfc2616.cpp
http/rfc7230.cpp
http/status.cpp
http/streambuf_body.cpp
http/string_body.cpp
http/write.cpp
)
add_executable (http-tests
${BEAST_INCLUDES}
${HTTP_TESTS_SRCS}
)
if (NOT WIN32)
target_link_libraries(http-tests ${Boost_LIBRARIES})
endif()
set(WEBSOCKET_TESTS_SRCS
add_executable (websocket-tests
${BEAST_INCLUDES}
main.cpp
websocket/error.cpp
websocket/option.cpp
@ -65,18 +65,18 @@ set(WEBSOCKET_TESTS_SRCS
websocket/utf8_checker.cpp
)
add_executable (websocket-tests
${BEAST_INCLUDES}
${WEBSOCKET_TESTS_SRCS}
)
if (NOT WIN32)
target_link_libraries(websocket-tests ${Boost_LIBRARIES})
endif()
set(PARSER_BENCH_SRCS
add_executable (parser-bench
${BEAST_INCLUDES}
main.cpp
http/nodejs_parser.cpp
http/parser_bench.cpp
)
add_executable (parser-bench
${BEAST_INCLUDES}
${PARSER_BENCH_SRCS}
)
if (NOT WIN32)
target_link_libraries(parser-bench ${Boost_LIBRARIES})
endif()

View File

@ -14,7 +14,6 @@ unit-test core-tests :
bind_handler.cpp
buffer_cat.cpp
buffers_adapter.cpp
buffers_debug.cpp
consuming_buffers.cpp
handler_alloc.cpp
placeholders.cpp
@ -22,6 +21,7 @@ unit-test core-tests :
static_streambuf.cpp
streambuf.cpp
streambuf_readstream.cpp
to_string.cpp
type_check.cpp
detail/base64.cpp
detail/empty_base_optimization.cpp
@ -31,12 +31,10 @@ unit-test http-tests :
main.cpp
http/basic_headers.cpp
http/basic_parser.cpp
http/chunk_encode.cpp
http/empty_body.cpp
http/error.cpp
http/headers.cpp
http/message.cpp
http/method.cpp
http/parse_error.cpp
http/parser.cpp
http/read.cpp
@ -44,6 +42,7 @@ unit-test http-tests :
http/resume_context.cpp
http/rfc2616.cpp
http/rfc7230.cpp
http/status.cpp
http/streambuf_body.cpp
http/string_body.cpp
http/write.cpp

View File

@ -179,40 +179,81 @@ public:
std::size_t v = s.size() - (t + u);
{
streambuf sb(i);
decltype(sb)::mutable_buffers_type d;
d = sb.prepare(z); expect(buffer_size(d) == z);
d = sb.prepare(0); expect(buffer_size(d) == 0);
d = sb.prepare(y); expect(buffer_size(d) == y);
d = sb.prepare(x); expect(buffer_size(d) == x);
sb.commit(buffer_copy(d, buffer(s.data(), x)));
{
auto d = sb.prepare(z);
expect(buffer_size(d) == z);
}
{
auto d = sb.prepare(0);
expect(buffer_size(d) == 0);
}
{
auto d = sb.prepare(y);
expect(buffer_size(d) == y);
}
{
auto d = sb.prepare(x);
expect(buffer_size(d) == x);
sb.commit(buffer_copy(d, buffer(s.data(), x)));
}
expect(sb.size() == x);
expect(buffer_size(sb.data()) == sb.size());
d = sb.prepare(x); expect(buffer_size(d) == x);
d = sb.prepare(0); expect(buffer_size(d) == 0);
d = sb.prepare(z); expect(buffer_size(d) == z);
d = sb.prepare(y); expect(buffer_size(d) == y);
sb.commit(buffer_copy(d, buffer(s.data()+x, y)));
{
auto d = sb.prepare(x);
expect(buffer_size(d) == x);
}
{
auto d = sb.prepare(0);
expect(buffer_size(d) == 0);
}
{
auto d = sb.prepare(z);
expect(buffer_size(d) == z);
}
{
auto d = sb.prepare(y);
expect(buffer_size(d) == y);
sb.commit(buffer_copy(d, buffer(s.data()+x, y)));
}
sb.commit(1);
expect(sb.size() == x + y);
expect(buffer_size(sb.data()) == sb.size());
d = sb.prepare(x); expect(buffer_size(d) == x);
d = sb.prepare(y); expect(buffer_size(d) == y);
d = sb.prepare(0); expect(buffer_size(d) == 0);
d = sb.prepare(z); expect(buffer_size(d) == z);
sb.commit(buffer_copy(d, buffer(s.data()+x+y, z)));
{
auto d = sb.prepare(x);
expect(buffer_size(d) == x);
}
{
auto d = sb.prepare(y);
expect(buffer_size(d) == y);
}
{
auto d = sb.prepare(0);
expect(buffer_size(d) == 0);
}
{
auto d = sb.prepare(z);
expect(buffer_size(d) == z);
sb.commit(buffer_copy(d, buffer(s.data()+x+y, z)));
}
sb.commit(2);
expect(sb.size() == x + y + z);
expect(buffer_size(sb.data()) == sb.size());
expect(to_string(sb.data()) == s);
sb.consume(t);
d = sb.prepare(0); expect(buffer_size(d) == 0);
{
auto d = sb.prepare(0);
expect(buffer_size(d) == 0);
}
expect(to_string(sb.data()) == s.substr(t, std::string::npos));
sb.consume(u);
expect(to_string(sb.data()) == s.substr(t + u, std::string::npos));
sb.consume(v);
expect(to_string(sb.data()) == "");
sb.consume(1);
d = sb.prepare(0); expect(buffer_size(d) == 0);
{
auto d = sb.prepare(0);
expect(buffer_size(d) == 0);
}
}
}}}}}
}

View File

@ -61,43 +61,83 @@ public:
mutable_buffer{&buf[i+j], k}}};
buffers_adapter<decltype(bs)> ba(std::move(bs));
expect(ba.max_size() == sizeof(buf));
decltype(ba)::mutable_buffers_type d;
d = ba.prepare(z); expect(buffer_size(d) == z);
d = ba.prepare(0); expect(buffer_size(d) == 0);
d = ba.prepare(y); expect(buffer_size(d) == y);
d = ba.prepare(x); expect(buffer_size(d) == x);
ba.commit(buffer_copy(d, buffer(s.data(), x)));
{
auto d = ba.prepare(z);
expect(buffer_size(d) == z);
}
{
auto d = ba.prepare(0);
expect(buffer_size(d) == 0);
}
{
auto d = ba.prepare(y);
expect(buffer_size(d) == y);
}
{
auto d = ba.prepare(x);
expect(buffer_size(d) == x);
ba.commit(buffer_copy(d, buffer(s.data(), x)));
}
expect(ba.size() == x);
expect(ba.max_size() == sizeof(buf) - x);
expect(buffer_size(ba.data()) == ba.size());
d = ba.prepare(x); expect(buffer_size(d) == x);
d = ba.prepare(0); expect(buffer_size(d) == 0);
d = ba.prepare(z); expect(buffer_size(d) == z);
d = ba.prepare(y); expect(buffer_size(d) == y);
ba.commit(buffer_copy(d, buffer(s.data()+x, y)));
{
auto d = ba.prepare(x);
expect(buffer_size(d) == x);
}
{
auto d = ba.prepare(0);
expect(buffer_size(d) == 0);
}
{
auto d = ba.prepare(z);
expect(buffer_size(d) == z);
}
{
auto d = ba.prepare(y);
expect(buffer_size(d) == y);
ba.commit(buffer_copy(d, buffer(s.data()+x, y)));
}
ba.commit(1);
expect(ba.size() == x + y);
expect(ba.max_size() == sizeof(buf) - (x + y));
expect(buffer_size(ba.data()) == ba.size());
d = ba.prepare(x); expect(buffer_size(d) == x);
d = ba.prepare(y); expect(buffer_size(d) == y);
d = ba.prepare(0); expect(buffer_size(d) == 0);
d = ba.prepare(z); expect(buffer_size(d) == z);
ba.commit(buffer_copy(d, buffer(s.data()+x+y, z)));
{
auto d = ba.prepare(x);
expect(buffer_size(d) == x);
}
{
auto d = ba.prepare(y);
expect(buffer_size(d) == y);
}
{
auto d = ba.prepare(0);
expect(buffer_size(d) == 0);
}
{
auto d = ba.prepare(z); expect(buffer_size(d) == z);
ba.commit(buffer_copy(d, buffer(s.data()+x+y, z)));
}
ba.commit(2);
expect(ba.size() == x + y + z);
expect(ba.max_size() == 0);
expect(buffer_size(ba.data()) == ba.size());
expect(to_string(ba.data()) == s);
ba.consume(t);
d = ba.prepare(0); expect(buffer_size(d) == 0);
{
auto d = ba.prepare(0);
expect(buffer_size(d) == 0);
}
expect(to_string(ba.data()) == s.substr(t, std::string::npos));
ba.consume(u);
expect(to_string(ba.data()) == s.substr(t + u, std::string::npos));
ba.consume(v);
expect(to_string(ba.data()) == "");
ba.consume(1);
d = ba.prepare(0); expect(buffer_size(d) == 0);
{
auto d = ba.prepare(0);
expect(buffer_size(d) == 0);
}
try
{
ba.prepare(1);

View File

@ -11,7 +11,6 @@
#include "message_fuzz.hpp"
#include <beast/streambuf.hpp>
#include <beast/buffers_debug.hpp>
#include <beast/write_streambuf.hpp>
#include <beast/http/error.hpp>
#include <beast/http/rfc2616.hpp>
@ -183,6 +182,7 @@ public:
void
testCallbacks()
{
using boost::asio::buffer;
{
cb_checker<true> p;
error_code ec;
@ -192,7 +192,7 @@ public:
"Content-Length: 1\r\n"
"\r\n"
"*";
p.write(s.data(), s.size(), ec);
p.write(buffer(s), ec);
if( expect(! ec))
{
expect(p.method);
@ -214,7 +214,7 @@ public:
"Content-Length: 1\r\n"
"\r\n"
"*";
p.write(s.data(), s.size(), ec);
p.write(buffer(s), ec);
if( expect(! ec))
{
expect(p.reason);
@ -235,10 +235,11 @@ public:
void
parse(boost::string_ref const& m, F&& f)
{
using boost::asio::buffer;
{
error_code ec;
Parser p;
p.write(m.data(), m.size(), ec);
p.write(buffer(m.data(), m.size()), ec);
if(expect(p.complete()))
if(expect(! ec, ec.message()))
f(p);
@ -247,7 +248,7 @@ public:
{
error_code ec;
Parser p;
p.write(&m[0], i, ec);
p.write(buffer(&m[0], i), ec);
if(! expect(! ec, ec.message()))
continue;
if(p.complete())
@ -256,7 +257,7 @@ public:
}
else
{
p.write(&m[i], m.size() - i, ec);
p.write(buffer(&m[i], m.size() - i), ec);
if(! expect(! ec, ec.message()))
continue;
expect(p.complete());
@ -271,10 +272,11 @@ public:
void
parse_ev(boost::string_ref const& m, parse_error ev)
{
using boost::asio::buffer;
{
error_code ec;
null_parser<isRequest> p;
p.write(m.data(), m.size(), ec);
p.write(buffer(m.data(), m.size()), ec);
if(expect(! p.complete()))
expect(ec == ev, ec.message());
}
@ -282,7 +284,7 @@ public:
{
error_code ec;
null_parser<isRequest> p;
p.write(&m[0], i, ec);
p.write(buffer(&m[0], i), ec);
if(! expect(! p.complete()))
continue;
if(ec)
@ -290,7 +292,7 @@ public:
expect(ec == ev, ec.message());
continue;
}
p.write(&m[i], m.size() - i, ec);
p.write(buffer(&m[i], m.size() - i), ec);
if(! expect(! p.complete()))
continue;
if(! expect(ec == ev, ec.message()))
@ -449,11 +451,12 @@ public:
void
testUpgrade()
{
using boost::asio::buffer;
null_parser<true> p;
boost::string_ref s =
"GET / HTTP/1.1\r\nConnection: upgrade\r\nUpgrade: WebSocket\r\n\r\n";
error_code ec;
p.write(s.data(), s.size(), ec);
p.write(buffer(s.data(), s.size()), ec);
if(! expect(! ec, ec.message()))
return;
expect(p.complete());
@ -481,6 +484,7 @@ public:
void
testRandomReq(std::size_t N)
{
using boost::asio::buffer;
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
message_fuzz mg;
@ -499,7 +503,7 @@ public:
for(std::size_t j = 1; j < s.size() - 1; ++j)
{
error_code ec;
p.write(&s[0], j, ec);
p.write(buffer(&s[0], j), ec);
if(! expect(! ec, ec.message()))
{
log << escaped_string(s);
@ -507,7 +511,7 @@ public:
}
if(! p.complete())
{
p.write(&s[j], s.size() - j, ec);
p.write(buffer(&s[j], s.size() - j), ec);
if(! expect(! ec, ec.message()))
{
log << escaped_string(s);
@ -528,6 +532,7 @@ public:
void
testRandomResp(std::size_t N)
{
using boost::asio::buffer;
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
message_fuzz mg;
@ -546,7 +551,7 @@ public:
for(std::size_t j = 1; j < s.size() - 1; ++j)
{
error_code ec;
p.write(&s[0], j, ec);
p.write(buffer(&s[0], j), ec);
if(! expect(! ec, ec.message()))
{
log << escaped_string(s);
@ -554,7 +559,7 @@ public:
}
if(! p.complete())
{
p.write(&s[j], s.size() - j, ec);
p.write(buffer(&s[j], s.size() - j), ec);
if(! expect(! ec, ec.message()))
{
log << escaped_string(s);

View File

@ -1,154 +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.
*/
//==============================================================================
#include <beast/streambuf.hpp>
#include <beast/http/chunk_encode.hpp>
#include <beast/detail/unit_test/suite.hpp>
namespace beast {
namespace http {
namespace test {
class chunk_encode_test : public beast::detail::unit_test::suite
{
public:
// Convert CR LF to printables for display
static
std::string
encode (std::string const& s)
{
std::string result;
for(auto const c : s)
{
if (c == '\r')
result += "\\r";
else if (c== '\n')
result += "\\n";
else
result += c;
}
return result;
}
// Print the contents of a ConstBufferSequence to the log
template <class ConstBufferSequence, class Log>
static
void
print (ConstBufferSequence const& buffers, Log log)
{
for(auto const& buf : buffers)
log << encode (std::string(
boost::asio::buffer_cast<char const*>(buf),
boost::asio::buffer_size(buf)));
}
// Convert a ConstBufferSequence to a string
template <class ConstBufferSequence>
static
std::string
buffer_to_string (ConstBufferSequence const& b)
{
std::string s;
auto const n = boost::asio::buffer_size(b);
s.resize(n);
boost::asio::buffer_copy(
boost::asio::buffer(&s[0], n), b);
return s;
}
// Append a ConstBufferSequence to an existing string
template <class ConstBufferSequence>
static
void
buffer_append (std::string& s, ConstBufferSequence const& b)
{
s += buffer_to_string(b);
}
// Convert the input sequence of the stream to a
// chunked-encoded string. The input sequence is consumed.
template <class Streambuf>
static
std::string
streambuf_to_string (Streambuf& sb,
bool final_chunk = false)
{
std::string s;
buffer_append(s, chunk_encode(sb.data(), final_chunk));
return s;
}
// Check an input against the correct chunk encoded version
void
check (std::string const& in, std::string const& answer,
bool final_chunk = true)
{
streambuf sb(3);
sb << in;
auto const out = streambuf_to_string (sb, final_chunk);
if (! expect (out == answer))
log << "expected\n" << encode(answer) <<
"\ngot\n" << encode(out);
}
void testStreambuf()
{
streambuf sb(3);
std::string const s =
"0123456789012345678901234567890123456789012345678901234567890123456789"
"0123456789012345678901234567890123456789012345678901234567890123456789"
"0123456789012345678901234567890123456789012345678901234567890123456789";
sb << s;
expect(buffer_to_string(sb.data()) == s);
}
void
testEncoder()
{
check("", "0\r\n\r\n");
check("x", "1\r\nx\r\n0\r\n\r\n");
check("abcd", "4\r\nabcd\r\n0\r\n\r\n");
check("x", "1\r\nx\r\n", false);
check(
"0123456789012345678901234567890123456789012345678901234567890123456789"
"0123456789012345678901234567890123456789012345678901234567890123456789"
"0123456789012345678901234567890123456789012345678901234567890123456789"
,
"d2\r\n"
"0123456789012345678901234567890123456789012345678901234567890123456789"
"0123456789012345678901234567890123456789012345678901234567890123456789"
"0123456789012345678901234567890123456789012345678901234567890123456789"
"\r\n"
"0\r\n\r\n");
}
void
run()
{
testStreambuf();
testEncoder();
}
};
BEAST_DEFINE_TESTSUITE(chunk_encode,http,beast);
} // test
} // http
} // beast

View File

@ -22,7 +22,6 @@
#include <beast/detail/unit_test/suite.hpp>
#include <beast/detail/unit_test/thread.hpp>
#include <beast/buffers_debug.hpp>
#include <beast/placeholders.hpp>
#include <beast/streambuf.hpp>
#include <beast/http.hpp>
@ -151,8 +150,7 @@ public:
streambuf rb;
{
request<string_body> req(
{beast::http::method_t::http_get, "/", 11});
request<string_body> req({"GET", "/", 11});
req.body = "Beast.HTTP";
req.headers.replace("Host",
ep.address().to_string() + ":" +
@ -176,7 +174,7 @@ public:
void run() override
{
//testAsio();
testAsio();
pass();
}
};

View File

@ -10,7 +10,6 @@
#include "nodejs-parser/http_parser.h"
#include <beast/http/method.hpp>
#include <beast/http/basic_parser.hpp>
#include <beast/http/rfc2616.hpp>
#include <beast/type_check.hpp>
@ -75,62 +74,62 @@ make_nodejs_error(int http_errno)
}
inline
beast::http::method_t
convert_http_method(http_method m)
char const*
method_to_string(unsigned method)
{
using namespace beast;
switch (m)
switch(static_cast<http_method>(method))
{
case HTTP_DELETE: return http::method_t::http_delete;
case HTTP_GET: return http::method_t::http_get;
case HTTP_HEAD: return http::method_t::http_head;
case HTTP_POST: return http::method_t::http_post;
case HTTP_PUT: return http::method_t::http_put;
case HTTP_DELETE: return "DELETE";
case HTTP_GET: return "GET";
case HTTP_HEAD: return "HEAD";
case HTTP_POST: return "POST";
case HTTP_PUT: return "PUT";
// pathological
case HTTP_CONNECT: return http::method_t::http_connect;
case HTTP_OPTIONS: return http::method_t::http_options;
case HTTP_TRACE: return http::method_t::http_trace;
case HTTP_CONNECT: return "CONNECT";
case HTTP_OPTIONS: return "OPTIONS";
case HTTP_TRACE: return "TRACE";
// webdav
case HTTP_COPY: return http::method_t::http_copy;
case HTTP_LOCK: return http::method_t::http_lock;
case HTTP_MKCOL: return http::method_t::http_mkcol;
case HTTP_MOVE: return http::method_t::http_move;
case HTTP_PROPFIND: return http::method_t::http_propfind;
case HTTP_PROPPATCH: return http::method_t::http_proppatch;
case HTTP_SEARCH: return http::method_t::http_search;
case HTTP_UNLOCK: return http::method_t::http_unlock;
case HTTP_BIND: return http::method_t::http_bind;
case HTTP_REBIND: return http::method_t::http_rebind;
case HTTP_UNBIND: return http::method_t::http_unbind;
case HTTP_ACL: return http::method_t::http_acl;
case HTTP_COPY: return "COPY";
case HTTP_LOCK: return "LOCK";
case HTTP_MKCOL: return "MKCOL";
case HTTP_MOVE: return "MOVE";
case HTTP_PROPFIND: return "PROPFIND";
case HTTP_PROPPATCH: return "PROPPATCH";
case HTTP_SEARCH: return "SEARCH";
case HTTP_UNLOCK: return "UNLOCK";
case HTTP_BIND: return "BIND";
case HTTP_REBIND: return "REBIND";
case HTTP_UNBIND: return "UNBIND";
case HTTP_ACL: return "ACL";
// subversion
case HTTP_REPORT: return http::method_t::http_report;
case HTTP_MKACTIVITY: return http::method_t::http_mkactivity;
case HTTP_CHECKOUT: return http::method_t::http_checkout;
case HTTP_MERGE: return http::method_t::http_merge;
case HTTP_REPORT: return "REPORT";
case HTTP_MKACTIVITY: return "MKACTIVITY";
case HTTP_CHECKOUT: return "CHECKOUT";
case HTTP_MERGE: return "MERGE";
// upnp
case HTTP_MSEARCH: return http::method_t::http_msearch;
case HTTP_NOTIFY: return http::method_t::http_notify;
case HTTP_SUBSCRIBE: return http::method_t::http_subscribe;
case HTTP_UNSUBSCRIBE: return http::method_t::http_unsubscribe;
case HTTP_MSEARCH: return "MSEARCH";
case HTTP_NOTIFY: return "NOTIFY";
case HTTP_SUBSCRIBE: return "SUBSCRIBE";
case HTTP_UNSUBSCRIBE: return "UNSUBSCRIBE";
// RFC-5789
case HTTP_PATCH: return http::method_t::http_patch;
case HTTP_PURGE: return http::method_t::http_purge;
case HTTP_PATCH: return "PATCH";
case HTTP_PURGE: return "PURGE";
// CalDav
case HTTP_MKCALENDAR: return http::method_t::http_mkcalendar;
case HTTP_MKCALENDAR: return "MKCALENDAR";
// RFC-2068, section 19.6.1.2
case HTTP_LINK: return http::method_t::http_link;
case HTTP_UNLINK: return http::method_t::http_unlink;
case HTTP_LINK: return "LINK";
case HTTP_UNLINK: return "UNLINK";
};
return http::method_t::http_get;
return "<unknown>";
}
} // detail
@ -308,7 +307,7 @@ private:
{
template<class T, class R =
decltype(std::declval<T>().on_request(
std::declval<method_t>(), std::declval<std::string>(),
std::declval<unsigned>(), std::declval<std::string>(),
std::declval<int>(), std::declval<int>(),
std::declval<bool>(), std::declval<bool>()),
std::true_type{})>
@ -324,7 +323,7 @@ private:
std::integral_constant<bool, has_on_request_t<C>::value>;
void
call_on_request(method_t method, std::string url,
call_on_request(unsigned method, std::string url,
int major, int minor, bool keep_alive, bool upgrade,
std::true_type)
{
@ -333,7 +332,7 @@ private:
}
void
call_on_request(method_t, std::string, int, int, bool, bool,
call_on_request(unsigned, std::string, int, int, bool, bool,
std::false_type)
{
}
@ -687,10 +686,9 @@ nodejs_basic_parser<Derived>::cb_headers_complete(http_parser* p)
http_should_keep_alive(p) != 0;
if(p->type == http_parser_type::HTTP_REQUEST)
{
t.call_on_request(detail::convert_http_method(
http_method(p->method)), t.url_,
p->http_major, p->http_minor, keep_alive,
p->upgrade, has_on_request<Derived>{});
t.call_on_request(p->method, t.url_,
p->http_major, p->http_minor, keep_alive,
p->upgrade, has_on_request<Derived>{});
return 0;
}
return t.call_on_response(p->status_code, t.status_,
@ -807,18 +805,18 @@ private:
}
bool
on_request(http::method_t method, std::string const& url,
on_request(unsigned method, std::string const& url,
int major, int minor, bool keep_alive, bool upgrade,
std::true_type)
{
m_.method = method;
m_.method = detail::method_to_string(method);
m_.url = url;
m_.version = major * 10 + minor;
return true;
}
bool
on_request(http::method_t, std::string const&,
on_request(unsigned, std::string const&,
int, int, bool, bool,
std::false_type)
{
@ -826,7 +824,7 @@ private:
}
bool
on_request(http::method_t method, std::string const& url,
on_request(unsigned method, std::string const& url,
int major, int minor, bool keep_alive, bool upgrade)
{
return on_request(method, url,

View File

@ -20,6 +20,7 @@ class parser_test : public beast::detail::unit_test::suite
public:
void run() override
{
using boost::asio::buffer;
{
error_code ec;
parser<true, string_body,
@ -30,11 +31,11 @@ public:
"Content-Length: 1\r\n"
"\r\n"
"*";
p.write(s.data(), s.size(), ec);
p.write(buffer(s), ec);
expect(! ec);
expect(p.complete());
auto m = p.release();
expect(m.method == method_t::http_get);
expect(m.method == "GET");
expect(m.url == "/");
expect(m.version == 11);
expect(m.headers["User-Agent"] == "test");
@ -50,7 +51,7 @@ public:
"Content-Length: 1\r\n"
"\r\n"
"*";
p.write(s.data(), s.size(), ec);
p.write(buffer(s), ec);
expect(! ec);
expect(p.complete());
auto m = p.release();

View File

@ -9,7 +9,7 @@
#include "message_fuzz.hpp"
#include <beast/http.hpp>
#include <beast/streambuf.hpp>
#include <beast/buffers_debug.hpp>
#include <beast/to_string.hpp>
#include <beast/detail/unit_test/suite.hpp>
#include <chrono>
#include <iostream>
@ -74,8 +74,7 @@ public:
error_code ec;
p.write(sb.data(), ec);
if(! expect(! ec, ec.message()))
log << debug::buffers_to_string(
sb.data()) << std::endl;
log << to_string(sb.data()) << std::endl;
}
}
@ -122,20 +121,20 @@ public:
[&]
{
testParser<nodejs_parser<
true, streambuf_body, http_headers>>(
true, streambuf_body, headers>>(
Repeat, creq_);
testParser<nodejs_parser<
false, streambuf_body, http_headers>>(
false, streambuf_body, headers>>(
Repeat, cres_);
});
timedTest(Trials, "http::basic_parser",
[&]
{
testParser<parser<
true, streambuf_body, http_headers>>(
true, streambuf_body, headers>>(
Repeat, creq_);
testParser<parser<
false, streambuf_body, http_headers>>(
false, streambuf_body, headers>>(
Repeat, cres_);
});
pass();

View File

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

9
test/http/type_check.cpp Normal file
View File

@ -0,0 +1,9 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Test that header file is self-contained.
#include <beast/http/type_check.hpp>

View File

@ -7,3 +7,252 @@
// Test that header file is self-contained.
#include <beast/http/write.hpp>
#include <beast/http/error.hpp>
#include <beast/http/headers.hpp>
#include <beast/http/message.hpp>
#include <beast/http/empty_body.hpp>
#include <beast/http/string_body.hpp>
#include <beast/http/write.hpp>
#include <beast/streambuf.hpp>
#include <beast/to_string.hpp>
#include <beast/detail/unit_test/suite.hpp>
#include <boost/asio/error.hpp>
#include <string>
namespace beast {
namespace http {
class write_test : public beast::detail::unit_test::suite
{
public:
struct string_SyncStream
{
std::string str;
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& ec)
{
auto const n = buffer_size(buffers);
using boost::asio::buffer_size;
using boost::asio::buffer_cast;
str.reserve(str.size() + n);
for(auto const& buffer : buffers)
str.append(buffer_cast<char const*>(buffer),
buffer_size(buffer));
return n;
}
};
struct test_Body
{
using value_type = std::string;
class writer
{
value_type const& body_;
public:
template<bool isRequest, class Allocator>
explicit
writer(message<isRequest, test_Body, Allocator> const& msg)
: body_(msg.body)
{
}
void
init(error_code& ec)
{
}
template<class Write>
boost::tribool
operator()(resume_context&&, error_code&, Write&& write)
{
write(boost::asio::buffer(body_));
return true;
}
};
};
template<bool isRequest, class Body, class Headers>
std::string
str(message<isRequest, Body, Headers> const& m)
{
string_SyncStream ss;
write(ss, m);
return ss.str;
}
void
testWrite()
{
// auto content-length HTTP/1.0
{
message<true, string_body, headers> m{{
"GET", "/", 10}};
m.headers.insert("User-Agent", "test");
m.body = "*";
prepare(m);
expect(str(m) ==
"GET / HTTP/1.0\r\n"
"User-Agent: test\r\n"
"Content-Length: 1\r\n"
"\r\n"
"*"
);
}
// keep-alive HTTP/1.0
{
message<true, string_body, headers> m{{
"GET", "/", 10}};
m.headers.insert("User-Agent", "test");
m.body = "*";
prepare(m, connection::keep_alive);
expect(str(m) ==
"GET / HTTP/1.0\r\n"
"User-Agent: test\r\n"
"Content-Length: 1\r\n"
"Connection: keep-alive\r\n"
"\r\n"
"*"
);
}
// upgrade HTTP/1.0
{
message<true, string_body, headers> m{{
"GET", "/", 10}};
m.headers.insert("User-Agent", "test");
m.body = "*";
try
{
prepare(m, connection::upgrade);
fail();
}
catch(std::exception const&)
{
pass();
}
}
// no content-length HTTP/1.0
{
message<true, test_Body, headers> m{{
"GET", "/", 10}};
m.headers.insert("User-Agent", "test");
m.body = "*";
prepare(m);
string_SyncStream ss;
error_code ec;
write(ss, m, ec);
expect(ec == boost::asio::error::eof);
expect(ss.str ==
"GET / HTTP/1.0\r\n"
"User-Agent: test\r\n"
"\r\n"
"*"
);
}
// auto content-length HTTP/1.1
{
message<true, string_body, headers> m{{
"GET", "/", 11}};
m.headers.insert("User-Agent", "test");
m.body = "*";
prepare(m);
expect(str(m) ==
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"Content-Length: 1\r\n"
"\r\n"
"*"
);
}
// close HTTP/1.1
{
message<true, string_body, headers> m{{
"GET", "/", 11}};
m.headers.insert("User-Agent", "test");
m.body = "*";
prepare(m, connection::close);
string_SyncStream ss;
error_code ec;
write(ss, m, ec);
expect(ec == boost::asio::error::eof);
expect(ss.str ==
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"Content-Length: 1\r\n"
"Connection: close\r\n"
"\r\n"
"*"
);
}
// upgrade HTTP/1.1
{
message<true, empty_body, headers> m{{
"GET", "/", 11}};
m.headers.insert("User-Agent", "test");
prepare(m, connection::upgrade);
expect(str(m) ==
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"Connection: upgrade\r\n"
"\r\n"
);
}
// no content-length HTTP/1.1
{
message<true, test_Body, headers> m{{
"GET", "/", 11}};
m.headers.insert("User-Agent", "test");
m.body = "*";
prepare(m);
string_SyncStream ss;
error_code ec;
write(ss, m, ec);
expect(ss.str ==
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"1\r\n"
"*\r\n"
"0\r\n\r\n"
);
}
}
void testConvert()
{
message<true, string_body, headers> m{{
"GET", "/", 11}};
m.headers.insert("User-Agent", "test");
m.body = "*";
prepare(m);
expect(boost::lexical_cast<std::string>(m) ==
"GET / HTTP/1.1\r\nUser-Agent: test\r\nContent-Length: 1\r\n\r\n*");
}
void run() override
{
testWrite();
testConvert();
}
};
BEAST_DEFINE_TESTSUITE(write,http,beast);
} // http
} // beast

View File

@ -17,26 +17,77 @@
*/
//==============================================================================
#include <beast/detail/unit_test/amount.hpp>
#include <beast/detail/unit_test/global_suites.hpp>
#include <beast/detail/unit_test/match.hpp>
#include <beast/detail/unit_test/reporter.hpp>
#include <beast/detail/unit_test/suite.hpp>
#include <beast/detail/stream/debug_ostream.hpp>
#include <boost/program_options.hpp>
#include <iostream>
#include <vector>
#ifdef _MSC_VER
# ifndef WIN32_LEAN_AND_MEAN // VC_EXTRALEAN
# define WIN32_LEAN_AND_MEAN
#include <windows.h>
# include <windows.h>
# undef WIN32_LEAN_AND_MEAN
# else
#include <windows.h>
# include <windows.h>
# endif
#endif
#include <cstdlib>
namespace beast {
namespace detail {
namespace unit_test {
std::string
prefix(suite_info const& s)
{
if (s.manual())
return "|M| ";
return " ";
}
template<class Log>
void
print(Log& log, suite_list const& c)
{
std::size_t manual = 0;
for(auto const& s : c)
{
log <<
prefix (s) <<
s.full_name();
if(s.manual())
++manual;
}
log <<
amount(c.size(), "suite") << " total, " <<
amount(manual, "manual suite")
;
}
template<class Log>
void
print(Log& log)
{
log << "------------------------------------------";
print(log, global_suites());
log << "------------------------------------------";
}
} // unit_test
} // detail
} // beast
// Simple main used to produce stand
// alone executables that run unit tests.
int main()
int main(int ac, char const* av[])
{
using namespace std;
using namespace beast::detail::unit_test;
#ifdef _MSC_VER
@ -47,10 +98,41 @@ int main()
}
#endif
namespace po = boost::program_options;
po::options_description desc("Options");
desc.add_options()
("help,h", "Produce a help message")
("print,r", "Print the list of available test suites")
("suites,s", po::value<string>(), "suites to run")
;
po::positional_options_description p;
po::variables_map vm;
po::store(po::parse_command_line(ac, av, desc), vm);
po::notify(vm);
beast::detail::debug_ostream log;
if(vm.count("help"))
{
beast::detail::debug_ostream s;
reporter r (s);
bool failed (r.run_each (global_suites()));
log << desc;
}
else if(vm.count("print"))
{
print(log);
}
else
{
std::string suites;
if(vm.count("suites") > 0)
suites = vm["suites"].as<string>();
reporter r(log);
bool failed;
if(! suites.empty())
failed = r.run_each_if(global_suites(),
match_auto(suites));
else
failed = r.run_each(global_suites());
if (failed)
return EXIT_FAILURE;
return EXIT_SUCCESS;

View File

@ -52,40 +52,81 @@ public:
{
std::memset(buf, 0, sizeof(buf));
static_streambuf_n<sizeof(buf)> ba;
decltype(ba)::mutable_buffers_type d;
d = ba.prepare(z); expect(buffer_size(d) == z);
d = ba.prepare(0); expect(buffer_size(d) == 0);
d = ba.prepare(y); expect(buffer_size(d) == y);
d = ba.prepare(x); expect(buffer_size(d) == x);
ba.commit(buffer_copy(d, buffer(s.data(), x)));
{
auto d = ba.prepare(z);
expect(buffer_size(d) == z);
}
{
auto d = ba.prepare(0);
expect(buffer_size(d) == 0);
}
{
auto d = ba.prepare(y);
expect(buffer_size(d) == y);
}
{
auto d = ba.prepare(x);
expect(buffer_size(d) == x);
ba.commit(buffer_copy(d, buffer(s.data(), x)));
}
expect(ba.size() == x);
expect(buffer_size(ba.data()) == ba.size());
d = ba.prepare(x); expect(buffer_size(d) == x);
d = ba.prepare(0); expect(buffer_size(d) == 0);
d = ba.prepare(z); expect(buffer_size(d) == z);
d = ba.prepare(y); expect(buffer_size(d) == y);
ba.commit(buffer_copy(d, buffer(s.data()+x, y)));
{
auto d = ba.prepare(x);
expect(buffer_size(d) == x);
}
{
auto d = ba.prepare(0);
expect(buffer_size(d) == 0);
}
{
auto d = ba.prepare(z);
expect(buffer_size(d) == z);
}
{
auto d = ba.prepare(y);
expect(buffer_size(d) == y);
ba.commit(buffer_copy(d, buffer(s.data()+x, y)));
}
ba.commit(1);
expect(ba.size() == x + y);
expect(buffer_size(ba.data()) == ba.size());
d = ba.prepare(x); expect(buffer_size(d) == x);
d = ba.prepare(y); expect(buffer_size(d) == y);
d = ba.prepare(0); expect(buffer_size(d) == 0);
d = ba.prepare(z); expect(buffer_size(d) == z);
ba.commit(buffer_copy(d, buffer(s.data()+x+y, z)));
{
auto d = ba.prepare(x);
expect(buffer_size(d) == x);
}
{
auto d = ba.prepare(y);
expect(buffer_size(d) == y);
}
{
auto d = ba.prepare(0);
expect(buffer_size(d) == 0);
}
{
auto d = ba.prepare(z);
expect(buffer_size(d) == z);
ba.commit(buffer_copy(d, buffer(s.data()+x+y, z)));
}
ba.commit(2);
expect(ba.size() == x + y + z);
expect(buffer_size(ba.data()) == ba.size());
expect(to_string(ba.data()) == s);
ba.consume(t);
d = ba.prepare(0); expect(buffer_size(d) == 0);
{
auto d = ba.prepare(0);
expect(buffer_size(d) == 0);
}
expect(to_string(ba.data()) == s.substr(t, std::string::npos));
ba.consume(u);
expect(to_string(ba.data()) == s.substr(t + u, std::string::npos));
ba.consume(v);
expect(to_string(ba.data()) == "");
ba.consume(1);
d = ba.prepare(0); expect(buffer_size(d) == 0);
{
auto d = ba.prepare(0);
expect(buffer_size(d) == 0);
}
try
{
ba.prepare(1);

View File

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