HTTP improvements:

* Increased code coverage

* New Body concepts:
    is_Body
    is_ReadableBody
    is_WritableBody
This commit is contained in:
Vinnie Falco
2016-05-18 12:30:15 -04:00
parent 039244cda4
commit 19256b85f0
16 changed files with 206 additions and 84 deletions

View File

@@ -41,7 +41,10 @@
</simplelist> </simplelist>
<bridgehead renderas="sect3">Type Traits</bridgehead> <bridgehead renderas="sect3">Type Traits</bridgehead>
<simplelist type="vert" columns="1"> <simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__is_Body">is_Body</link></member>
<member><link linkend="beast.ref.http__is_Parser">is_Parser</link></member> <member><link linkend="beast.ref.http__is_Parser">is_Parser</link></member>
<member><link linkend="beast.ref.http__is_ReadableBody">is_ReadableBody</link></member>
<member><link linkend="beast.ref.http__is_WritableBody">is_WritableBody</link></member>
</simplelist> </simplelist>
</entry> </entry>
<entry valign="top"> <entry valign="top">

View File

@@ -22,7 +22,7 @@ In this table:
[`boost::string_ref`] [`boost::string_ref`]
[ [
This function returns a value implicitly convertible to This function returns a value implicitly convertible to
`boost::string_ref` containing the case-insensitve field `boost::string_ref` containing the case-insensitive field
name, without leading or trailing white space. name, without leading or trailing white space.
] ]
] ]

View File

@@ -38,7 +38,7 @@ In this table:
an error occurs, the end of the buffer is reached, or a complete an error occurs, the end of the buffer is reached, or a complete
HTTP/1 message has been parsed. If an error occurs, `ec` is set HTTP/1 message has been parsed. If an error occurs, `ec` is set
to the error code and parsing stops. This function returns the to the error code and parsing stops. This function returns the
number of bytes consumed in the input buffer. number of bytes consumed from the input buffer.
] ]
] ]
[ [

View File

@@ -0,0 +1,156 @@
//
// 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/core/error.hpp>
#include <boost/asio/buffer.hpp>
#include <type_traits>
#include <utility>
namespace beast {
namespace http {
namespace detail {
template<class T>
class has_value_type
{
template<class U, class R =
typename U::value_type>
static std::true_type check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<T>(0));
public:
static bool constexpr value = type::value;
};
template<class T, bool B = has_value_type<T>::value>
struct extract_value_type
{
using type = void;
};
template<class T>
struct extract_value_type<T, true>
{
using type = typename T::value_type;
};
template<class T>
class has_reader
{
template<class U, class R =
typename U::reader>
static std::true_type check(int);
template<class>
static std::false_type check(...);
public:
using type = decltype(check<T>(0));
};
template<class T>
class has_writer
{
template<class U, class R =
typename U::writer>
static std::true_type check(int);
template<class>
static std::false_type check(...);
public:
using type = decltype(check<T>(0));
};
template<class T>
struct is_Body
{
using type = std::integral_constant<bool,
has_value_type<T>::value &&
std::is_default_constructible<
typename extract_value_type<T>::type>::value
>;
};
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 = decltype(
std::declval<U>().write_eof(std::declval<error_code&>()),
std::true_type{})>
static R check3(int);
template<class>
static std::false_type check3(...);
using type3 = decltype(check3<T>(0));
public:
using type = std::integral_constant<bool,
type1::value &&
type2::value &&
type3::value
>;
};
} // detail
/// Determine if `T` meets the requirements of `Body`.
template<class T>
#if GENERATING_DOCS
struct is_Body : std::integral_constant<bool, ...>{};
#else
using is_Body = typename detail::is_Body<T>::type;
#endif
/// Determine if `T` meets the requirements of `ReadableBody`.
template<class T>
#if GENERATING_DOCS
struct is_ReadableBody : std::integral_constant<bool, ...>{};
#else
using is_ReadableBody = typename detail::has_reader<T>::type;
#endif
/// Determine if `T` meets the requirements of `WritableBody`.
template<class T>
#if GENERATING_DOCS
struct is_WritableBody : std::integral_constant<bool, ...>{};
#else
using is_WritableBody = typename detail::has_writer<T>::type;
#endif
/// Determine if `T` meets the requirements of `Parser`.
template<class T>
#if GENERATING_DOCS
struct is_Parser : std::integral_constant<bool, ...>{};
#else
using is_Parser = typename detail::is_Parser<T>::type;
#endif
} // http
} // beast
#endif

