Files
boost_beast/include/boost/beast/http/impl/fields.ipp

1373 lines
31 KiB
Plaintext
Raw Normal View History

2017-07-20 08:01:46 -07:00
//
2017-07-24 09:42:36 -07:00
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
2017-07-20 08:01:46 -07:00
//
// 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)
//
2017-07-20 13:40:34 -07:00
// Official repository: https://github.com/boostorg/beast
//
2017-07-20 08:01:46 -07:00
2017-07-20 13:40:34 -07:00
#ifndef BOOST_BEAST_HTTP_IMPL_FIELDS_IPP
#define BOOST_BEAST_HTTP_IMPL_FIELDS_IPP
#include <boost/beast/core/buffer_cat.hpp>
#include <boost/beast/core/string.hpp>
#include <boost/beast/core/static_string.hpp>
#include <boost/beast/core/detail/buffers_ref.hpp>
#include <boost/beast/http/verb.hpp>
#include <boost/beast/http/rfc7230.hpp>
#include <boost/beast/http/status.hpp>
#include <boost/beast/http/chunk_encode.hpp>
#include <boost/throw_exception.hpp>
#include <stdexcept>
#include <string>
#if defined(BOOST_LIBSTDCXX_VERSION) && BOOST_LIBSTDCXX_VERSION < 60000
// Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
2017-07-20 13:40:34 -07:00
#ifndef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
#define BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
#endif
#endif
2017-07-20 13:40:34 -07:00
namespace boost {
2017-07-20 08:01:46 -07:00
namespace beast {
namespace http {
2017-06-06 17:26:11 -07:00
template<class Allocator>
class basic_fields<Allocator>::reader
{
public:
using iter_type = typename list_t::const_iterator;
struct field_iterator
{
iter_type it_;
using value_type = boost::asio::const_buffer;
using pointer = value_type const*;
using reference = value_type const;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
field_iterator() = default;
field_iterator(field_iterator&& other) = default;
field_iterator(field_iterator const& other) = default;
field_iterator& operator=(field_iterator&& other) = default;
field_iterator& operator=(field_iterator const& other) = default;
explicit
field_iterator(iter_type it)
: it_(it)
{
}
bool
operator==(field_iterator const& other) const
{
return it_ == other.it_;
}
bool
operator!=(field_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
return it_->buffer();
}
field_iterator&
operator++()
{
++it_;
return *this;
}
field_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
field_iterator&
operator--()
{
--it_;
return *this;
}
field_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
};
class field_range
{
field_iterator first_;
field_iterator last_;
public:
using const_iterator =
field_iterator;
using value_type =
typename const_iterator::value_type;
field_range(field_range const&) = default;
field_range(iter_type first, iter_type last)
: first_(first)
, last_(last)
{
}
const_iterator
begin() const
{
return first_;
}
const_iterator
end() const
{
return last_;
}
};
Refactor chunked-encoding serialization: New buffer sequence classes are provided to allow full control over the serialization of chunk-encoded message payloads: * chunk_header A ConstBufferSequence representing the chunk header. It includes a hexadecimal-encoded size, an optional set of chunk extensions, and the trailing CRLF required to denote the end of the chunk header. This allows the caller to manually serialize the chunk body in one or more calls to a stream output function. The caller must also output an object of type `chunk_crlf` to the stream to indicate the end of the chunk body. * chunk_crlf A small ConstBufferSequence that simply represents the two character sequence "\r\n" (CRLF). This is needed for the case where the caller wants to output a chunk body as a series of buffers (i.e. "chunking a chunk"). * chunk_body A ConstBufferSequence representing a complete chunk. This includes the size, an optional set of chunk extensions, a caller provided buffer containing the body, and the required CRLF that follows. * chunk_final A ConstBufferSequence representing a final chunk. It includes an optional set of caller-provided field trailers * chunk_extensions A container for building a set of chunk extensions to use during serialization. The use of the container is optional, callers may provide their own buffer containing a correctly formatted set of chunk extensions, or they may use their own convenience container which meets the requirements. The basic_fields container is modified to allow construction outside the context of a message. The container can be used to provide trailers to `chunk_final`. Actions Required: * Remove references to ChunkDecorators. Use the new chunk-encoding buffer sequences to manually produce a chunked payload body in the case where control over the chunk-extensions and/or trailers is required.
2017-07-09 20:09:30 -07:00
using view_type = buffer_cat_view<
boost::asio::const_buffers_1,
boost::asio::const_buffers_1,
boost::asio::const_buffers_1,
field_range,
chunk_crlf>;
2017-06-06 17:26:11 -07:00
basic_fields const& f_;
Refactor chunked-encoding serialization: New buffer sequence classes are provided to allow full control over the serialization of chunk-encoded message payloads: * chunk_header A ConstBufferSequence representing the chunk header. It includes a hexadecimal-encoded size, an optional set of chunk extensions, and the trailing CRLF required to denote the end of the chunk header. This allows the caller to manually serialize the chunk body in one or more calls to a stream output function. The caller must also output an object of type `chunk_crlf` to the stream to indicate the end of the chunk body. * chunk_crlf A small ConstBufferSequence that simply represents the two character sequence "\r\n" (CRLF). This is needed for the case where the caller wants to output a chunk body as a series of buffers (i.e. "chunking a chunk"). * chunk_body A ConstBufferSequence representing a complete chunk. This includes the size, an optional set of chunk extensions, a caller provided buffer containing the body, and the required CRLF that follows. * chunk_final A ConstBufferSequence representing a final chunk. It includes an optional set of caller-provided field trailers * chunk_extensions A container for building a set of chunk extensions to use during serialization. The use of the container is optional, callers may provide their own buffer containing a correctly formatted set of chunk extensions, or they may use their own convenience container which meets the requirements. The basic_fields container is modified to allow construction outside the context of a message. The container can be used to provide trailers to `chunk_final`. Actions Required: * Remove references to ChunkDecorators. Use the new chunk-encoding buffer sequences to manually produce a chunked payload body in the case where control over the chunk-extensions and/or trailers is required.
2017-07-09 20:09:30 -07:00
boost::optional<view_type> view_;
2017-06-23 09:33:28 -07:00
char buf_[13];
2017-06-06 17:26:11 -07:00
public:
using const_buffers_type =
Refactor chunked-encoding serialization: New buffer sequence classes are provided to allow full control over the serialization of chunk-encoded message payloads: * chunk_header A ConstBufferSequence representing the chunk header. It includes a hexadecimal-encoded size, an optional set of chunk extensions, and the trailing CRLF required to denote the end of the chunk header. This allows the caller to manually serialize the chunk body in one or more calls to a stream output function. The caller must also output an object of type `chunk_crlf` to the stream to indicate the end of the chunk body. * chunk_crlf A small ConstBufferSequence that simply represents the two character sequence "\r\n" (CRLF). This is needed for the case where the caller wants to output a chunk body as a series of buffers (i.e. "chunking a chunk"). * chunk_body A ConstBufferSequence representing a complete chunk. This includes the size, an optional set of chunk extensions, a caller provided buffer containing the body, and the required CRLF that follows. * chunk_final A ConstBufferSequence representing a final chunk. It includes an optional set of caller-provided field trailers * chunk_extensions A container for building a set of chunk extensions to use during serialization. The use of the container is optional, callers may provide their own buffer containing a correctly formatted set of chunk extensions, or they may use their own convenience container which meets the requirements. The basic_fields container is modified to allow construction outside the context of a message. The container can be used to provide trailers to `chunk_final`. Actions Required: * Remove references to ChunkDecorators. Use the new chunk-encoding buffer sequences to manually produce a chunked payload body in the case where control over the chunk-extensions and/or trailers is required.
2017-07-09 20:09:30 -07:00
beast::detail::buffers_ref<view_type>;
2017-06-06 17:26:11 -07:00
reader(basic_fields const& f,
unsigned version, verb v);
reader(basic_fields const& f,
2017-06-17 17:38:24 -07:00
unsigned version, unsigned code);
2017-06-06 17:26:11 -07:00
Refactor chunked-encoding serialization: New buffer sequence classes are provided to allow full control over the serialization of chunk-encoded message payloads: * chunk_header A ConstBufferSequence representing the chunk header. It includes a hexadecimal-encoded size, an optional set of chunk extensions, and the trailing CRLF required to denote the end of the chunk header. This allows the caller to manually serialize the chunk body in one or more calls to a stream output function. The caller must also output an object of type `chunk_crlf` to the stream to indicate the end of the chunk body. * chunk_crlf A small ConstBufferSequence that simply represents the two character sequence "\r\n" (CRLF). This is needed for the case where the caller wants to output a chunk body as a series of buffers (i.e. "chunking a chunk"). * chunk_body A ConstBufferSequence representing a complete chunk. This includes the size, an optional set of chunk extensions, a caller provided buffer containing the body, and the required CRLF that follows. * chunk_final A ConstBufferSequence representing a final chunk. It includes an optional set of caller-provided field trailers * chunk_extensions A container for building a set of chunk extensions to use during serialization. The use of the container is optional, callers may provide their own buffer containing a correctly formatted set of chunk extensions, or they may use their own convenience container which meets the requirements. The basic_fields container is modified to allow construction outside the context of a message. The container can be used to provide trailers to `chunk_final`. Actions Required: * Remove references to ChunkDecorators. Use the new chunk-encoding buffer sequences to manually produce a chunked payload body in the case where control over the chunk-extensions and/or trailers is required.
2017-07-09 20:09:30 -07:00
reader(basic_fields const& f);
2017-06-06 17:26:11 -07:00
const_buffers_type
get() const
{
Refactor chunked-encoding serialization: New buffer sequence classes are provided to allow full control over the serialization of chunk-encoded message payloads: * chunk_header A ConstBufferSequence representing the chunk header. It includes a hexadecimal-encoded size, an optional set of chunk extensions, and the trailing CRLF required to denote the end of the chunk header. This allows the caller to manually serialize the chunk body in one or more calls to a stream output function. The caller must also output an object of type `chunk_crlf` to the stream to indicate the end of the chunk body. * chunk_crlf A small ConstBufferSequence that simply represents the two character sequence "\r\n" (CRLF). This is needed for the case where the caller wants to output a chunk body as a series of buffers (i.e. "chunking a chunk"). * chunk_body A ConstBufferSequence representing a complete chunk. This includes the size, an optional set of chunk extensions, a caller provided buffer containing the body, and the required CRLF that follows. * chunk_final A ConstBufferSequence representing a final chunk. It includes an optional set of caller-provided field trailers * chunk_extensions A container for building a set of chunk extensions to use during serialization. The use of the container is optional, callers may provide their own buffer containing a correctly formatted set of chunk extensions, or they may use their own convenience container which meets the requirements. The basic_fields container is modified to allow construction outside the context of a message. The container can be used to provide trailers to `chunk_final`. Actions Required: * Remove references to ChunkDecorators. Use the new chunk-encoding buffer sequences to manually produce a chunked payload body in the case where control over the chunk-extensions and/or trailers is required.
2017-07-09 20:09:30 -07:00
return const_buffers_type(*view_);
2017-06-06 17:26:11 -07:00
}
};
Refactor chunked-encoding serialization: New buffer sequence classes are provided to allow full control over the serialization of chunk-encoded message payloads: * chunk_header A ConstBufferSequence representing the chunk header. It includes a hexadecimal-encoded size, an optional set of chunk extensions, and the trailing CRLF required to denote the end of the chunk header. This allows the caller to manually serialize the chunk body in one or more calls to a stream output function. The caller must also output an object of type `chunk_crlf` to the stream to indicate the end of the chunk body. * chunk_crlf A small ConstBufferSequence that simply represents the two character sequence "\r\n" (CRLF). This is needed for the case where the caller wants to output a chunk body as a series of buffers (i.e. "chunking a chunk"). * chunk_body A ConstBufferSequence representing a complete chunk. This includes the size, an optional set of chunk extensions, a caller provided buffer containing the body, and the required CRLF that follows. * chunk_final A ConstBufferSequence representing a final chunk. It includes an optional set of caller-provided field trailers * chunk_extensions A container for building a set of chunk extensions to use during serialization. The use of the container is optional, callers may provide their own buffer containing a correctly formatted set of chunk extensions, or they may use their own convenience container which meets the requirements. The basic_fields container is modified to allow construction outside the context of a message. The container can be used to provide trailers to `chunk_final`. Actions Required: * Remove references to ChunkDecorators. Use the new chunk-encoding buffer sequences to manually produce a chunked payload body in the case where control over the chunk-extensions and/or trailers is required.
2017-07-09 20:09:30 -07:00
template<class Allocator>
basic_fields<Allocator>::reader::
reader(basic_fields const& f)
: f_(f)
{
view_.emplace(
boost::asio::const_buffers_1{nullptr, 0},
boost::asio::const_buffers_1{nullptr, 0},
boost::asio::const_buffers_1{nullptr, 0},
field_range(f_.list_.begin(), f_.list_.end()),
chunk_crlf());
}
template<class Allocator>
basic_fields<Allocator>::reader::
reader(basic_fields const& f,
unsigned version, verb v)
: f_(f)
{
2017-06-23 09:33:28 -07:00
/*
request
"<method>"
" <target>"
" HTTP/X.Y\r\n" (11 chars)
*/
string_view sv;
if(v == verb::unknown)
sv = f_.get_method_impl();
else
sv = to_string(v);
// target_or_reason_ has a leading SP
buf_[0] = ' ';
buf_[1] = 'H';
buf_[2] = 'T';
buf_[3] = 'T';
buf_[4] = 'P';
buf_[5] = '/';
buf_[6] = '0' + static_cast<char>(version / 10);
buf_[7] = '.';
buf_[8] = '0' + static_cast<char>(version % 10);
buf_[9] = '\r';
buf_[10]= '\n';
Refactor chunked-encoding serialization: New buffer sequence classes are provided to allow full control over the serialization of chunk-encoded message payloads: * chunk_header A ConstBufferSequence representing the chunk header. It includes a hexadecimal-encoded size, an optional set of chunk extensions, and the trailing CRLF required to denote the end of the chunk header. This allows the caller to manually serialize the chunk body in one or more calls to a stream output function. The caller must also output an object of type `chunk_crlf` to the stream to indicate the end of the chunk body. * chunk_crlf A small ConstBufferSequence that simply represents the two character sequence "\r\n" (CRLF). This is needed for the case where the caller wants to output a chunk body as a series of buffers (i.e. "chunking a chunk"). * chunk_body A ConstBufferSequence representing a complete chunk. This includes the size, an optional set of chunk extensions, a caller provided buffer containing the body, and the required CRLF that follows. * chunk_final A ConstBufferSequence representing a final chunk. It includes an optional set of caller-provided field trailers * chunk_extensions A container for building a set of chunk extensions to use during serialization. The use of the container is optional, callers may provide their own buffer containing a correctly formatted set of chunk extensions, or they may use their own convenience container which meets the requirements. The basic_fields container is modified to allow construction outside the context of a message. The container can be used to provide trailers to `chunk_final`. Actions Required: * Remove references to ChunkDecorators. Use the new chunk-encoding buffer sequences to manually produce a chunked payload body in the case where control over the chunk-extensions and/or trailers is required.
2017-07-09 20:09:30 -07:00
view_.emplace(
boost::asio::const_buffers_1{sv.data(), sv.size()},
boost::asio::const_buffers_1{
f_.target_or_reason_.data(),
f_.target_or_reason_.size()},
boost::asio::const_buffers_1{buf_, 11},
field_range(f_.list_.begin(), f_.list_.end()),
chunk_crlf());
}
template<class Allocator>
basic_fields<Allocator>::reader::
reader(basic_fields const& f,
2017-06-17 17:38:24 -07:00
unsigned version, unsigned code)
: f_(f)
{
2017-06-23 09:33:28 -07:00
/*
response
"HTTP/X.Y ### " (13 chars)
"<reason>"
"\r\n"
*/
buf_[0] = 'H';
buf_[1] = 'T';
buf_[2] = 'T';
buf_[3] = 'P';
buf_[4] = '/';
buf_[5] = '0' + static_cast<char>(version / 10);
buf_[6] = '.';
buf_[7] = '0' + static_cast<char>(version % 10);
buf_[8] = ' ';
buf_[9] = '0' + static_cast<char>(code / 100);
buf_[10]= '0' + static_cast<char>((code / 10) % 10);
buf_[11]= '0' + static_cast<char>(code % 10);
buf_[12]= ' ';
string_view sv;
if(! f_.target_or_reason_.empty())
sv = f_.target_or_reason_;
else
sv = obsolete_reason(static_cast<status>(code));
Refactor chunked-encoding serialization: New buffer sequence classes are provided to allow full control over the serialization of chunk-encoded message payloads: * chunk_header A ConstBufferSequence representing the chunk header. It includes a hexadecimal-encoded size, an optional set of chunk extensions, and the trailing CRLF required to denote the end of the chunk header. This allows the caller to manually serialize the chunk body in one or more calls to a stream output function. The caller must also output an object of type `chunk_crlf` to the stream to indicate the end of the chunk body. * chunk_crlf A small ConstBufferSequence that simply represents the two character sequence "\r\n" (CRLF). This is needed for the case where the caller wants to output a chunk body as a series of buffers (i.e. "chunking a chunk"). * chunk_body A ConstBufferSequence representing a complete chunk. This includes the size, an optional set of chunk extensions, a caller provided buffer containing the body, and the required CRLF that follows. * chunk_final A ConstBufferSequence representing a final chunk. It includes an optional set of caller-provided field trailers * chunk_extensions A container for building a set of chunk extensions to use during serialization. The use of the container is optional, callers may provide their own buffer containing a correctly formatted set of chunk extensions, or they may use their own convenience container which meets the requirements. The basic_fields container is modified to allow construction outside the context of a message. The container can be used to provide trailers to `chunk_final`. Actions Required: * Remove references to ChunkDecorators. Use the new chunk-encoding buffer sequences to manually produce a chunked payload body in the case where control over the chunk-extensions and/or trailers is required.
2017-07-09 20:09:30 -07:00
view_.emplace(
boost::asio::const_buffers_1{buf_, 13},
boost::asio::const_buffers_1{sv.data(), sv.size()},
boost::asio::const_buffers_1{"\r\n", 2},
field_range(f_.list_.begin(), f_.list_.end()),
chunk_crlf{});
}
2017-06-06 17:26:11 -07:00
//------------------------------------------------------------------------------
2017-07-20 08:01:46 -07:00
template<class Allocator>
basic_fields<Allocator>::
value_type::
value_type(field name,
string_view sname, string_view value)
: off_(static_cast<off_t>(sname.size() + 2))
2017-06-06 11:56:06 -07:00
, len_(static_cast<off_t>(value.size()))
, f_(name)
2017-07-20 08:01:46 -07:00
{
//BOOST_ASSERT(name == field::unknown ||
// iequals(sname, to_string(name)));
2017-06-06 11:56:06 -07:00
char* p = reinterpret_cast<char*>(this + 1);
p[off_-2] = ':';
p[off_-1] = ' ';
p[off_ + len_] = '\r';
p[off_ + len_ + 1] = '\n';
std::memcpy(p, sname.data(), sname.size());
2017-06-06 11:56:06 -07:00
std::memcpy(p + off_, value.data(), value.size());
2017-07-20 08:01:46 -07:00
}
template<class Allocator>
inline
field
basic_fields<Allocator>::
value_type::
2017-06-06 11:56:06 -07:00
name() const
{
return f_;
}
template<class Allocator>
inline
string_view
basic_fields<Allocator>::
value_type::
name_string() const
2017-07-20 08:01:46 -07:00
{
2017-06-06 11:56:06 -07:00
return {reinterpret_cast<
char const*>(this + 1),
static_cast<std::size_t>(off_ - 2)};
2017-07-20 08:01:46 -07:00
}
template<class Allocator>
inline
2017-06-06 11:56:06 -07:00
string_view
basic_fields<Allocator>::
value_type::
2017-06-06 11:56:06 -07:00
value() const
2017-07-20 08:01:46 -07:00
{
2017-06-06 11:56:06 -07:00
return {reinterpret_cast<
char const*>(this + 1) + off_,
static_cast<std::size_t>(len_)};
2017-07-20 08:01:46 -07:00
}
2017-06-06 17:26:11 -07:00
template<class Allocator>
inline
boost::asio::const_buffer
basic_fields<Allocator>::
value_type::
2017-06-06 17:26:11 -07:00
buffer() const
{
return boost::asio::const_buffer{
reinterpret_cast<char const*>(this + 1),
static_cast<std::size_t>(off_) + len_ + 2};
}
2017-06-06 11:56:06 -07:00
//------------------------------------------------------------------------------
2017-07-20 08:01:46 -07:00
template<class Allocator>
basic_fields<Allocator>::
2017-06-06 11:56:06 -07:00
~basic_fields()
2017-07-20 08:01:46 -07:00
{
2017-06-06 11:56:06 -07:00
delete_list();
realloc_string(method_, {});
2017-06-23 09:33:28 -07:00
realloc_string(
target_or_reason_, {});
2017-07-20 08:01:46 -07:00
}
template<class Allocator>
basic_fields<Allocator>::
2017-06-06 11:56:06 -07:00
basic_fields(Allocator const& alloc)
: alloc_(alloc)
2017-07-20 08:01:46 -07:00
{
}
template<class Allocator>
basic_fields<Allocator>::
2017-06-06 11:56:06 -07:00
basic_fields(basic_fields&& other)
2017-06-10 14:28:15 -07:00
: alloc_(std::move(other.alloc_))
, set_(std::move(other.set_))
2017-06-06 11:56:06 -07:00
, list_(std::move(other.list_))
, method_(other.method_)
, target_or_reason_(other.target_or_reason_)
{
2017-06-06 11:56:06 -07:00
other.method_.clear();
other.target_or_reason_.clear();
}
2017-07-20 08:01:46 -07:00
2017-06-10 14:28:15 -07:00
template<class Allocator>
basic_fields<Allocator>::
basic_fields(basic_fields&& other, Allocator const& alloc)
: alloc_(alloc)
{
if(alloc_ != other.alloc_)
{
copy_all(other);
other.clear_all();
}
else
{
set_ = std::move(other.set_);
list_ = std::move(other.list_);
method_ = other.method_;
target_or_reason_ = other.target_or_reason_;
}
}
2017-07-20 08:01:46 -07:00
template<class Allocator>
basic_fields<Allocator>::
2017-06-06 11:56:06 -07:00
basic_fields(basic_fields const& other)
2017-06-10 14:28:15 -07:00
: alloc_(alloc_traits::
2017-06-06 11:56:06 -07:00
select_on_container_copy_construction(other.alloc_))
2017-07-20 08:01:46 -07:00
{
2017-06-06 11:56:06 -07:00
copy_all(other);
2017-07-20 08:01:46 -07:00
}
template<class Allocator>
basic_fields<Allocator>::
2017-06-06 11:56:06 -07:00
basic_fields(basic_fields const& other,
Allocator const& alloc)
: alloc_(alloc)
2017-07-20 08:01:46 -07:00
{
2017-06-06 11:56:06 -07:00
copy_all(other);
2017-07-20 08:01:46 -07:00
}
template<class Allocator>
2017-06-10 14:28:15 -07:00
template<class OtherAlloc>
basic_fields<Allocator>::
2017-06-06 11:56:06 -07:00
basic_fields(basic_fields<OtherAlloc> const& other)
2017-07-20 08:01:46 -07:00
{
2017-06-06 11:56:06 -07:00
copy_all(other);
}
template<class Allocator>
2017-06-10 14:28:15 -07:00
template<class OtherAlloc>
2017-06-06 11:56:06 -07:00
basic_fields<Allocator>::
basic_fields(basic_fields<OtherAlloc> const& other,
Allocator const& alloc)
: alloc_(alloc)
{
copy_all(other);
2017-07-20 08:01:46 -07:00
}
template<class Allocator>
auto
basic_fields<Allocator>::
operator=(basic_fields&& other) ->
basic_fields&
2017-07-20 08:01:46 -07:00
{
if(this == &other)
return *this;
2017-06-06 11:56:06 -07:00
move_assign(other, typename alloc_traits::
propagate_on_container_move_assignment{});
2017-07-20 08:01:46 -07:00
return *this;
}
template<class Allocator>
auto
basic_fields<Allocator>::
operator=(basic_fields const& other) ->
basic_fields&
2017-07-20 08:01:46 -07:00
{
2017-06-06 11:56:06 -07:00
copy_assign(other, typename alloc_traits::
propagate_on_container_copy_assignment{});
2017-07-20 08:01:46 -07:00
return *this;
}
template<class Allocator>
2017-06-10 14:28:15 -07:00
template<class OtherAlloc>
2017-07-20 08:01:46 -07:00
auto
basic_fields<Allocator>::
operator=(basic_fields<OtherAlloc> const& other) ->
basic_fields&
2017-07-20 08:01:46 -07:00
{
2017-06-06 11:56:06 -07:00
clear_all();
copy_all(other);
2017-07-20 08:01:46 -07:00
return *this;
}
//------------------------------------------------------------------------------
//
// Element access
//
2017-06-06 11:56:06 -07:00
//------------------------------------------------------------------------------
template<class Allocator>
string_view
basic_fields<Allocator>::
at(field name) const
2017-07-20 08:01:46 -07:00
{
BOOST_ASSERT(name != field::unknown);
auto const it = find(name);
if(it == end())
BOOST_THROW_EXCEPTION(std::out_of_range{
"field not found"});
return it->value();
2017-07-20 08:01:46 -07:00
}
2017-06-08 22:03:58 -07:00
template<class Allocator>
string_view
2017-06-08 22:03:58 -07:00
basic_fields<Allocator>::
at(string_view name) const
2017-06-08 22:03:58 -07:00
{
auto const it = find(name);
if(it == end())
BOOST_THROW_EXCEPTION(std::out_of_range{
"field not found"});
return it->value();
2017-06-08 22:03:58 -07:00
}
2017-07-20 08:01:46 -07:00
template<class Allocator>
string_view
basic_fields<Allocator>::
operator[](field name) const
2017-07-20 08:01:46 -07:00
{
BOOST_ASSERT(name != field::unknown);
2017-07-20 08:01:46 -07:00
auto const it = find(name);
if(it == end())
return {};
return it->value();
2017-07-20 08:01:46 -07:00
}
2017-06-08 22:03:58 -07:00
template<class Allocator>
string_view
2017-06-08 22:03:58 -07:00
basic_fields<Allocator>::
operator[](string_view name) const
2017-06-08 22:03:58 -07:00
{
auto const it = find(name);
if(it == end())
return {};
return it->value();
}
//------------------------------------------------------------------------------
//
// Modifiers
//
//------------------------------------------------------------------------------
2017-07-20 08:01:46 -07:00
template<class Allocator>
void
basic_fields<Allocator>::
clear()
2017-07-20 08:01:46 -07:00
{
2017-06-06 11:56:06 -07:00
delete_list();
2017-07-20 08:01:46 -07:00
set_.clear();
2017-06-06 11:56:06 -07:00
list_.clear();
2017-07-20 08:01:46 -07:00
}
template<class Allocator>
inline
void
basic_fields<Allocator>::
insert(field name, string_param const& value)
{
BOOST_ASSERT(name != field::unknown);
insert(name, to_string(name), value);
}
2017-07-20 08:01:46 -07:00
template<class Allocator>
void
basic_fields<Allocator>::
insert(string_view sname, string_param const& value)
2017-07-20 08:01:46 -07:00
{
auto const name =
string_to_field(sname);
insert(name, sname, value);
}
template<class Allocator>
void
basic_fields<Allocator>::
insert(field name,
string_view sname, string_param const& value)
{
2017-06-22 16:49:46 -07:00
auto& e = new_element(name, sname,
static_cast<string_view>(value));
auto const before =
set_.upper_bound(sname, key_compare{});
if(before == set_.begin())
{
2017-06-15 08:31:47 -07:00
BOOST_ASSERT(count(sname) == 0);
set_.insert_before(before, e);
list_.push_back(e);
return;
}
auto const last = std::prev(before);
2017-06-15 08:31:47 -07:00
// VFALCO is it worth comparing `field name` first?
if(! iequals(sname, last->name_string()))
{
2017-06-15 08:31:47 -07:00
BOOST_ASSERT(count(sname) == 0);
set_.insert_before(before, e);
list_.push_back(e);
return;
}
2017-06-15 08:31:47 -07:00
// keep duplicate fields together in the list
set_.insert_before(before, e);
2017-06-15 08:31:47 -07:00
list_.insert(++list_.iterator_to(*last), e);
2017-07-20 08:01:46 -07:00
}
template<class Allocator>
void
basic_fields<Allocator>::
set(field name, string_param const& value)
{
BOOST_ASSERT(name != field::unknown);
2017-06-22 16:49:46 -07:00
set_element(new_element(name, to_string(name),
static_cast<string_view>(value)));
}
2017-07-20 08:01:46 -07:00
template<class Allocator>
void
basic_fields<Allocator>::
set(string_view sname, string_param const& value)
{
set_element(new_element(
2017-06-22 16:49:46 -07:00
string_to_field(sname), sname,
static_cast<string_view>(value)));
}
template<class Allocator>
auto
basic_fields<Allocator>::
erase(const_iterator pos) ->
const_iterator
{
auto next = pos.iter();
auto& e = *next++;
set_.erase(e);
list_.erase(e);
delete_element(e);
return next;
}
template<class Allocator>
std::size_t
basic_fields<Allocator>::
erase(field name)
2017-07-20 08:01:46 -07:00
{
BOOST_ASSERT(name != field::unknown);
return erase(to_string(name));
}
template<class Allocator>
std::size_t
basic_fields<Allocator>::
erase(string_view name)
{
std::size_t n =0;
set_.erase_and_dispose(name, key_compare{},
[&](value_type* e)
{
++n;
list_.erase(list_.iterator_to(*e));
delete_element(*e);
});
return n;
2017-07-20 08:01:46 -07:00
}
template<class Allocator>
void
basic_fields<Allocator>::
swap(basic_fields<Allocator>& other)
2017-07-20 08:01:46 -07:00
{
swap(other, typename alloc_traits::
propagate_on_container_swap{});
2017-07-20 08:01:46 -07:00
}
2017-06-08 22:03:58 -07:00
template<class Allocator>
void
swap(
basic_fields<Allocator>& lhs,
basic_fields<Allocator>& rhs)
{
lhs.swap(rhs);
}
//------------------------------------------------------------------------------
//
// Lookup
//
//------------------------------------------------------------------------------
template<class Allocator>
inline
std::size_t
2017-06-08 22:03:58 -07:00
basic_fields<Allocator>::
count(field name) const
2017-06-08 22:03:58 -07:00
{
BOOST_ASSERT(name != field::unknown);
return count(to_string(name));
}
template<class Allocator>
std::size_t
basic_fields<Allocator>::
count(string_view name) const
{
return set_.count(name, key_compare{});
}
template<class Allocator>
inline
auto
basic_fields<Allocator>::
find(field name) const ->
const_iterator
{
BOOST_ASSERT(name != field::unknown);
return find(to_string(name));
}
template<class Allocator>
auto
basic_fields<Allocator>::
find(string_view name) const ->
const_iterator
{
auto const it = set_.find(
name, key_compare{});
if(it == set_.end())
return list_.end();
return list_.iterator_to(*it);
}
template<class Allocator>
inline
auto
basic_fields<Allocator>::
equal_range(field name) const ->
std::pair<const_iterator, const_iterator>
{
BOOST_ASSERT(name != field::unknown);
return equal_range(to_string(name));
}
template<class Allocator>
auto
basic_fields<Allocator>::
equal_range(string_view name) const ->
std::pair<const_iterator, const_iterator>
{
auto const result =
set_.equal_range(name, key_compare{});
return {
list_.iterator_to(result->first),
list_.iterator_to(result->second)};
2017-06-08 22:03:58 -07:00
}
2017-06-06 11:56:06 -07:00
//------------------------------------------------------------------------------
namespace detail {
2017-06-06 17:26:11 -07:00
// Filter a token list
//
template<class String, class Pred>
2017-06-06 11:56:06 -07:00
void
filter_token_list(
String& s,
2017-07-12 16:35:52 -07:00
string_view value,
Pred&& pred)
2017-06-06 11:56:06 -07:00
{
token_list te{value};
auto it = te.begin();
auto last = te.end();
if(it == last)
return;
while(pred(*it))
if(++it == last)
return;
s.append(it->data(), it->size());
while(++it != last)
{
if(! pred(*it))
{
s.append(", ");
s.append(it->data(), it->size());
}
}
2017-06-06 11:56:06 -07:00
}
// Filter the last item in a token list
template<class String, class Pred>
2017-06-06 11:56:06 -07:00
void
filter_token_list_last(
String& s,
2017-07-12 16:35:52 -07:00
string_view value,
Pred&& pred)
2017-06-06 11:56:06 -07:00
{
token_list te{value};
if(te.begin() != te.end())
{
auto it = te.begin();
auto next = std::next(it);
if(next == te.end())
{
if(! pred(*it))
s.append(it->data(), it->size());
return;
}
s.append(it->data(), it->size());
for(;;)
{
it = next;
next = std::next(it);
if(next == te.end())
{
if(! pred(*it))
{
s.append(", ");
s.append(it->data(), it->size());
}
return;
}
s.append(", ");
s.append(it->data(), it->size());
}
}
2017-06-06 11:56:06 -07:00
}
template<class String>
2017-06-06 11:56:06 -07:00
void
keep_alive_impl(
2017-07-12 16:35:52 -07:00
String& s, string_view value,
unsigned version, bool keep_alive)
2017-06-06 11:56:06 -07:00
{
if(version < 11)
{
if(keep_alive)
{
// remove close
filter_token_list(s, value,
[](string_view s)
{
return iequals(s, "close");
});
// add keep-alive
if(s.empty())
s.append("keep-alive");
else if(! token_list{value}.exists("keep-alive"))
s.append(", keep-alive");
}
else
{
// remove close and keep-alive
filter_token_list(s, value,
[](string_view s)
{
return
iequals(s, "close") ||
iequals(s, "keep-alive");
});
}
}
else
{
if(keep_alive)
{
// remove close and keep-alive
filter_token_list(s, value,
[](string_view s)
{
return
iequals(s, "close") ||
iequals(s, "keep-alive");
});
}
else
{
// remove keep-alive
filter_token_list(s, value,
[](string_view s)
{
return iequals(s, "keep-alive");
});
// add close
if(s.empty())
s.append("close");
else if(! token_list{value}.exists("close"))
s.append(", close");
}
}
2017-06-06 11:56:06 -07:00
}
} // detail
//------------------------------------------------------------------------------
// Fields
2017-06-06 17:26:11 -07:00
template<class Allocator>
inline
string_view
basic_fields<Allocator>::
get_method_impl() const
{
return method_;
}
template<class Allocator>
inline
string_view
basic_fields<Allocator>::
get_target_impl() const
{
2017-06-23 09:33:28 -07:00
if(target_or_reason_.empty())
return target_or_reason_;
return {
target_or_reason_.data() + 1,
target_or_reason_.size() - 1};
2017-06-06 17:26:11 -07:00
}
template<class Allocator>
inline
string_view
basic_fields<Allocator>::
get_reason_impl() const
{
return target_or_reason_;
}
template<class Allocator>
bool
basic_fields<Allocator>::
get_chunked_impl() const
2017-06-06 11:56:06 -07:00
{
auto const te = token_list{
(*this)[field::transfer_encoding]};
for(auto it = te.begin(); it != te.end();)
{
auto const next = std::next(it);
if(next == te.end())
return iequals(*it, "chunked");
it = next;
}
return false;
2017-06-06 11:56:06 -07:00
}
template<class Allocator>
bool
basic_fields<Allocator>::
get_keep_alive_impl(unsigned version) const
{
auto const it = find(field::connection);
if(version < 11)
{
if(it == end())
return false;
return token_list{
it->value()}.exists("keep-alive");
}
if(it == end())
return true;
return ! token_list{
it->value()}.exists("close");
}
template<class Allocator>
inline
void
basic_fields<Allocator>::
set_method_impl(string_view s)
{
realloc_string(method_, s);
}
template<class Allocator>
inline
void
basic_fields<Allocator>::
set_target_impl(string_view s)
{
realloc_target(
target_or_reason_, s);
}
2017-06-06 11:56:06 -07:00
template<class Allocator>
inline
2017-06-06 11:56:06 -07:00
void
basic_fields<Allocator>::
set_reason_impl(string_view s)
2017-06-06 11:56:06 -07:00
{
realloc_string(
target_or_reason_, s);
}
template<class Allocator>
void
basic_fields<Allocator>::
set_chunked_impl(bool value)
{
auto it = find(field::transfer_encoding);
if(value)
{
// append "chunked"
if(it == end())
{
set(field::transfer_encoding, "chunked");
return;
}
auto const te = token_list{it->value()};
for(auto itt = te.begin();;)
{
auto const next = std::next(itt);
if(next == te.end())
{
if(iequals(*itt, "chunked"))
return; // already set
break;
}
itt = next;
}
static_string<max_static_buffer> buf;
if(it->value().size() <= buf.size() + 9)
{
buf.append(it->value().data(), it->value().size());
buf.append(", chunked", 9);
set(field::transfer_encoding, buf);
}
else
{
2017-07-20 13:40:34 -07:00
#ifdef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
// Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
std::string s;
#else
using rebind_type =
typename std::allocator_traits<
Allocator>::template rebind_alloc<char>;
std::basic_string<
char,
std::char_traits<char>,
rebind_type> s{rebind_type{alloc_}};
#endif
s.reserve(it->value().size() + 9);
s.append(it->value().data(), it->value().size());
s.append(", chunked", 9);
set(field::transfer_encoding, s);
}
return;
}
// filter "chunked"
if(it == end())
return;
try
{
static_string<max_static_buffer> buf;
detail::filter_token_list_last(buf, it->value(),
2017-07-12 16:35:52 -07:00
[](string_view s)
{
return iequals(s, "chunked");
});
if(! buf.empty())
set(field::transfer_encoding, buf);
else
erase(field::transfer_encoding);
}
catch(std::length_error const&)
{
2017-07-20 13:40:34 -07:00
#ifdef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
// Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
std::string s;
#else
using rebind_type =
typename std::allocator_traits<
Allocator>::template rebind_alloc<char>;
std::basic_string<
char,
std::char_traits<char>,
rebind_type> s{rebind_type{alloc_}};
#endif
s.reserve(it->value().size());
detail::filter_token_list_last(s, it->value(),
2017-07-12 16:35:52 -07:00
[](string_view s)
{
return iequals(s, "chunked");
});
if(! s.empty())
set(field::transfer_encoding, s);
else
erase(field::transfer_encoding);
}
}
template<class Allocator>
void
basic_fields<Allocator>::
set_content_length_impl(
boost::optional<std::uint64_t> const& value)
{
if(! value)
erase(field::content_length);
2017-06-06 11:56:06 -07:00
else
set(field::content_length, *value);
}
template<class Allocator>
void
basic_fields<Allocator>::
set_keep_alive_impl(
unsigned version, bool keep_alive)
{
// VFALCO What about Proxy-Connection ?
auto const value = (*this)[field::connection];
try
{
static_string<max_static_buffer> buf;
detail::keep_alive_impl(
buf, value, version, keep_alive);
if(buf.empty())
erase(field::connection);
else
set(field::connection, buf);
}
catch(std::length_error const&)
{
2017-07-20 13:40:34 -07:00
#ifdef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
// Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
std::string s;
#else
using rebind_type =
typename std::allocator_traits<
Allocator>::template rebind_alloc<char>;
std::basic_string<
char,
std::char_traits<char>,
rebind_type> s{rebind_type{alloc_}};
#endif
s.reserve(value.size());
detail::keep_alive_impl(
s, value, version, keep_alive);
if(s.empty())
erase(field::connection);
else
set(field::connection, s);
}
2017-06-06 11:56:06 -07:00
}
//------------------------------------------------------------------------------
template<class Allocator>
auto
basic_fields<Allocator>::
new_element(field name,
string_view sname, string_view value) ->
value_type&
2017-06-06 11:56:06 -07:00
{
if(sname.size() + 2 >
2017-06-06 11:56:06 -07:00
(std::numeric_limits<off_t>::max)())
BOOST_THROW_EXCEPTION(std::length_error{
"field name too large"});
if(value.size() + 2 >
(std::numeric_limits<off_t>::max)())
BOOST_THROW_EXCEPTION(std::length_error{
"field value too large"});
value = detail::trim(value);
std::uint16_t const off =
static_cast<off_t>(sname.size() + 2);
2017-06-06 11:56:06 -07:00
std::uint16_t const len =
static_cast<off_t>(value.size());
auto const p = alloc_traits::allocate(alloc_,
1 + (off + len + 2 + sizeof(value_type) - 1) /
sizeof(value_type));
// VFALCO allocator can't call the constructor because its private
//alloc_traits::construct(alloc_, p, name, sname, value);
new(p) value_type{name, sname, value};
2017-06-06 11:56:06 -07:00
return *p;
}
template<class Allocator>
void
basic_fields<Allocator>::
delete_element(value_type& e)
2017-06-06 11:56:06 -07:00
{
auto const n = 1 + (e.off_ + e.len_ + 2 +
sizeof(value_type) - 1) / sizeof(value_type);
2017-06-06 11:56:06 -07:00
alloc_traits::destroy(alloc_, &e);
alloc_traits::deallocate(alloc_, &e, n);
}
template<class Allocator>
void
basic_fields<Allocator>::
set_element(value_type& e)
{
auto it = set_.lower_bound(
e.name_string(), key_compare{});
if(it == set_.end() || ! iequals(
e.name_string(), it->name_string()))
{
set_.insert_before(it, e);
list_.push_back(e);
return;
}
for(;;)
{
auto next = it;
++next;
set_.erase(it);
list_.erase(list_.iterator_to(*it));
delete_element(*it);
it = next;
if(it == set_.end() ||
! iequals(e.name_string(), it->name_string()))
break;
}
set_.insert_before(it, e);
list_.push_back(e);
}
2017-06-06 11:56:06 -07:00
template<class Allocator>
void
basic_fields<Allocator>::
realloc_string(string_view& dest, string_view s)
{
if(dest.empty() && s.empty())
return;
auto a = typename std::allocator_traits<
Allocator>::template rebind_alloc<
char>(alloc_);
if(! dest.empty())
{
a.deallocate(const_cast<char*>(
dest.data()), dest.size());
dest.clear();
}
if(! s.empty())
{
auto const p = a.allocate(s.size());
std::memcpy(p, s.data(), s.size());
dest = {p, s.size()};
}
}
2017-06-23 09:33:28 -07:00
template<class Allocator>
void
basic_fields<Allocator>::
realloc_target(
string_view& dest, string_view s)
{
// The target string are stored with an
// extra space at the beginning to help
// the reader class.
if(dest.empty() && s.empty())
return;
auto a = typename std::allocator_traits<
Allocator>::template rebind_alloc<
char>(alloc_);
if(! dest.empty())
{
a.deallocate(const_cast<char*>(
dest.data()), dest.size());
dest.clear();
}
if(! s.empty())
{
auto const p = a.allocate(1 + s.size());
p[0] = ' ';
std::memcpy(p + 1, s.data(), s.size());
dest = {p, 1 + s.size()};
}
}
2017-06-06 11:56:06 -07:00
template<class Allocator>
template<class OtherAlloc>
void
basic_fields<Allocator>::
copy_all(basic_fields<OtherAlloc> const& other)
{
for(auto const& e : other.list_)
insert(e.name(), e.name_string(), e.value());
2017-06-06 11:56:06 -07:00
realloc_string(method_, other.method_);
realloc_string(target_or_reason_,
other.target_or_reason_);
}
template<class Allocator>
void
basic_fields<Allocator>::
clear_all()
{
clear();
realloc_string(method_, {});
realloc_string(target_or_reason_, {});
}
template<class Allocator>
void
basic_fields<Allocator>::
delete_list()
{
for(auto it = list_.begin(); it != list_.end();)
delete_element(*it++);
}
//------------------------------------------------------------------------------
template<class Allocator>
inline
void
basic_fields<Allocator>::
move_assign(basic_fields& other, std::true_type)
{
clear_all();
set_ = std::move(other.set_);
list_ = std::move(other.list_);
method_ = other.method_;
target_or_reason_ = other.target_or_reason_;
other.method_.clear();
other.target_or_reason_.clear();
alloc_ = other.alloc_;
}
template<class Allocator>
inline
void
basic_fields<Allocator>::
move_assign(basic_fields& other, std::false_type)
{
clear_all();
if(alloc_ != other.alloc_)
{
copy_all(other);
other.clear_all();
}
else
{
set_ = std::move(other.set_);
list_ = std::move(other.list_);
method_ = other.method_;
target_or_reason_ = other.target_or_reason_;
other.method_.clear();
other.target_or_reason_.clear();
}
}
template<class Allocator>
inline
void
basic_fields<Allocator>::
copy_assign(basic_fields const& other, std::true_type)
{
clear_all();
alloc_ = other.alloc_;
copy_all(other);
}
template<class Allocator>
inline
void
basic_fields<Allocator>::
copy_assign(basic_fields const& other, std::false_type)
{
clear_all();
copy_all(other);
}
template<class Allocator>
inline
void
basic_fields<Allocator>::
swap(basic_fields& other, std::true_type)
{
using std::swap;
swap(alloc_, other.alloc_);
swap(set_, other.set_);
swap(list_, other.list_);
swap(method_, other.method_);
swap(target_or_reason_, other.target_or_reason_);
}
template<class Allocator>
inline
void
basic_fields<Allocator>::
swap(basic_fields& other, std::false_type)
{
BOOST_ASSERT(alloc_ == other.alloc_);
using std::swap;
swap(set_, other.set_);
swap(list_, other.list_);
swap(method_, other.method_);
swap(target_or_reason_, other.target_or_reason_);
}
2017-07-20 08:01:46 -07:00
} // http
} // beast
2017-07-20 13:40:34 -07:00
} // boost
2017-07-20 08:01:46 -07:00
#endif