//
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//

#include <boost/beast/core.hpp>
#include <boost/asio.hpp>
#include <boost/config.hpp>
#include <iostream>
#include <thread>

using namespace boost::beast;

//[http_snippet_1

#include <boost/beast/http.hpp>
using namespace boost::beast::http;

//]

namespace doc_http_snippets {

//[http_snippet_17
// This function returns the buffer containing the next chunk body
boost::asio::const_buffer get_next_chunk_body();
//]

boost::asio::const_buffer get_next_chunk_body()
{
    return {nullptr, 0};
}

void fxx() {

    boost::asio::io_context ioc;
    auto work = boost::asio::make_work_guard(ioc);
    std::thread t{[&](){ ioc.run(); }};
    boost::asio::ip::tcp::socket sock{ioc};

{
//[http_snippet_2

    request<empty_body> req;
    req.version(11);   // HTTP/1.1
    req.method(verb::get);
    req.target("/index.htm");
    req.set(field::accept, "text/html");
    req.set(field::user_agent, "Beast");

//]
}

{
//[http_snippet_3

    response<string_body> res;
    res.version(11);   // HTTP/1.1
    res.result(status::ok);
    res.set(field::server, "Beast");
    res.body() = "Hello, world!";
    res.prepare_payload();

//]
}

{
//[http_snippet_4

    flat_buffer buffer;         // (The parser is optimized for flat buffers)
    request<string_body> req;
    read(sock, buffer, req);

//]
}

{
//[http_snippet_5

    flat_buffer buffer;
    response<string_body> res;
    async_read(sock, buffer, res,
        [&](error_code ec, std::size_t bytes_transferred)
        {
            boost::ignore_unused(bytes_transferred);
            std::cerr << ec.message() << std::endl;
        });

//]
}

{
//[http_snippet_6

    // This buffer's max size is too small for much of anything
    flat_buffer buffer{10};

    // Try to read a request
    error_code ec;
    request<string_body> req;
    read(sock, buffer, req, ec);
    if(ec == error::buffer_overflow)
        std::cerr << "Buffer limit exceeded!" << std::endl;

//]
}

{
//[http_snippet_7

    response<string_body> res;
    res.version(11);
    res.result(status::ok);
    res.set(field::server, "Beast");
    res.body() = "Hello, world!";
    res.prepare_payload();

    error_code ec;
    write(sock, res, ec);
//]

//[http_snippet_8
    async_write(sock, res,
        [&](error_code ec, std::size_t bytes_transferred)
        {
            boost::ignore_unused(bytes_transferred);
            if(ec)
                std::cerr << ec.message() << std::endl;
        });
//]
}

{
//[http_snippet_10

    response<string_body> res;

    response_serializer<string_body> sr{res};

//]
}

{
//[http_snippet_18
    // Prepare an HTTP/1.1 response with a chunked body
    response<empty_body> res{status::ok, 11};
    res.set(field::server, "Beast");

    // Set Transfer-Encoding to "chunked".
    // If a Content-Length was present, it is removed.
    res.chunked(true);

    // Set up the serializer
    response_serializer<empty_body> sr{res};

    // Write the header first
    write_header(sock, sr);

    // Now manually emit three chunks:
    boost::asio::write(sock, make_chunk(get_next_chunk_body()));
    boost::asio::write(sock, make_chunk(get_next_chunk_body()));
    boost::asio::write(sock, make_chunk(get_next_chunk_body()));

    // We are responsible for sending the last chunk:
    boost::asio::write(sock, make_chunk_last());
//]
}

{
//[http_snippet_19
    // Prepare a set of chunk extension to emit with the body
    chunk_extensions ext;
    ext.insert("mp3");
    ext.insert("title", "Beale Street Blues");
    ext.insert("artist", "W.C. Handy");

    // Write the next chunk with the chunk extensions
    // The implementation will make a copy of the extensions object,
    // so the caller does not need to manage lifetime issues.
    boost::asio::write(sock, make_chunk(get_next_chunk_body(), ext));

    // Write the next chunk with the chunk extensions
    // The implementation will make a copy of the extensions object, storing the copy
    // using the custom allocator, so the caller does not need to manage lifetime issues.
    boost::asio::write(sock, make_chunk(get_next_chunk_body(), ext, std::allocator<char>{}));

    // Write the next chunk with the chunk extensions
    // The implementation allocates memory using the default allocator and takes ownership
    // of the extensions object, so the caller does not need to manage lifetime issues.
    // Note: ext is moved
    boost::asio::write(sock, make_chunk(get_next_chunk_body(), std::move(ext)));
//]
}

{
//[http_snippet_20
    // Manually specify the chunk extensions.
    // Some of the strings contain spaces and a period and must be quoted
    boost::asio::write(sock, make_chunk(get_next_chunk_body(),
        ";mp3"
        ";title=\"Danny Boy\""
        ";artist=\"Fred E. Weatherly\""
        ));
//]
}

{
//[http_snippet_21
    // Prepare a chunked HTTP/1.1 response with some trailer fields
    response<empty_body> res{status::ok, 11};
    res.set(field::server, "Beast");

    // Inform the client of the trailer fields we will send
    res.set(field::trailer, "Content-MD5, Expires");

    res.chunked(true);

    // Serialize the header and two chunks
    response_serializer<empty_body> sr{res};
    write_header(sock, sr);
    boost::asio::write(sock, make_chunk(get_next_chunk_body()));
    boost::asio::write(sock, make_chunk(get_next_chunk_body()));

    // Prepare the trailer
    fields trailer;
    trailer.set(field::content_md5, "f4a5c16584f03d90");
    trailer.set(field::expires, "never");

    // Emit the trailer in the last chunk.
    // The implementation will use the default allocator to create the storage for holding
    // the serialized fields.
    boost::asio::write(sock, make_chunk_last(trailer));
//]
}

{
//[http_snippet_22
    // Use a custom allocator for serializing the last chunk
    fields trailer;
    trailer.set(field::approved, "yes");
    boost::asio::write(sock, make_chunk_last(trailer, std::allocator<char>{}));
//]
}

{
//[http_snippet_23
    // Manually emit a trailer.
    // We are responsible for ensuring that the trailer format adheres to the specification.
    string_view ext =
        "Content-MD5: f4a5c16584f03d90\r\n"
        "Expires: never\r\n"
        "\r\n";
    boost::asio::write(sock, make_chunk_last(boost::asio::const_buffer{ext.data(), ext.size()}));
//]
}

{
//[http_snippet_24
    // Prepare a chunked HTTP/1.1 response and send the header
    response<empty_body> res{status::ok, 11};
    res.set(field::server, "Beast");
    res.chunked(true);
    response_serializer<empty_body> sr{res};
    write_header(sock, sr);

    // Obtain three body buffers up front
    auto const cb1 = get_next_chunk_body();
    auto const cb2 = get_next_chunk_body();
    auto const cb3 = get_next_chunk_body();

    // Manually emit a chunk by first writing the chunk-size header with the correct size
    boost::asio::write(sock, chunk_header{
        boost::asio::buffer_size(cb1) +
        boost::asio::buffer_size(cb2) +
        boost::asio::buffer_size(cb3)});

    // And then output the chunk body in three pieces ("chunk the chunk")
    boost::asio::write(sock, cb1);
    boost::asio::write(sock, cb2);
    boost::asio::write(sock, cb3);

    // When we go this deep, we are also responsible for the terminating CRLF
    boost::asio::write(sock, chunk_crlf{});
//]
}

} // fxx()



//[http_snippet_12

/** Send a message to a stream synchronously.

    @param stream The stream to write to. This type must support
    the @b SyncWriteStream concept.

    @param m The message to send. The Body type must support
    the @b BodyWriter concept.
*/
template<
    class SyncWriteStream,
    bool isRequest, class Body, class Fields>
void
send(
    SyncWriteStream& stream,
    message<isRequest, Body, Fields> const& m)
{
    // Check the template types
    static_assert(is_sync_write_stream<SyncWriteStream>::value,
        "SyncWriteStream requirements not met");
    static_assert(is_body_writer<Body>::value,
        "BodyWriter requirements not met");

    // Create the instance of serializer for the message
    serializer<isRequest, Body, Fields> sr{m};

    // Loop until the serializer is finished
    do
    {
        // This call guarantees it will make some
        // forward progress, or otherwise return an error.
        write_some(stream, sr);
    }
    while(! sr.is_done());
}

//]

//[http_snippet_13

template<class SyncReadStream>
void
print_response(SyncReadStream& stream)
{
    static_assert(is_sync_read_stream<SyncReadStream>::value,
        "SyncReadStream requirements not met");

    // Declare a parser for an HTTP response
    response_parser<string_body> parser;

    // Read the entire message
    read(stream, parser);

    // Now print the message
    std::cout << parser.get() << std::endl;
}

//]

#ifdef BOOST_MSVC
//[http_snippet_14

template<bool isRequest, class Body, class Fields>
void
print_cxx14(message<isRequest, Body, Fields> const& m)
{
    error_code ec;
    serializer<isRequest, Body, Fields> sr{m};
    do
    {
        sr.next(ec,
            [&sr](error_code& ec, auto const& buffer)
            {
                ec.assign(0, ec.category());
                std::cout << buffers(buffer);
                sr.consume(boost::asio::buffer_size(buffer));
            });
    }
    while(! ec && ! sr.is_done());
    if(! ec)
        std::cout << std::endl;
    else
        std::cerr << ec.message() << std::endl;
}

//]
#endif

//[http_snippet_15

template<class Serializer>
struct lambda
{
    Serializer& sr;

