Move everything in basic_fields.hpp to fields.hpp (API Change)

fix #320
This commit is contained in:
Vinnie Falco
2017-04-27 17:38:50 -07:00
parent 4b19600dd2
commit 28a994e8cb
10 changed files with 379 additions and 411 deletions

View File

@ -13,6 +13,7 @@ API Changes:
* Provide websocket::stream accept() overloads * Provide websocket::stream accept() overloads
* Refactor websocket decorators * Refactor websocket decorators
* Move everything in basic_fields.hpp to fields.hpp
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View File

@ -10,7 +10,6 @@
#include <beast/config.hpp> #include <beast/config.hpp>
#include <beast/http/basic_fields.hpp>
#include <beast/http/basic_parser.hpp> #include <beast/http/basic_parser.hpp>
#include <beast/http/chunk_encode.hpp> #include <beast/http/chunk_encode.hpp>
#include <beast/http/error.hpp> #include <beast/http/error.hpp>

View File

@ -1,307 +0,0 @@
//
// Copyright (c) 2013-2017 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_BASIC_FIELDS_HPP
#define BEAST_HTTP_BASIC_FIELDS_HPP
#include <beast/config.hpp>
#include <beast/core/detail/empty_base_optimization.hpp>
#include <beast/http/detail/basic_fields.hpp>
#include <boost/lexical_cast.hpp>
#include <algorithm>
#include <cctype>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
namespace beast {
namespace http {
/** A container for storing HTTP header fields.
This container is designed to store the field value pairs that make
up the fields and trailers in a HTTP message. Objects of this type
are iterable, with each element holding the field name and field
value.
Field names are stored as-is, but comparisons are case-insensitive.
When the container is iterated, the fields are presented in the order
of insertion. For fields with the same name, the container behaves
as a `std::multiset`; there will be a separate value for each occurrence
of the field name.
@note Meets the requirements of @b FieldSequence.
*/
template<class Allocator>
class basic_fields :
#if ! BEAST_DOXYGEN
private beast::detail::empty_base_optimization<
typename std::allocator_traits<Allocator>::
template rebind_alloc<
detail::basic_fields_base::element>>,
#endif
public detail::basic_fields_base
{
using alloc_type = typename
std::allocator_traits<Allocator>::
template rebind_alloc<
detail::basic_fields_base::element>;
using alloc_traits =
std::allocator_traits<alloc_type>;
using size_type =
typename std::allocator_traits<Allocator>::size_type;
void
delete_all();
void
move_assign(basic_fields&, std::false_type);
void
move_assign(basic_fields&, std::true_type);
void
copy_assign(basic_fields const&, std::false_type);
void
copy_assign(basic_fields const&, std::true_type);
template<class FieldSequence>
void
copy_from(FieldSequence const& fs)
{
for(auto const& e : fs)
insert(e.first, e.second);
}
public:
/// The type of allocator used.
using allocator_type = Allocator;
/** The value type of the field sequence.
Meets the requirements of @b Field.
*/
#if BEAST_DOXYGEN
using value_type = implementation_defined;
#endif
/// A const iterator to the field sequence
#if BEAST_DOXYGEN
using iterator = implementation_defined;
#endif
/// A const iterator to the field sequence
#if BEAST_DOXYGEN
using const_iterator = implementation_defined;
#endif
/// Default constructor.
basic_fields() = default;
/// Destructor
~basic_fields();
/** Construct the fields.
@param alloc The allocator to use.
*/
explicit
basic_fields(Allocator const& alloc);
/** Move constructor.
The moved-from object becomes an empty field sequence.
@param other The object to move from.
*/
basic_fields(basic_fields&& other);
/** Move assignment.
The moved-from object becomes an empty field sequence.
@param other The object to move from.
*/
basic_fields& operator=(basic_fields&& other);
/// Copy constructor.
basic_fields(basic_fields const&);
/// Copy assignment.
basic_fields& operator=(basic_fields const&);
/// Copy constructor.
template<class OtherAlloc>
basic_fields(basic_fields<OtherAlloc> const&);
/// Copy assignment.
template<class OtherAlloc>
basic_fields& operator=(basic_fields<OtherAlloc> const&);
/// Construct from a field sequence.
template<class FwdIt>
basic_fields(FwdIt first, FwdIt last);
/// Returns `true` if the field sequence contains no elements.
bool
empty() const
{
return set_.empty();
}
/// Returns the number of elements in the field sequence.
std::size_t
size() const
{
return set_.size();
}
/// Returns a const iterator to the beginning of the field sequence.
const_iterator
begin() const
{
return list_.cbegin();
}
/// Returns a const iterator to the end of the field sequence.
const_iterator
end() const
{
return list_.cend();
}
/// Returns a const iterator to the beginning of the field sequence.
const_iterator
cbegin() const
{
return list_.cbegin();
}
/// Returns a const iterator to the end of the field sequence.
const_iterator
cend() const
{
return list_.cend();
}
/// Returns `true` if the specified field exists.
bool
exists(boost::string_ref const& name) const
{
return set_.find(name, less{}) != set_.end();
}
/// Returns the number of values for the specified field.
std::size_t
count(boost::string_ref const& name) const;
/** Returns an iterator to the case-insensitive matching field name.
If more than one field with the specified name exists, the
first field defined by insertion order is returned.
*/
iterator
find(boost::string_ref const& name) const;
/** Returns the value for a case-insensitive matching header, or `""`.
If more than one field with the specified name exists, the
first field defined by insertion order is returned.
*/
boost::string_ref
operator[](boost::string_ref const& name) const;
/// Clear the contents of the basic_fields.
void
clear() noexcept;
/** Remove a field.
If more than one field with the specified name exists, all
matching fields will be removed.
@param name The name of the field(s) to remove.
@return The number of fields removed.
*/
std::size_t
erase(boost::string_ref const& name);
/** Insert a field value.
If a field with the same name already exists, the
existing field is untouched and a new field value pair
is inserted into the container.
@param name The name of the field.
@param value A string holding the value of the field.
*/
void
insert(boost::string_ref const& name, boost::string_ref value);
/** Insert a field value.
If a field with the same name already exists, the
existing field is untouched and a new field value pair
is inserted into the container.
@param name The name of the field
@param value The value of the field. The object will be
converted to a string using `boost::lexical_cast`.
*/
template<class T>
typename std::enable_if<
! std::is_constructible<boost::string_ref, T>::value>::type
insert(boost::string_ref name, T const& value)
{
insert(name, boost::lexical_cast<std::string>(value));
}
/** Replace a field value.
First removes any values with matching field names, then
inserts the new field value.
@param name The name of the field.
@param value A string holding the value of the field.
*/
void
replace(boost::string_ref const& name, boost::string_ref value);
/** Replace a field value.
First removes any values with matching field names, then
inserts the new field value.
@param name The name of the field
@param value The value of the field. The object will be
converted to a string using `boost::lexical_cast`.
*/
template<class T>
typename std::enable_if<
! std::is_constructible<boost::string_ref, T>::value>::type
replace(boost::string_ref const& name, T const& value)
{
replace(name,
boost::lexical_cast<std::string>(value));
}
};
} // http
} // beast
#include <beast/http/impl/basic_fields.ipp>
#endif