View File

@@ -34,20 +34,6 @@ struct empty_body
private: private:
#endif #endif
struct reader
{
template<bool isRequest, class Headers>
explicit
reader(message<isRequest, empty_body, Headers>&)
{
}
void
write(void const*, std::size_t, error_code&)
{
}
};
struct writer struct writer
{ {
writer(writer const&) = delete; writer(writer const&) = delete;

View File

@@ -8,8 +8,8 @@
#ifndef BEAST_HTTP_IMPL_READ_IPP_HPP #ifndef BEAST_HTTP_IMPL_READ_IPP_HPP
#define BEAST_HTTP_IMPL_READ_IPP_HPP #define BEAST_HTTP_IMPL_READ_IPP_HPP
#include <beast/http/concepts.hpp>
#include <beast/http/parser_v1.hpp> #include <beast/http/parser_v1.hpp>
#include <beast/http/type_check.hpp>
#include <beast/core/bind_handler.hpp> #include <beast/core/bind_handler.hpp>
#include <beast/core/handler_alloc.hpp> #include <beast/core/handler_alloc.hpp>
#include <beast/core/stream_concepts.hpp> #include <beast/core/stream_concepts.hpp>
@@ -323,6 +323,12 @@ void
parse(SyncReadStream& stream, parse(SyncReadStream& stream,
Streambuf& streambuf, Parser& parser) Streambuf& streambuf, Parser& parser)
{ {
static_assert(is_SyncReadStream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
static_assert(is_Parser<Parser>::value,
"Parser requirements not met");
error_code ec; error_code ec;
parse(stream, streambuf, parser, ec); parse(stream, streambuf, parser, ec);
if(ec) if(ec)
@@ -403,6 +409,8 @@ read(SyncReadStream& stream, Streambuf& streambuf,
"SyncReadStream requirements not met"); "SyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value, static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met"); "Streambuf requirements not met");
static_assert(is_ReadableBody<Body>::value,
"ReadableBody requirements not met");
error_code ec; error_code ec;
read(stream, streambuf, msg, ec); read(stream, streambuf, msg, ec);
if(ec) if(ec)
@@ -420,6 +428,8 @@ read(SyncReadStream& stream, Streambuf& streambuf,
"SyncReadStream requirements not met"); "SyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value, static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met"); "Streambuf requirements not met");
static_assert(is_ReadableBody<Body>::value,
"ReadableBody requirements not met");
parser_v1<isRequest, Body, Headers> p; parser_v1<isRequest, Body, Headers> p;
parse(stream, streambuf, p, ec); parse(stream, streambuf, p, ec);
if(ec) if(ec)
@@ -441,6 +451,8 @@ async_read(AsyncReadStream& stream, Streambuf& streambuf,
"AsyncReadStream requirements not met"); "AsyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value, static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met"); "Streambuf requirements not met");
static_assert(is_ReadableBody<Body>::value,
"ReadableBody requirements not met");
beast::async_completion<ReadHandler, beast::async_completion<ReadHandler,
void(error_code)> completion(handler); void(error_code)> completion(handler);
detail::read_op<AsyncReadStream, Streambuf, detail::read_op<AsyncReadStream, Streambuf,

View File