    lambda(Serializer& sr_) : sr(sr_) {}

    template<class ConstBufferSequence>
    void operator()(error_code& ec, ConstBufferSequence const& buffer) const
    {
        ec.assign(0, ec.category());
        std::cout << buffers(buffer);
        sr.consume(boost::asio::buffer_size(buffer));
    }
};

template<bool isRequest, class Body, class Fields>
void
print(message<isRequest, Body, Fields> const& m)
{
    error_code ec;
    serializer<isRequest, Body, Fields> sr{m};
    do
    {
        sr.next(ec, lambda<decltype(sr)>{sr});
    }
    while(! ec && ! sr.is_done());
    if(! ec)
        std::cout << std::endl;
    else
        std::cerr << ec.message() << std::endl;
}

//]

#if BOOST_MSVC
//[http_snippet_16

template<bool isRequest, class Body, class Fields>
void
split_print_cxx14(message<isRequest, Body, Fields> const& m)
{
    error_code ec;
    serializer<isRequest, Body, Fields> sr{m};
    sr.split(true);
    std::cout << "Header:" << std::endl;
    do
    {
        sr.next(ec,
            [&sr](error_code& ec, auto const& buffer)
            {
                ec.assign(0, ec.category());
                std::cout << buffers(buffer);
                sr.consume(boost::asio::buffer_size(buffer));
            });
    }
    while(! sr.is_header_done());
    if(! ec && ! sr.is_done())
    {
        std::cout << "Body:" << std::endl;
        do
        {
            sr.next(ec,
                [&sr](error_code& ec, auto const& buffer)
                {
                    ec.assign(0, ec.category());
                    std::cout << buffers(buffer);
                    sr.consume(boost::asio::buffer_size(buffer));
                });
        }
        while(! ec && ! sr.is_done());
    }
    if(ec)
        std::cerr << ec.message() << std::endl;
}

//]
#endif

// Highest snippet: 

} // doc_http_snippets
