1//
2// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
3//
4// Distributed under the Boost Software License, Version 1.0. (See accompanying
5// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6//
7// Official repository: https://github.com/boostorg/beast
8//
9
10#ifndef BOOST_BEAST_HTTP_IMPL_FIELDS_IPP
11#define BOOST_BEAST_HTTP_IMPL_FIELDS_IPP
12
13#include <boost/beast/core/buffers_cat.hpp>
14#include <boost/beast/core/string.hpp>
15#include <boost/beast/core/static_string.hpp>
16#include <boost/beast/core/detail/buffers_ref.hpp>
17#include <boost/beast/http/verb.hpp>
18#include <boost/beast/http/rfc7230.hpp>
19#include <boost/beast/http/status.hpp>
20#include <boost/beast/http/chunk_encode.hpp>
21#include <boost/core/exchange.hpp>
22#include <boost/throw_exception.hpp>
23#include <stdexcept>
24#include <string>
25
26#if defined(BOOST_LIBSTDCXX_VERSION) && BOOST_LIBSTDCXX_VERSION < 60000
27    // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
28#ifndef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
29#define BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
30#endif
31#endif
32
33namespace boost {
34namespace beast {
35namespace http {
36
37template<class Allocator>
38class basic_fields<Allocator>::writer
39{
40public:
41    using iter_type = typename list_t::const_iterator;
42
43    struct field_iterator
44    {
45        iter_type it_;
46
47        using value_type = boost::asio::const_buffer;
48        using pointer = value_type const*;
49        using reference = value_type const;
50        using difference_type = std::ptrdiff_t;
51        using iterator_category =
52            std::bidirectional_iterator_tag;
53
54        field_iterator() = default;
55        field_iterator(field_iterator&& other) = default;
56        field_iterator(field_iterator const& other) = default;
57        field_iterator& operator=(field_iterator&& other) = default;
58        field_iterator& operator=(field_iterator const& other) = default;
59
60        explicit
61        field_iterator(iter_type it)
62            : it_(it)
63        {
64        }
65
66        bool
67        operator==(field_iterator const& other) const
68        {
69            return it_ == other.it_;
70        }
71
72        bool
73        operator!=(field_iterator const& other) const
74        {
75            return !(*this == other);
76        }
77
78        reference
79        operator*() const
80        {
81            return it_->buffer();
82        }
83
84        field_iterator&
85        operator++()
86        {
87            ++it_;
88            return *this;
89        }
90
91        field_iterator
92        operator++(int)
93        {
94            auto temp = *this;
95            ++(*this);
96            return temp;
97        }
98
99        field_iterator&
100        operator--()
101        {
102            --it_;
103            return *this;
104        }
105
106        field_iterator
107        operator--(int)
108        {
109            auto temp = *this;
110            --(*this);
111            return temp;
112        }
113    };
114
115    class field_range
116    {
117        field_iterator first_;
118        field_iterator last_;
119
120    public:
121        using const_iterator =
122            field_iterator;
123
124        using value_type =
125            typename const_iterator::value_type;
126
127        field_range(iter_type first, iter_type last)
128            : first_(first)
129            , last_(last)
130        {
131        }
132
133        const_iterator
134        begin() const
135        {
136            return first_;
137        }
138
139        const_iterator
140        end() const
141        {
142            return last_;
143        }
144    };
145
146    using view_type = buffers_cat_view<
147        boost::asio::const_buffer,
148        boost::asio::const_buffer,
149        boost::asio::const_buffer,
150        field_range,
151        chunk_crlf>;
152
153    basic_fields const& f_;
154    boost::optional<view_type> view_;
155    char buf_[13];
156
157public:
158    using const_buffers_type =
159        beast::detail::buffers_ref<view_type>;
160
161    writer(basic_fields const& f,
162        unsigned version, verb v);
163
164    writer(basic_fields const& f,
165        unsigned version, unsigned code);
166
167    writer(basic_fields const& f);
168
169    const_buffers_type
170    get() const
171    {
172        return const_buffers_type(*view_);
173    }
174};
175
176template<class Allocator>
177basic_fields<Allocator>::writer::
178writer(basic_fields const& f)
179    : f_(f)
180{
181    view_.emplace(
182        boost::asio::const_buffer{nullptr, 0},
183        boost::asio::const_buffer{nullptr, 0},
184        boost::asio::const_buffer{nullptr, 0},
185        field_range(f_.list_.begin(), f_.list_.end()),
186        chunk_crlf());
187}
188
189template<class Allocator>
190basic_fields<Allocator>::writer::
191writer(basic_fields const& f,
192        unsigned version, verb v)
193    : f_(f)
194{
195/*
196    request
197        "<method>"
198        " <target>"
199        " HTTP/X.Y\r\n" (11 chars)
200*/
201    string_view sv;
202    if(v == verb::unknown)
203        sv = f_.get_method_impl();
204    else
205        sv = to_string(v);
206
207    // target_or_reason_ has a leading SP
208
209    buf_[0] = ' ';
210    buf_[1] = 'H';
211    buf_[2] = 'T';
212    buf_[3] = 'T';
213    buf_[4] = 'P';
214    buf_[5] = '/';
215    buf_[6] = '0' + static_cast<char>(version / 10);
216    buf_[7] = '.';
217    buf_[8] = '0' + static_cast<char>(version % 10);
218    buf_[9] = '\r';
219    buf_[10]= '\n';
220
221    view_.emplace(
222        boost::asio::const_buffer{sv.data(), sv.size()},
223        boost::asio::const_buffer{
224            f_.target_or_reason_.data(),
225            f_.target_or_reason_.size()},
226        boost::asio::const_buffer{buf_, 11},
227        field_range(f_.list_.begin(), f_.list_.end()),
228        chunk_crlf());
229}
230
231template<class Allocator>
232basic_fields<Allocator>::writer::
233writer(basic_fields const& f,
234        unsigned version, unsigned code)
235    : f_(f)
236{
237/*
238    response
239        "HTTP/X.Y ### " (13 chars)
240        "<reason>"
241        "\r\n"
242*/
243    buf_[0] = 'H';
244    buf_[1] = 'T';
245    buf_[2] = 'T';
246    buf_[3] = 'P';
247    buf_[4] = '/';
248    buf_[5] = '0' + static_cast<char>(version / 10);
249    buf_[6] = '.';
250    buf_[7] = '0' + static_cast<char>(version % 10);
251    buf_[8] = ' ';
252    buf_[9] = '0' + static_cast<char>(code / 100);
253    buf_[10]= '0' + static_cast<char>((code / 10) % 10);
254    buf_[11]= '0' + static_cast<char>(code % 10);
255    buf_[12]= ' ';
256
257    string_view sv;
258    if(! f_.target_or_reason_.empty())
259        sv = f_.target_or_reason_;
260    else
261        sv = obsolete_reason(static_cast<status>(code));
262
263    view_.emplace(
264        boost::asio::const_buffer{buf_, 13},
265        boost::asio::const_buffer{sv.data(), sv.size()},
266        boost::asio::const_buffer{"\r\n", 2},
267        field_range(f_.list_.begin(), f_.list_.end()),
268        chunk_crlf{});
269}
270
271//------------------------------------------------------------------------------
272
273template<class Allocator>
274char*
275basic_fields<Allocator>::
276value_type::
277data() const
278{
279    return const_cast<char*>(
280        reinterpret_cast<char const*>(
281            static_cast<element const*>(this) + 1));
282}
283
284template<class Allocator>
285boost::asio::const_buffer
286basic_fields<Allocator>::
287value_type::
288buffer() const
289{
290    return boost::asio::const_buffer{data(),
291        static_cast<std::size_t>(off_) + len_ + 2};
292}
293
294template<class Allocator>
295basic_fields<Allocator>::
296value_type::
297value_type(field name,
298    string_view sname, string_view value)
299    : off_(static_cast<off_t>(sname.size() + 2))
300    , len_(static_cast<off_t>(value.size()))
301    , f_(name)
302{
303    //BOOST_ASSERT(name == field::unknown ||
304    //    iequals(sname, to_string(name)));
305    char* p = data();
306    p[off_-2] = ':';
307    p[off_-1] = ' ';
308    p[off_ + len_] = '\r';
309    p[off_ + len_ + 1] = '\n';
310    sname.copy(p, sname.size());
311    value.copy(p + off_, value.size());
312}
313
314template<class Allocator>
315field
316basic_fields<Allocator>::
317value_type::
318name() const
319{
320    return f_;
321}
322
323template<class Allocator>
324string_view const
325basic_fields<Allocator>::
326value_type::
327name_string() const
328{
329    return {data(),
330        static_cast<std::size_t>(off_ - 2)};
331}
332
333template<class Allocator>
334string_view const
335basic_fields<Allocator>::
336value_type::
337value() const
338{
339    return {data() + off_,
340        static_cast<std::size_t>(len_)};
341}
342
343template<class Allocator>
344basic_fields<Allocator>::
345element::
346element(field name,
347    string_view sname, string_view value)
348    : value_type(name, sname, value)
349{
350}
351
352//------------------------------------------------------------------------------
353
354template<class Allocator>
355basic_fields<Allocator>::
356~basic_fields()
357{
358    delete_list();
359    realloc_string(method_, {});
360    realloc_string(
361        target_or_reason_, {});
362}
363
364template<class Allocator>
365basic_fields<Allocator>::
366basic_fields(Allocator const& alloc) noexcept
367    : boost::empty_value<Allocator>(boost::empty_init_t(), alloc)
368{
369}
370
371template<class Allocator>
372basic_fields<Allocator>::
373basic_fields(basic_fields&& other) noexcept
374    : boost::empty_value<Allocator>(boost::empty_init_t(),
375        std::move(other.get()))
376    , set_(std::move(other.set_))
377    , list_(std::move(other.list_))
378    , method_(boost::exchange(other.method_, {}))
379    , target_or_reason_(boost::exchange(other.target_or_reason_, {}))
380{
381}
382
383template<class Allocator>
384basic_fields<Allocator>::
385basic_fields(basic_fields&& other, Allocator const& alloc)
386    : boost::empty_value<Allocator>(boost::empty_init_t(), alloc)
387{
388    if(this->get() != other.get())
389    {
390        copy_all(other);
391        other.clear_all();
392    }
393    else
394    {
395        set_ = std::move(other.set_);
396        list_ = std::move(other.list_);
397        method_ = other.method_;
398        target_or_reason_ = other.target_or_reason_;
399    }
400}
401
402template<class Allocator>
403basic_fields<Allocator>::
404basic_fields(basic_fields const& other)
405    : boost::empty_value<Allocator>(boost::empty_init_t(), alloc_traits::
406        select_on_container_copy_construction(other.get()))
407{
408    copy_all(other);
409}
410
411template<class Allocator>
412basic_fields<Allocator>::
413basic_fields(basic_fields const& other,
414        Allocator const& alloc)
415    : boost::empty_value<Allocator>(boost::empty_init_t(), alloc)
416{
417    copy_all(other);
418}
419
420template<class Allocator>
421template<class OtherAlloc>
422basic_fields<Allocator>::
423basic_fields(basic_fields<OtherAlloc> const& other)
424{
425    copy_all(other);
426}
427
428template<class Allocator>
429template<class OtherAlloc>
430basic_fields<Allocator>::
431basic_fields(basic_fields<OtherAlloc> const& other,
432        Allocator const& alloc)
433    : boost::empty_value<Allocator>(boost::empty_init_t(), alloc)
434{
435    copy_all(other);
436}
437
438template<class Allocator>
439auto
440basic_fields<Allocator>::
441operator=(basic_fields&& other) noexcept(
442    alloc_traits::propagate_on_container_move_assignment::value)
443      -> basic_fields&
444{
445    static_assert(is_nothrow_move_assignable<Allocator>::value,
446        "Allocator must be noexcept assignable.");
447    if(this == &other)
448        return *this;
449    move_assign(other, std::integral_constant<bool,
450        alloc_traits:: propagate_on_container_move_assignment::value>{});
451    return *this;
452}
453
454template<class Allocator>
455auto
456basic_fields<Allocator>::
457operator=(basic_fields const& other) ->
458    basic_fields&
459{
460    copy_assign(other, std::integral_constant<bool,
461        alloc_traits::propagate_on_container_copy_assignment::value>{});
462    return *this;
463}
464
465template<class Allocator>
466template<class OtherAlloc>
467auto
468basic_fields<Allocator>::
469operator=(basic_fields<OtherAlloc> const& other) ->
470    basic_fields&
471{
472    clear_all();
473    copy_all(other);
474    return *this;
475}
476
477//------------------------------------------------------------------------------
478//
479// Element access
480//
481//------------------------------------------------------------------------------
482
483template<class Allocator>
484string_view const
485basic_fields<Allocator>::
486at(field name) const
487{
488    BOOST_ASSERT(name != field::unknown);
489    auto const it = find(name);
490    if(it == end())
491        BOOST_THROW_EXCEPTION(std::out_of_range{
492            "field not found"});
493    return it->value();
494}
495
496template<class Allocator>
497string_view const
498basic_fields<Allocator>::
499at(string_view name) const
500{
501    auto const it = find(name);
502    if(it == end())
503        BOOST_THROW_EXCEPTION(std::out_of_range{
504            "field not found"});
505    return it->value();
506}
507
508template<class Allocator>
509string_view const
510basic_fields<Allocator>::
511operator[](field name) const
512{
513    BOOST_ASSERT(name != field::unknown);
514    auto const it = find(name);
515    if(it == end())
516        return {};
517    return it->value();
518}
519
520template<class Allocator>
521string_view const
522basic_fields<Allocator>::
523operator[](string_view name) const
524{
525    auto const it = find(name);
526    if(it == end())
527        return {};
528    return it->value();
529}
530
531//------------------------------------------------------------------------------
532//
533// Modifiers
534//
535//------------------------------------------------------------------------------
536
537template<class Allocator>
538void
539basic_fields<Allocator>::
540clear()
541{
542    delete_list();
543    set_.clear();
544    list_.clear();
545}
546
547template<class Allocator>
548inline
549void
550basic_fields<Allocator>::
551insert(field name, string_param const& value)
552{
553    BOOST_ASSERT(name != field::unknown);
554    insert(name, to_string(name), value);
555}
556
557template<class Allocator>
558void
559basic_fields<Allocator>::
560insert(string_view sname, string_param const& value)
561{
562    auto const name =
563        string_to_field(sname);
564    insert(name, sname, value);
565}
566
567template<class Allocator>
568void
569basic_fields<Allocator>::
570insert(field name,
571    string_view sname, string_param const& value)
572{
573    auto& e = new_element(name, sname,
574        static_cast<string_view>(value));
575    auto const before =
576        set_.upper_bound(sname, key_compare{});
577    if(before == set_.begin())
578    {
579        BOOST_ASSERT(count(sname) == 0);
580        set_.insert_before(before, e);
581        list_.push_back(e);
582        return;
583    }
584    auto const last = std::prev(before);
585    // VFALCO is it worth comparing `field name` first?
586    if(! iequals(sname, last->name_string()))
587    {
588        BOOST_ASSERT(count(sname) == 0);
589        set_.insert_before(before, e);
590        list_.push_back(e);
591        return;
592    }
593    // keep duplicate fields together in the list
594    set_.insert_before(before, e);
595    list_.insert(++list_.iterator_to(*last), e);
596}
597
598template<class Allocator>
599void
600basic_fields<Allocator>::
601set(field name, string_param const& value)
602{
603    BOOST_ASSERT(name != field::unknown);
604    set_element(new_element(name, to_string(name),
605        static_cast<string_view>(value)));
606}
607
608template<class Allocator>
609void
610basic_fields<Allocator>::
611set(string_view sname, string_param const& value)
612{
613    set_element(new_element(
614        string_to_field(sname), sname,
615            static_cast<string_view>(value)));
616}
617
618template<class Allocator>
619auto
620basic_fields<Allocator>::
621erase(const_iterator pos) ->
622    const_iterator
623{
624    auto next = pos;
625    auto& e = *next++;
626    set_.erase(e);
627    list_.erase(pos);
628    delete_element(const_cast<element&>(e));
629    return next;
630}
631
632template<class Allocator>
633std::size_t
634basic_fields<Allocator>::
635erase(field name)
636{
637    BOOST_ASSERT(name != field::unknown);
638    return erase(to_string(name));
639}
640
641template<class Allocator>
642std::size_t
643basic_fields<Allocator>::
644erase(string_view name)
645{
646    std::size_t n =0;
647    set_.erase_and_dispose(name, key_compare{},
648        [&](element* e)
649        {
650            ++n;
651            list_.erase(list_.iterator_to(*e));
652            delete_element(*e);
653        });
654    return n;
655}
656
657template<class Allocator>
658void
659basic_fields<Allocator>::
660swap(basic_fields<Allocator>& other)
661{
662    swap(other, std::integral_constant<bool,
663        alloc_traits::propagate_on_container_swap::value>{});
664}
665
666template<class Allocator>
667void
668swap(
669    basic_fields<Allocator>& lhs,
670    basic_fields<Allocator>& rhs)
671{
672    lhs.swap(rhs);
673}
674
675//------------------------------------------------------------------------------
676//
677// Lookup
678//
679//------------------------------------------------------------------------------
680
681template<class Allocator>
682inline
683std::size_t
684basic_fields<Allocator>::
685count(field name) const
686{
687    BOOST_ASSERT(name != field::unknown);
688    return count(to_string(name));
689}
690
691template<class Allocator>
692std::size_t
693basic_fields<Allocator>::
694count(string_view name) const
695{
696    return set_.count(name, key_compare{});
697}
698
699template<class Allocator>
700inline
701auto
702basic_fields<Allocator>::
703find(field name) const ->
704    const_iterator
705{
706    BOOST_ASSERT(name != field::unknown);
707    return find(to_string(name));
708}
709
710template<class Allocator>
711auto
712basic_fields<Allocator>::
713find(string_view name) const ->
714    const_iterator
715{
716    auto const it = set_.find(
717        name, key_compare{});
718    if(it == set_.end())
719        return list_.end();
720    return list_.iterator_to(*it);
721}
722
723template<class Allocator>
724inline
725auto
726basic_fields<Allocator>::
727equal_range(field name) const ->
728    std::pair<const_iterator, const_iterator>
729{
730    BOOST_ASSERT(name != field::unknown);
731    return equal_range(to_string(name));
732}
733
734template<class Allocator>
735auto
736basic_fields<Allocator>::
737equal_range(string_view name) const ->
738    std::pair<const_iterator, const_iterator>
739{
740    auto result =
741        set_.equal_range(name, key_compare{});
742    if(result.first == result.second)
743        return {list_.end(), list_.end()};
744    return {
745        list_.iterator_to(*result.first),
746        ++list_.iterator_to(*(--result.second))};
747}
748
749//------------------------------------------------------------------------------
750
751namespace detail {
752
753// Filter a token list
754//
755template<class String, class Pred>
756void
757filter_token_list(
758    String& s,
759    string_view value,
760    Pred&& pred)
761{
762    token_list te{value};
763    auto it = te.begin();
764    auto last = te.end();
765    if(it == last)
766        return;
767    while(pred(*it))
768        if(++it == last)
769            return;
770    s.append(it->data(), it->size());
771    while(++it != last)
772    {
773        if(! pred(*it))
774        {
775            s.append(", ");
776            s.append(it->data(), it->size());
777        }
778    }
779}
780
781// Filter the last item in a token list
782template<class String, class Pred>
783void
784filter_token_list_last(
785    String& s,
786    string_view value,
787    Pred&& pred)
788{
789    token_list te{value};
790    if(te.begin() != te.end())
791    {
792        auto it = te.begin();
793        auto next = std::next(it);
794        if(next == te.end())
795        {
796            if(! pred(*it))
797                s.append(it->data(), it->size());
798            return;
799        }
800        s.append(it->data(), it->size());
801        for(;;)
802        {
803            it = next;
804            next = std::next(it);
805            if(next == te.end())
806            {
807                if(! pred(*it))
808                {
809                    s.append(", ");
810                    s.append(it->data(), it->size());
811                }
812                return;
813            }
814            s.append(", ");
815            s.append(it->data(), it->size());
816        }
817    }
818}
819
820template<class String>
821void
822keep_alive_impl(
823    String& s, string_view value,
824    unsigned version, bool keep_alive)
825{
826    if(version < 11)
827    {
828        if(keep_alive)
829        {
830            // remove close
831            filter_token_list(s, value,
832                [](string_view s)
833                {
834                    return iequals(s, "close");
835                });
836            // add keep-alive
837            if(s.empty())
838                s.append("keep-alive");
839            else if(! token_list{value}.exists("keep-alive"))
840                s.append(", keep-alive");
841        }
842        else
843        {
844            // remove close and keep-alive
845            filter_token_list(s, value,
846                [](string_view s)
847                {
848                    return
849                        iequals(s, "close") ||
850                        iequals(s, "keep-alive");
851                });
852        }
853    }
854    else
855    {
856        if(keep_alive)
857        {
858            // remove close and keep-alive
859            filter_token_list(s, value,
860                [](string_view s)
861                {
862                    return
863                        iequals(s, "close") ||
864                        iequals(s, "keep-alive");
865                });
866        }
867        else
868        {
869            // remove keep-alive
870            filter_token_list(s, value,
871                [](string_view s)
872                {
873                    return iequals(s, "keep-alive");
874                });
875            // add close
876            if(s.empty())
877                s.append("close");
878            else if(! token_list{value}.exists("close"))
879                s.append(", close");
880        }
881    }
882}
883
884} // detail
885
886//------------------------------------------------------------------------------
887
888// Fields
889
890template<class Allocator>
891inline
892string_view
893basic_fields<Allocator>::
894get_method_impl() const
895{
896    return method_;
897}
898
899template<class Allocator>
900inline
901string_view
902basic_fields<Allocator>::
903get_target_impl() const
904{
905    if(target_or_reason_.empty())
906        return target_or_reason_;
907    return {
908        target_or_reason_.data() + 1,
909        target_or_reason_.size() - 1};
910}
911
912template<class Allocator>
913inline
914string_view
915basic_fields<Allocator>::
916get_reason_impl() const
917{
918    return target_or_reason_;
919}
920
921template<class Allocator>
922bool
923basic_fields<Allocator>::
924get_chunked_impl() const
925{
926    auto const te = token_list{
927        (*this)[field::transfer_encoding]};
928    for(auto it = te.begin(); it != te.end();)
929    {
930        auto const next = std::next(it);
931        if(next == te.end())
932            return iequals(*it, "chunked");
933        it = next;
934    }
935    return false;
936}
937
938template<class Allocator>
939bool
940basic_fields<Allocator>::
941get_keep_alive_impl(unsigned version) const
942{
943    auto const it = find(field::connection);
944    if(version < 11)
945    {
946        if(it == end())
947            return false;
948        return token_list{
949            it->value()}.exists("keep-alive");
950    }
951    if(it == end())
952        return true;
953    return ! token_list{
954        it->value()}.exists("close");
955}
956
957template<class Allocator>
958bool
959basic_fields<Allocator>::
960has_content_length_impl() const
961{
962    return count(field::content_length) > 0;
963}
964
965template<class Allocator>
966inline
967void
968basic_fields<Allocator>::
969set_method_impl(string_view s)
970{
971    realloc_string(method_, s);
972}
973
974template<class Allocator>
975inline
976void
977basic_fields<Allocator>::
978set_target_impl(string_view s)
979{
980    realloc_target(
981        target_or_reason_, s);
982}
983
984template<class Allocator>
985inline
986void
987basic_fields<Allocator>::
988set_reason_impl(string_view s)
989{
990    realloc_string(
991        target_or_reason_, s);
992}
993
994template<class Allocator>
995void
996basic_fields<Allocator>::
997set_chunked_impl(bool value)
998{
999    auto it = find(field::transfer_encoding);
1000    if(value)
1001    {
1002        // append "chunked"
1003        if(it == end())
1004        {
1005            set(field::transfer_encoding, "chunked");
1006            return;
1007        }
1008        auto const te = token_list{it->value()};
1009        for(auto itt = te.begin();;)
1010        {
1011            auto const next = std::next(itt);
1012            if(next == te.end())
1013            {
1014                if(iequals(*itt, "chunked"))
1015                    return; // already set
1016                break;
1017            }
1018            itt = next;
1019        }
1020        static_string<max_static_buffer> buf;
1021        if(it->value().size() <= buf.size() + 9)
1022        {
1023            buf.append(it->value().data(), it->value().size());
1024            buf.append(", chunked", 9);
1025            set(field::transfer_encoding, buf);
1026        }
1027        else
1028        {
1029        #ifdef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
1030            // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
1031            std::string s;
1032        #else
1033            using A =
1034                typename beast::detail::allocator_traits<
1035                    Allocator>::template rebind_alloc<char>;
1036            std::basic_string<
1037                char,
1038                std::char_traits<char>,
1039                A> s{A{this->get()}};
1040        #endif
1041            s.reserve(it->value().size() + 9);
1042            s.append(it->value().data(), it->value().size());
1043            s.append(", chunked", 9);
1044            set(field::transfer_encoding, s);
1045        }
1046        return;
1047    }
1048    // filter "chunked"
1049    if(it == end())
1050        return;
1051    try
1052    {
1053        static_string<max_static_buffer> buf;
1054        detail::filter_token_list_last(buf, it->value(),
1055            [](string_view s)
1056            {
1057                return iequals(s, "chunked");
1058            });
1059        if(! buf.empty())
1060            set(field::transfer_encoding, buf);
1061        else
1062            erase(field::transfer_encoding);
1063    }
1064    catch(std::length_error const&)
1065    {
1066    #ifdef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
1067        // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
1068        std::string s;
1069    #else
1070        using A =
1071            typename beast::detail::allocator_traits<
1072                Allocator>::template rebind_alloc<char>;
1073        std::basic_string<
1074            char,
1075            std::char_traits<char>,
1076            A> s{A{this->get()}};
1077    #endif
1078        s.reserve(it->value().size());
1079        detail::filter_token_list_last(s, it->value(),
1080            [](string_view s)
1081            {
1082                return iequals(s, "chunked");
1083            });
1084        if(! s.empty())
1085            set(field::transfer_encoding, s);
1086        else
1087            erase(field::transfer_encoding);
1088    }
1089}
1090
1091template<class Allocator>
1092void
1093basic_fields<Allocator>::
1094set_content_length_impl(
1095    boost::optional<std::uint64_t> const& value)
1096{
1097    if(! value)
1098        erase(field::content_length);
1099    else
1100        set(field::content_length, *value);
1101}
1102
1103template<class Allocator>
1104void
1105basic_fields<Allocator>::
1106set_keep_alive_impl(
1107    unsigned version, bool keep_alive)
1108{
1109    // VFALCO What about Proxy-Connection ?
1110    auto const value = (*this)[field::connection];
1111    try
1112    {
1113        static_string<max_static_buffer> buf;
1114        detail::keep_alive_impl(
1115            buf, value, version, keep_alive);
1116        if(buf.empty())
1117            erase(field::connection);
1118        else
1119            set(field::connection, buf);
1120    }
1121    catch(std::length_error const&)
1122    {
1123    #ifdef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
1124        // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
1125        std::string s;
1126    #else
1127        using A =
1128            typename beast::detail::allocator_traits<
1129                Allocator>::template rebind_alloc<char>;
1130        std::basic_string<
1131            char,
1132            std::char_traits<char>,
1133            A> s{A{this->get()}};
1134    #endif
1135        s.reserve(value.size());
1136        detail::keep_alive_impl(
1137            s, value, version, keep_alive);
1138        if(s.empty())
1139            erase(field::connection);
1140        else
1141            set(field::connection, s);
1142    }
1143}
1144
1145//------------------------------------------------------------------------------
1146
1147template<class Allocator>
1148auto
1149basic_fields<Allocator>::
1150new_element(field name,
1151    string_view sname, string_view value) ->
1152        element&
1153{
1154    if(sname.size() + 2 >
1155            (std::numeric_limits<off_t>::max)())
1156        BOOST_THROW_EXCEPTION(std::length_error{
1157            "field name too large"});
1158    if(value.size() + 2 >
1159            (std::numeric_limits<off_t>::max)())
1160        BOOST_THROW_EXCEPTION(std::length_error{
1161            "field value too large"});
1162    value = detail::trim(value);
1163    std::uint16_t const off =
1164        static_cast<off_t>(sname.size() + 2);
1165    std::uint16_t const len =
1166        static_cast<off_t>(value.size());
1167    auto a = rebind_type{this->get()};
1168    auto const p = alloc_traits::allocate(a,
1169        (sizeof(element) + off + len + 2 + sizeof(align_type) - 1) /
1170            sizeof(align_type));
1171    return *(new(p) element(name, sname, value));
1172}
1173
1174template<class Allocator>
1175void
1176basic_fields<Allocator>::
1177delete_element(element& e)
1178{
1179    auto a = rebind_type{this->get()};
1180    auto const n =
1181        (sizeof(element) + e.off_ + e.len_ + 2 + sizeof(align_type) - 1) /
1182            sizeof(align_type);
1183    e.~element();
1184    alloc_traits::deallocate(a, &e, n);
1185        //reinterpret_cast<align_type*>(&e), n);
1186}
1187
1188template<class Allocator>
1189void
1190basic_fields<Allocator>::
1191set_element(element& e)
1192{
1193    auto it = set_.lower_bound(
1194        e.name_string(), key_compare{});
1195    if(it == set_.end() || ! iequals(
1196        e.name_string(), it->name_string()))
1197    {
1198        set_.insert_before(it, e);
1199        list_.push_back(e);
1200        return;
1201    }
1202    for(;;)
1203    {
1204        auto next = it;
1205        ++next;
1206        set_.erase(it);
1207        list_.erase(list_.iterator_to(*it));
1208        delete_element(*it);
1209        it = next;
1210        if(it == set_.end() ||
1211            ! iequals(e.name_string(), it->name_string()))
1212            break;
1213    }
1214    set_.insert_before(it, e);
1215    list_.push_back(e);
1216}
1217
1218template<class Allocator>
1219void
1220basic_fields<Allocator>::
1221realloc_string(string_view& dest, string_view s)
1222{
1223    if(dest.empty() && s.empty())
1224        return;
1225    auto a = typename beast::detail::allocator_traits<
1226        Allocator>::template rebind_alloc<
1227            char>(this->get());
1228    char* p = nullptr;
1229    if(! s.empty())
1230    {
1231        p = a.allocate(s.size());
1232        s.copy(p, s.size());
1233    }
1234    if(! dest.empty())
1235        a.deallocate(const_cast<char*>(
1236            dest.data()), dest.size());
1237    if(p)
1238        dest = {p, s.size()};
1239    else
1240        dest = {};
1241}
1242
1243template<class Allocator>
1244void
1245basic_fields<Allocator>::
1246realloc_target(
1247    string_view& dest, string_view s)
1248{
1249    // The target string are stored with an
1250    // extra space at the beginning to help
1251    // the writer class.
1252    if(dest.empty() && s.empty())
1253        return;
1254    auto a = typename beast::detail::allocator_traits<
1255        Allocator>::template rebind_alloc<
1256            char>(this->get());
1257    char* p = nullptr;
1258    if(! s.empty())
1259    {
1260        p = a.allocate(1 + s.size());
1261        p[0] = ' ';
1262        s.copy(p + 1, s.size());
1263    }
1264    if(! dest.empty())
1265        a.deallocate(const_cast<char*>(
1266            dest.data()), dest.size());
1267    if(p)
1268        dest = {p, 1 + s.size()};
1269    else
1270        dest = {};
1271}
1272
1273template<class Allocator>
1274template<class OtherAlloc>
1275void
1276basic_fields<Allocator>::
1277copy_all(basic_fields<OtherAlloc> const& other)
1278{
1279    for(auto const& e : other.list_)
1280        insert(e.name(), e.name_string(), e.value());
1281    realloc_string(method_, other.method_);
1282    realloc_string(target_or_reason_,
1283        other.target_or_reason_);
1284}
1285
1286template<class Allocator>
1287void
1288basic_fields<Allocator>::
1289clear_all()
1290{
1291    clear();
1292    realloc_string(method_, {});
1293    realloc_string(target_or_reason_, {});
1294}
1295
1296template<class Allocator>
1297void
1298basic_fields<Allocator>::
1299delete_list()
1300{
1301    for(auto it = list_.begin(); it != list_.end();)
1302        delete_element(*it++);
1303}
1304
1305//------------------------------------------------------------------------------
1306
1307template<class Allocator>
1308inline
1309void
1310basic_fields<Allocator>::
1311move_assign(basic_fields& other, std::true_type)
1312{
1313    clear_all();
1314    set_ = std::move(other.set_);
1315    list_ = std::move(other.list_);
1316    method_ = other.method_;
1317    target_or_reason_ = other.target_or_reason_;
1318    other.method_ = {};
1319    other.target_or_reason_ = {};
1320    this->get() = other.get();
1321}
1322
1323template<class Allocator>
1324inline
1325void
1326basic_fields<Allocator>::
1327move_assign(basic_fields& other, std::false_type)
1328{
1329    clear_all();
1330    if(this->get() != other.get())
1331    {
1332        copy_all(other);
1333        other.clear_all();
1334    }
1335    else
1336    {
1337        set_ = std::move(other.set_);
1338        list_ = std::move(other.list_);
1339        method_ = other.method_;
1340        target_or_reason_ = other.target_or_reason_;
1341        other.method_ = {};
1342        other.target_or_reason_ = {};
1343    }
1344}
1345
1346template<class Allocator>
1347inline
1348void
1349basic_fields<Allocator>::
1350copy_assign(basic_fields const& other, std::true_type)
1351{
1352    clear_all();
1353    this->get() = other.get();
1354    copy_all(other);
1355}
1356
1357template<class Allocator>
1358inline
1359void
1360basic_fields<Allocator>::
1361copy_assign(basic_fields const& other, std::false_type)
1362{
1363    clear_all();
1364    copy_all(other);
1365}
1366
1367template<class Allocator>
1368inline
1369void
1370basic_fields<Allocator>::
1371swap(basic_fields& other, std::true_type)
1372{
1373    using std::swap;
1374    swap(this->get(), other.get());
1375    swap(set_, other.set_);
1376    swap(list_, other.list_);
1377    swap(method_, other.method_);
1378    swap(target_or_reason_, other.target_or_reason_);
1379}
1380
1381template<class Allocator>
1382inline
1383void
1384basic_fields<Allocator>::
1385swap(basic_fields& other, std::false_type)
1386{
1387    BOOST_ASSERT(this->get() == other.get());
1388    using std::swap;
1389    swap(set_, other.set_);
1390    swap(list_, other.list_);
1391    swap(method_, other.method_);
1392    swap(target_or_reason_, other.target_or_reason_);
1393}
1394
1395} // http
1396} // beast
1397} // boost
1398
1399#endif
1400