diff --git a/Builds/VisualStudio2013/RippleD.vcxproj b/Builds/VisualStudio2013/RippleD.vcxproj
index af4de2fe2..09a8813ed 100644
--- a/Builds/VisualStudio2013/RippleD.vcxproj
+++ b/Builds/VisualStudio2013/RippleD.vcxproj
@@ -318,6 +318,8 @@
+
+
@@ -355,6 +357,9 @@
+
+ True
+
True
diff --git a/Builds/VisualStudio2013/RippleD.vcxproj.filters b/Builds/VisualStudio2013/RippleD.vcxproj.filters
index e42e4d040..ab627a2f3 100644
--- a/Builds/VisualStudio2013/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2013/RippleD.vcxproj.filters
@@ -834,6 +834,9 @@
beast\http
+
+ beast\http
+
beast\http
@@ -879,6 +882,9 @@
beast\http
+
+ beast\http\tests
+
beast\http\tests
diff --git a/src/beast/beast/http/HTTP.unity.cpp b/src/beast/beast/http/HTTP.unity.cpp
index 750ba7fcd..c1de90944 100644
--- a/src/beast/beast/http/HTTP.unity.cpp
+++ b/src/beast/beast/http/HTTP.unity.cpp
@@ -27,6 +27,7 @@
#include
#include
+#include
#include
#include
#include
diff --git a/src/beast/beast/http/chunk_encode.h b/src/beast/beast/http/chunk_encode.h
new file mode 100644
index 000000000..0f74cf900
--- /dev/null
+++ b/src/beast/beast/http/chunk_encode.h
@@ -0,0 +1,284 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#ifndef BEAST_HTTP_CHUNK_ENCODE_H_INCLUDED
+#define BEAST_HTTP_CHUNK_ENCODE_H_INCLUDED
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include //
+
+namespace beast {
+namespace http {
+
+namespace detail {
+
+template
+class chunk_encoded_buffers
+{
+private:
+ using const_buffer = boost::asio::const_buffer;
+
+ Buffers buffers_;
+ const_buffer head_;
+ const_buffer tail_;
+
+ // Storage for the longest hex string we might need, plus delimiters.
+ std::array data_;
+
+public:
+ using value_type = boost::asio::const_buffer;
+
+ class const_iterator;
+
+ chunk_encoded_buffers() = delete;
+ chunk_encoded_buffers (chunk_encoded_buffers const&) = default;
+ chunk_encoded_buffers& operator= (chunk_encoded_buffers const&) = default;
+
+ chunk_encoded_buffers (Buffers const& buffers, bool final_chunk);
+
+ const_iterator
+ begin() const
+ {
+ return const_iterator(*this, false);
+ }
+
+ const_iterator
+ end() const
+ {
+ return const_iterator(*this, true);
+ }
+
+private:
+ // Unchecked conversion of unsigned to hex string
+ template
+ std::enable_if_t::value, OutIter>
+ to_hex(OutIter const first, OutIter const last, Unsigned n);
+};
+
+template
+class chunk_encoded_buffers::const_iterator
+ : public std::iterator
+{
+private:
+ using iterator = typename Buffers::const_iterator;
+ enum class Where { head, input, end };
+ chunk_encoded_buffers const* buffers_;
+ Where where_;
+ iterator iter_;
+
+public:
+ const_iterator();
+ const_iterator (const_iterator const&) = default;
+ const_iterator& operator= (const_iterator const&) = default;
+ bool operator== (const_iterator const& other) const;
+ bool operator!= (const_iterator const& other) const;
+ const_iterator& operator++();
+ const_iterator& operator--();
+ const_iterator operator++(int) const;
+ const_iterator operator--(int) const;
+ const_buffer operator*() const;
+
+private:
+ friend class chunk_encoded_buffers;
+ const_iterator(chunk_encoded_buffers const& buffers, bool past_the_end);
+};
+
+//------------------------------------------------------------------------------
+
+template
+chunk_encoded_buffers::chunk_encoded_buffers (
+ Buffers const& buffers, bool final_chunk)
+ : buffers_(buffers)
+{
+ auto const size = boost::asio::buffer_size(buffers);
+ data_[data_.size() - 2] = '\r';
+ data_[data_.size() - 1] = '\n';
+ auto pos = to_hex(data_.begin(), data_.end() - 2, size);
+ head_ = const_buffer(&*pos,
+ std::distance(pos, data_.end()));
+ if (size > 0 && final_chunk)
+ tail_ = const_buffer("\r\n0\r\n\r\n", 7);
+ else
+ tail_ = const_buffer("\r\n", 2);
+}
+
+template
+template
+std::enable_if_t::value, OutIter>
+chunk_encoded_buffers::to_hex(
+ OutIter const first, OutIter const last, Unsigned n)
+{
+ assert(first != last);
+ OutIter iter = last;
+ if(n == 0)
+ {
+ *--iter = '0';
+ return iter;
+ }
+ while(n)
+ {
+ assert(iter != first);
+ *--iter = "0123456789abcdef"[n&0xf];
+ n>>=4;
+ }
+ return iter;
+}
+
+template
+chunk_encoded_buffers::const_iterator::const_iterator()
+ : where_(Where::end)
+ , buffers_(nullptr)
+{
+}
+
+template
+bool
+chunk_encoded_buffers::const_iterator::operator==(
+ const_iterator const& other) const
+{
+ return buffers_ == other.buffers_ &&
+ where_ == other.where_ && iter_ == other.iter_;
+}
+
+template
+bool
+chunk_encoded_buffers::const_iterator::operator!=(
+ const_iterator const& other) const
+{
+ return buffers_ != other.buffers_ ||
+ where_ != other.where_ || iter_ != other.iter_;
+}
+
+template
+auto
+chunk_encoded_buffers::const_iterator::operator++() ->
+ const_iterator&
+{
+ assert(buffers_);
+ assert(where_ != Where::end);
+ if (where_ == Where::head)
+ where_ = Where::input;
+ else if (iter_ != buffers_->buffers_.end())
+ ++iter_;
+ else
+ where_ = Where::end;
+ return *this;
+}
+
+template
+auto
+chunk_encoded_buffers::const_iterator::operator--() ->
+ const_iterator&
+{
+ assert(buffers_);
+ assert(where_ != Where::begin);
+ if (where_ == Where::end)
+ where_ = Where::input;
+ else if (iter_ != buffers_->buffers_.begin())
+ --iter_;
+ else
+ where_ = Where::head;
+ return *this;
+}
+
+template
+auto
+chunk_encoded_buffers::const_iterator::operator++(int) const ->
+ const_iterator
+{
+ auto iter = *this;
+ ++iter;
+ return iter;
+}
+
+template
+auto
+chunk_encoded_buffers::const_iterator::operator--(int) const ->
+ const_iterator
+{
+ auto iter = *this;
+ --iter;
+ return iter;
+}
+
+template
+auto
+chunk_encoded_buffers::const_iterator::operator*() const ->
+ const_buffer
+{
+ assert(buffers_);
+ assert(where_ != Where::end);
+ if (where_ == Where::head)
+ return buffers_->head_;
+ if (iter_ != buffers_->buffers_.end())
+ return *iter_;
+ return buffers_->tail_;
+}
+
+template
+chunk_encoded_buffers::const_iterator::const_iterator(
+ chunk_encoded_buffers const& buffers, bool past_the_end)
+ : buffers_(&buffers)
+ , where_(past_the_end ? Where::end : Where::head)
+ , iter_(past_the_end ? buffers_->buffers_.end() :
+ buffers_->buffers_.begin())
+{
+}
+
+}
+
+/** Returns a chunk-encoded BufferSequence.
+
+ See:
+ http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1
+
+ @tparam Buffers A type meeting the requirements of BufferSequence.
+ @param buffers The input buffer sequence.
+ @param final_chunk `true` If this should include a final-chunk.
+ @return A chunk-encoded ConstBufferSeqeunce representing the input.
+*/
+/** @{ */
+template
+detail::chunk_encoded_buffers
+chunk_encode (Buffers const& buffers,
+ bool final_chunk = false)
+{
+ return detail::chunk_encoded_buffers<
+ Buffers>(buffers, final_chunk);
+}
+
+// Returns a chunked encoding final chunk.
+inline
+boost::asio::const_buffers_1
+chunk_encode_final()
+{
+ return boost::asio::const_buffers_1(
+ "0\r\n\r\n", 5);
+}
+/** @} */
+
+} // http
+} // beast
+
+#endif
diff --git a/src/beast/beast/http/tests/chunked_encoder.test.cpp b/src/beast/beast/http/tests/chunked_encoder.test.cpp
new file mode 100644
index 000000000..81ea77d8d
--- /dev/null
+++ b/src/beast/beast/http/tests/chunked_encoder.test.cpp
@@ -0,0 +1,151 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of Beast: https://github.com/vinniefalco/Beast
+ Copyright 2013, Vinnie Falco
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#include
+#include
+#include
+
+namespace beast {
+namespace http {
+
+class chunk_encode_test : public unit_test::suite
+{
+public:
+ // Convert CR LF to printables for display
+ static
+ std::string
+ encode (std::string const& s)
+ {
+ std::string result;
+ for(auto const c : s)
+ {
+ if (c == '\r')
+ result += "\\r";
+ else if (c== '\n')
+ result += "\\n";
+ else
+ result += c;
+ }
+ return result;
+ }
+
+ // Print the contents of a ConstBufferSequence to the log
+ template
+ static
+ void
+ print (ConstBufferSequence const& buffers, Log log)
+ {
+ for(auto const& buf : buffers)
+ log << encode (std::string(
+ boost::asio::buffer_cast(buf),
+ boost::asio::buffer_size(buf)));
+ }
+
+ // Convert a ConstBufferSequence to a string
+ template
+ static
+ std::string
+ buffer_to_string (ConstBufferSequence const& b)
+ {
+ std::string s;
+ auto const n = boost::asio::buffer_size(b);
+ s.resize(n);
+ boost::asio::buffer_copy(
+ boost::asio::buffer(&s[0], n), b);
+ return s;
+ }
+
+ // Append a ConstBufferSequence to an existing string
+ template
+ static
+ void
+ buffer_append (std::string& s, ConstBufferSequence const& b)
+ {
+ s += buffer_to_string(b);
+ }
+
+ // Convert the input sequence of the stream to a
+ // chunked-encoded string. The input sequence is consumed.
+ template
+ static
+ std::string
+ streambuf_to_string (Streambuf& sb,
+ bool final_chunk = false)
+ {
+ std::string s;
+ buffer_append(s, chunk_encode(sb.data(), final_chunk));
+ return s;
+ }
+
+ // Check an input against the correct chunk encoded version
+ void
+ check (std::string const& in, std::string const& answer,
+ bool final_chunk = true)
+ {
+ asio::streambuf sb(3);
+ sb << in;
+ auto const out = streambuf_to_string (sb, final_chunk);
+ if (! expect (out == answer))
+ log << "expected\n" << encode(answer) <<
+ "\ngot\n" << encode(out);
+ }
+
+ void testStreambuf()
+ {
+ asio::streambuf sb(3);
+ std::string const s =
+ "0123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789";
+ sb << s;
+ expect(buffer_to_string(sb.data()) == s);
+ }
+
+ void
+ testEncoder()
+ {
+ check("", "0\r\n\r\n");
+ check("x", "1\r\nx\r\n0\r\n\r\n");
+ check("abcd", "4\r\nabcd\r\n0\r\n\r\n");
+ check("x", "1\r\nx\r\n", false);
+ check(
+ "0123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789"
+ ,
+ "d2\r\n"
+ "0123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789"
+ "\r\n"
+ "0\r\n\r\n");
+ }
+
+ void
+ run()
+ {
+ testStreambuf();
+ testEncoder();
+ }
+};
+
+BEAST_DEFINE_TESTSUITE(chunk_encode,http,beast);
+
+}
+}