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 f3c3e0bfff
commit 47dc31d8c2
69 changed files with 1315 additions and 953 deletions

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>