@@ -8,6 +8,7 @@
#ifndef BEAST_HTTP_IMPL_WRITE_IPP #ifndef BEAST_HTTP_IMPL_WRITE_IPP
#define BEAST_HTTP_IMPL_WRITE_IPP #define BEAST_HTTP_IMPL_WRITE_IPP
#include <beast/http/concepts.hpp>
#include <beast/http/resume_context.hpp> #include <beast/http/resume_context.hpp>
#include <beast/http/detail/chunk_encode.hpp> #include <beast/http/detail/chunk_encode.hpp>
#include <beast/http/detail/has_content_length.hpp> #include <beast/http/detail/has_content_length.hpp>
@@ -448,6 +449,8 @@ write(SyncWriteStream& stream,
{ {
static_assert(is_SyncWriteStream<SyncWriteStream>::value, static_assert(is_SyncWriteStream<SyncWriteStream>::value,
"SyncWriteStream requirements not met"); "SyncWriteStream requirements not met");
static_assert(is_WritableBody<Body>::value,
"WritableBody requirements not met");
error_code ec; error_code ec;
write(stream, msg, ec); write(stream, msg, ec);
if(ec) if(ec)
@@ -463,6 +466,8 @@ write(SyncWriteStream& stream,
{ {
static_assert(is_SyncWriteStream<SyncWriteStream>::value, static_assert(is_SyncWriteStream<SyncWriteStream>::value,
"SyncWriteStream requirements not met"); "SyncWriteStream requirements not met");
static_assert(is_WritableBody<Body>::value,
"WritableBody requirements not met");
detail::write_preparation<isRequest, Body, Headers> wp(msg); detail::write_preparation<isRequest, Body, Headers> wp(msg);
wp.init(ec); wp.init(ec);
if(ec) if(ec)
@@ -546,6 +551,8 @@ async_write(AsyncWriteStream& stream,
static_assert( static_assert(
is_AsyncWriteStream<AsyncWriteStream>::value, is_AsyncWriteStream<AsyncWriteStream>::value,
"AsyncWriteStream requirements not met"); "AsyncWriteStream requirements not met");
static_assert(is_WritableBody<Body>::value,
"WritableBody requirements not met");
beast::async_completion<WriteHandler, beast::async_completion<WriteHandler,
void(error_code)> completion(handler); void(error_code)> completion(handler);
detail::write_op<AsyncWriteStream, decltype(completion.handler), detail::write_op<AsyncWriteStream, decltype(completion.handler),
@@ -607,6 +614,8 @@ std::ostream&
operator<<(std::ostream& os, operator<<(std::ostream& os,
message_v1<isRequest, Body, Headers> const& msg) message_v1<isRequest, Body, Headers> const& msg)
{ {
static_assert(is_WritableBody<Body>::value,
"WritableBody requirements not met");
detail::ostream_SyncStream oss(os); detail::ostream_SyncStream oss(os);
error_code ec; error_code ec;
write(oss, msg, ec); write(oss, msg, ec);

View File

@@ -9,6 +9,7 @@
#define BEAST_HTTP_PARSER_V1_HPP #define BEAST_HTTP_PARSER_V1_HPP
#include <beast/http/basic_parser_v1.hpp> #include <beast/http/basic_parser_v1.hpp>
#include <beast/http/concepts.hpp>
#include <beast/http/message_v1.hpp> #include <beast/http/message_v1.hpp>
#include <beast/core/error.hpp> #include <beast/core/error.hpp>
#include <functional> #include <functional>
@@ -54,6 +55,9 @@ public:
message_v1<isRequest, Body, Headers>; message_v1<isRequest, Body, Headers>;
private: private:
static_assert(is_ReadableBody<Body>::value,
"ReadableBody requirements not met");
std::string field_; std::string field_;
std::string value_; std::string value_;
message_type m_; message_type m_;

View File

@@ -1,60 +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_TYPE_CHECK_HPP
#define BEAST_HTTP_TYPE_CHECK_HPP
#include <beast/core/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 = decltype(
std::declval<U>().write_eof(std::declval<error_code&>()),
std::true_type{})>
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

@@ -33,7 +33,7 @@ class stream<NextLayer>::accept_op
struct data struct data
{ {
stream<NextLayer>& ws; stream<NextLayer>& ws;
http::request_v1<http::empty_body> req; http::request_v1<http::string_body> req;
Handler h; Handler h;
bool cont; bool cont;
int state = 0; int state = 0;

View File

@@ -253,7 +253,7 @@ accept(ConstBufferSequence const& buffers, error_code& ec)
stream_.buffer().commit(buffer_copy( stream_.buffer().commit(buffer_copy(
stream_.buffer().prepare( stream_.buffer().prepare(
buffer_size(buffers)), buffers)); buffer_size(buffers)), buffers));
http::request_v1<http::empty_body> m; http::request_v1<http::string_body> m;
http::read(next_layer(), stream_.buffer(), m, ec); http::read(next_layer(), stream_.buffer(), m, ec);
if(ec) if(ec)
return; return;

View File

@@ -44,6 +44,7 @@ unit-test http-tests :
http/basic_headers.cpp http/basic_headers.cpp
http/basic_parser_v1.cpp http/basic_parser_v1.cpp
http/body_type.cpp http/body_type.cpp
http/concepts.cpp
http/empty_body.cpp http/empty_body.cpp
http/headers.cpp http/headers.cpp
http/message.cpp http/message.cpp
@@ -58,7 +59,6 @@ unit-test http-tests :
http/status.cpp http/status.cpp
http/streambuf_body.cpp http/streambuf_body.cpp
http/string_body.cpp http/string_body.cpp
http/type_check.cpp
http/write.cpp http/write.cpp
http/detail/chunk_encode.cpp http/detail/chunk_encode.cpp
; ;

View File

@@ -66,6 +66,14 @@ public:
expect(buffer_size(buffer_cat( expect(buffer_size(buffer_cat(
sb1.data(), sb2.data())) == 12); sb1.data(), sb2.data())) == 12);
} }
for(auto it = bs.begin(); it != bs.end(); ++it)
{
decltype(bs)::const_iterator copy;
copy = it;
expect(copy == it);
copy = copy;
expect(copy == it);
}
} }
void testIterators() void testIterators()

View File

@@ -10,6 +10,7 @@ add_executable (http-tests
basic_headers.cpp basic_headers.cpp
basic_parser_v1.cpp basic_parser_v1.cpp
body_type.cpp body_type.cpp
concepts.cpp
empty_body.cpp empty_body.cpp
headers.cpp headers.cpp
message.cpp message.cpp
@@ -24,7 +25,6 @@ add_executable (http-tests
status.cpp status.cpp
streambuf_body.cpp streambuf_body.cpp
string_body.cpp string_body.cpp
type_check.cpp
write.cpp write.cpp
detail/chunk_encode.cpp detail/chunk_encode.cpp
) )

View File

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

View File

@@ -12,8 +12,10 @@
#include <beast/http/headers.hpp> #include <beast/http/headers.hpp>
#include <beast/http/parser_v1.hpp> #include <beast/http/parser_v1.hpp>
#include <beast/http/read.hpp> #include <beast/http/read.hpp>
#include <beast/http/write.hpp>
#include <beast/test/string_stream.hpp> #include <beast/test/string_stream.hpp>
#include <beast/unit_test/suite.hpp> #include <beast/unit_test/suite.hpp>
#include <boost/lexical_cast.hpp>
namespace beast { namespace beast {
namespace http { namespace http {
@@ -25,16 +27,18 @@ class streambuf_body_test : public beast::unit_test::suite
public: public:
void run() override void run() override
{ {
test::string_stream ss(ios_, std::string const s =
"HTTP/1.1 200 OK\r\n" "HTTP/1.1 200 OK\r\n"
"Server: test\r\n" "Server: test\r\n"
"Content-Length: 3\r\n" "Content-Length: 3\r\n"
"\r\n" "\r\n"
"xyz"); "xyz";
test::string_stream ss(ios_, s);
parser_v1<false, streambuf_body, headers> p; parser_v1<false, streambuf_body, headers> p;
streambuf sb; streambuf sb;
parse(ss, sb, p); parse(ss, sb, p);
expect(to_string(p.get().body.data()) == "xyz"); expect(to_string(p.get().body.data()) == "xyz");
expect(boost::lexical_cast<std::string>(p.get()) == s);
} }
}; };