View File

@ -5,8 +5,8 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// //
#ifndef BEAST_HTTP_DETAIL_BASIC_FIELDS_HPP #ifndef BEAST_HTTP_DETAIL_FIELDS_HPP
#define BEAST_HTTP_DETAIL_BASIC_FIELDS_HPP #define BEAST_HTTP_DETAIL_FIELDS_HPP
#include <beast/core/detail/ci_char_traits.hpp> #include <beast/core/detail/ci_char_traits.hpp>
#include <boost/intrusive/list.hpp> #include <boost/intrusive/list.hpp>

View File

@ -9,12 +9,296 @@
#define BEAST_HTTP_FIELDS_HPP #define BEAST_HTTP_FIELDS_HPP
#include <beast/config.hpp> #include <beast/config.hpp>
#include <beast/http/basic_fields.hpp> #include <beast/core/detail/empty_base_optimization.hpp>
#include <beast/http/detail/fields.hpp>
#include <boost/lexical_cast.hpp>
#include <algorithm>
#include <cctype>
#include <memory> #include <memory>
#include <string>
#include <type_traits>
#include <utility>
namespace beast { namespace beast {
namespace http { namespace http {
/** A container for storing HTTP header fields.
This container is designed to store the field value pairs that make
up the fields and trailers in a HTTP message. Objects of this type
are iterable, with each element holding the field name and field
value.
Field names are stored as-is, but comparisons are case-insensitive.
When the container is iterated, the fields are presented in the order
of insertion. For fields with the same name, the container behaves
as a `std::multiset`; there will be a separate value for each occurrence
of the field name.
@note Meets the requirements of @b FieldSequence.
*/
template<class Allocator>
class basic_fields :
#if ! BEAST_DOXYGEN
private beast::detail::empty_base_optimization<
typename std::allocator_traits<Allocator>::
template rebind_alloc<
detail::basic_fields_base::element>>,
#endif
public detail::basic_fields_base
{
using alloc_type = typename
std::allocator_traits<Allocator>::
template rebind_alloc<
detail::basic_fields_base::element>;
using alloc_traits =
std::allocator_traits<alloc_type>;
using size_type =
typename std::allocator_traits<Allocator>::size_type;
void
delete_all();
void
move_assign(basic_fields&, std::false_type);
void
move_assign(basic_fields&, std::true_type);
void
copy_assign(basic_fields const&, std::false_type);
void
copy_assign(basic_fields const&, std::true_type);
template<class FieldSequence>
void
copy_from(FieldSequence const& fs)
{
for(auto const& e : fs)
insert(e.first, e.second);
}
public:
/// The type of allocator used.
using allocator_type = Allocator;
/** The value type of the field sequence.
Meets the requirements of @b Field.
*/
#if BEAST_DOXYGEN
using value_type = implementation_defined;
#endif
/// A const iterator to the field sequence
#if BEAST_DOXYGEN
using iterator = implementation_defined;
#endif
/// A const iterator to the field sequence
#if BEAST_DOXYGEN
using const_iterator = implementation_defined;
#endif
/// Default constructor.
basic_fields() = default;
/// Destructor
~basic_fields();
/** Construct the fields.
@param alloc The allocator to use.
*/
explicit
basic_fields(Allocator const& alloc);
/** Move constructor.
The moved-from object becomes an empty field sequence.
@param other The object to move from.
*/
basic_fields(basic_fields&& other);
/** Move assignment.
The moved-from object becomes an empty field sequence.
@param other The object to move from.
*/
basic_fields& operator=(basic_fields&& other);
/// Copy constructor.
basic_fields(basic_fields const&);
/// Copy assignment.
basic_fields& operator=(basic_fields const&);
/// Copy constructor.
template<class OtherAlloc>
basic_fields(basic_fields<OtherAlloc> const&);
/// Copy assignment.
template<class OtherAlloc>
basic_fields& operator=(basic_fields<OtherAlloc> const&);
/// Construct from a field sequence.
template<class FwdIt>
basic_fields(FwdIt first, FwdIt last);
/// Returns `true` if the field sequence contains no elements.
bool
empty() const
{
return set_.empty();
}
/// Returns the number of elements in the field sequence.
std::size_t
size() const
{
return set_.size();
}
/// Returns a const iterator to the beginning of the field sequence.
const_iterator
begin() const
{
return list_.cbegin();
}
/// Returns a const iterator to the end of the field sequence.
const_iterator
end() const
{
return list_.cend();
}
/// Returns a const iterator to the beginning of the field sequence.
const_iterator
cbegin() const
{
return list_.cbegin();
}
/// Returns a const iterator to the end of the field sequence.
const_iterator
cend() const
{
return list_.cend();
}
/// Returns `true` if the specified field exists.
bool
exists(boost::string_ref const& name) const
{
return set_.find(name, less{}) != set_.end();
}
/// Returns the number of values for the specified field.
std::size_t
count(boost::string_ref const& name) const;
/** Returns an iterator to the case-insensitive matching field name.
If more than one field with the specified name exists, the
first field defined by insertion order is returned.
*/
iterator
find(boost::string_ref const& name) const;
/** Returns the value for a case-insensitive matching header, or `""`.
If more than one field with the specified name exists, the
first field defined by insertion order is returned.
*/
boost::string_ref
operator[](boost::string_ref const& name) const;
/// Clear the contents of the basic_fields.
void
clear() noexcept;
/** Remove a field.
If more than one field with the specified name exists, all
matching fields will be removed.
@param name The name of the field(s) to remove.
@return The number of fields removed.
*/
std::size_t
erase(boost::string_ref const& name);
/** Insert a field value.
If a field with the same name already exists, the
existing field is untouched and a new field value pair
is inserted into the container.
@param name The name of the field.
@param value A string holding the value of the field.
*/
void
insert(boost::string_ref const& name, boost::string_ref value);
/** Insert a field value.
If a field with the same name already exists, the
existing field is untouched and a new field value pair
is inserted into the container.
@param name The name of the field
@param value The value of the field. The object will be
converted to a string using `boost::lexical_cast`.
*/
template<class T>
typename std::enable_if<
! std::is_constructible<boost::string_ref, T>::value>::type
insert(boost::string_ref name, T const& value)
{
insert(name, boost::lexical_cast<std::string>(value));
}
/** Replace a field value.
First removes any values with matching field names, then
inserts the new field value.
@param name The name of the field.
@param value A string holding the value of the field.
*/
void
replace(boost::string_ref const& name, boost::string_ref value);
/** Replace a field value.
First removes any values with matching field names, then
inserts the new field value.
@param name The name of the field
@param value The value of the field. The object will be
converted to a string using `boost::lexical_cast`.
*/
template<class T>
typename std::enable_if<
! std::is_constructible<boost::string_ref, T>::value>::type
replace(boost::string_ref const& name, T const& value)
{
replace(name,
boost::lexical_cast<std::string>(value));
}
};
/// A typical HTTP header fields container /// A typical HTTP header fields container
using fields = using fields =
basic_fields<std::allocator<char>>; basic_fields<std::allocator<char>>;
@ -22,4 +306,6 @@ using fields =
} // http } // http
} // beast } // beast
#include <beast/http/impl/fields.ipp>
#endif #endif

View File

@ -5,8 +5,8 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// //
#ifndef BEAST_HTTP_IMPL_BASIC_FIELDS_IPP #ifndef BEAST_HTTP_IMPL_FIELDS_IPP
#define BEAST_HTTP_IMPL_BASIC_FIELDS_IPP #define BEAST_HTTP_IMPL_FIELDS_IPP
#include <beast/http/detail/rfc7230.hpp> #include <beast/http/detail/rfc7230.hpp>
#include <algorithm> #include <algorithm>

View File

@ -48,7 +48,6 @@ unit-test core-tests :
unit-test http-tests : unit-test http-tests :
../extras/beast/unit_test/main.cpp ../extras/beast/unit_test/main.cpp
http/basic_dynabuf_body.cpp http/basic_dynabuf_body.cpp
http/basic_fields.cpp
http/basic_parser.cpp http/basic_parser.cpp
http/concepts.cpp http/concepts.cpp
http/design.cpp http/design.cpp

View File

@ -11,7 +11,6 @@ add_executable (http-tests
test_parser.hpp test_parser.hpp
../../extras/beast/unit_test/main.cpp ../../extras/beast/unit_test/main.cpp
basic_dynabuf_body.cpp basic_dynabuf_body.cpp
basic_fields.cpp
basic_parser.cpp basic_parser.cpp
concepts.cpp concepts.cpp
design.cpp design.cpp

View File

@ -1,96 +0,0 @@
//
// Copyright (c) 2013-2017 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/basic_fields.hpp>
#include <beast/unit_test/suite.hpp>
#include <boost/lexical_cast.hpp>
namespace beast {
namespace http {
class basic_fields_test : public beast::unit_test::suite
{
public:
template<class Allocator>
using bha = basic_fields<Allocator>;
using bh = basic_fields<std::allocator<char>>;
template<class Allocator>
static
void
fill(std::size_t n, basic_fields<Allocator>& h)
{
for(std::size_t i = 1; i<= n; ++i)
h.insert(boost::lexical_cast<std::string>(i), i);
}
template<class U, class V>
static
void
self_assign(U& u, V&& v)
{
u = std::forward<V>(v);
}
void testHeaders()
{
bh h1;
BEAST_EXPECT(h1.empty());
fill(1, h1);
BEAST_EXPECT(h1.size() == 1);
bh h2;
h2 = h1;
BEAST_EXPECT(h2.size() == 1);
h2.insert("2", "2");
BEAST_EXPECT(std::distance(h2.begin(), h2.end()) == 2);
h1 = std::move(h2);
BEAST_EXPECT(h1.size() == 2);
BEAST_EXPECT(h2.size() == 0);
bh h3(std::move(h1));
BEAST_EXPECT(h3.size() == 2);
BEAST_EXPECT(h1.size() == 0);
self_assign(h3, std::move(h3));
BEAST_EXPECT(h3.size() == 2);
BEAST_EXPECT(h2.erase("Not-Present") == 0);
}
void testRFC2616()
{
bh h;
h.insert("a", "w");
h.insert("a", "x");
h.insert("aa", "y");
h.insert("b", "z");
BEAST_EXPECT(h.count("a") == 2);
}
void testErase()
{
bh h;
h.insert("a", "w");
h.insert("a", "x");
h.insert("aa", "y");
h.insert("b", "z");
BEAST_EXPECT(h.size() == 4);
h.erase("a");
BEAST_EXPECT(h.size() == 2);
}
void run() override
{
testHeaders();
testRFC2616();
}
};
BEAST_DEFINE_TESTSUITE(basic_fields,http,beast);
} // http
} // beast

View File

@ -7,3 +7,90 @@
// Test that header file is self-contained. // Test that header file is self-contained.
#include <beast/http/fields.hpp> #include <beast/http/fields.hpp>
#include <beast/unit_test/suite.hpp>
#include <boost/lexical_cast.hpp>
namespace beast {
namespace http {
class basic_fields_test : public beast::unit_test::suite
{
public:
template<class Allocator>
using bha = basic_fields<Allocator>;
using bh = basic_fields<std::allocator<char>>;
template<class Allocator>
static
void
fill(std::size_t n, basic_fields<Allocator>& h)
{
for(std::size_t i = 1; i<= n; ++i)
h.insert(boost::lexical_cast<std::string>(i), i);
}
template<class U, class V>
static
void
self_assign(U& u, V&& v)
{
u = std::forward<V>(v);
}
void testHeaders()
{
bh h1;
BEAST_EXPECT(h1.empty());
fill(1, h1);
BEAST_EXPECT(h1.size() == 1);
bh h2;
h2 = h1;
BEAST_EXPECT(h2.size() == 1);
h2.insert("2", "2");
BEAST_EXPECT(std::distance(h2.begin(), h2.end()) == 2);
h1 = std::move(h2);
BEAST_EXPECT(h1.size() == 2);
BEAST_EXPECT(h2.size() == 0);
bh h3(std::move(h1));
BEAST_EXPECT(h3.size() == 2);
BEAST_EXPECT(h1.size() == 0);
self_assign(h3, std::move(h3));
BEAST_EXPECT(h3.size() == 2);
BEAST_EXPECT(h2.erase("Not-Present") == 0);
}
void testRFC2616()
{
bh h;
h.insert("a", "w");
h.insert("a", "x");
h.insert("aa", "y");
h.insert("b", "z");
BEAST_EXPECT(h.count("a") == 2);
}
void testErase()
{
bh h;
h.insert("a", "w");
h.insert("a", "x");
h.insert("aa", "y");
h.insert("b", "z");
BEAST_EXPECT(h.size() == 4);
h.erase("a");
BEAST_EXPECT(h.size() == 2);
}
void run() override
{
testHeaders();
testRFC2616();
}
};
BEAST_DEFINE_TESTSUITE(basic_fields,http,beast);
} // http
} // beast