Boost.Beast で websocket の handshake に custom request header をつける方法

# はじめに
Beast の [ドキュメント](https://www.boost.org/doc/libs/master/libs/beast/doc/html/beast/using_websocket/handshaking_clients.html) を見ていると、websocket client で handshake をリクエストする時に **handshake_ex()** を使って Decorator という関数オブジェクトを渡すとリクエストヘッダを追加できるみたいです

こんな例がでてます

```C++:
ws.handshake_ex("localhost", "/",
    [](request_type& m)
    {
        m.insert(http::field::sec_websocket_protocol, "xmpp;ws-chat");
    });
```

m.insert の最初の引数は [こちら](https://github.com/boostorg/beast/blob/develop/include/boost/beast/http/field.hpp) で定義されてる enum class で、**リクエストヘッダフィールドの名前**がポケモンの一覧なみにたくさん定義されてます
見たことのないのがほとんどで、リクエストヘッダの世界って奥が深かったんだなと自分の知識の浅さを思い知らされます

第一引数でヘッダフィールドを **enum class** で指定して、第二引数の文字列を設定してくれるみたいで、例えば **Authorization** とかは

```C++:
m.insert(boost::beast::http::field::authorization, "Bearer 1234");
```

でうまくいきました

ここで自然に疑問がわいてきます。enmu で指定するんだったら **カスタムヘッダ** ってどうしたらいいんでしょ？

# 正解1
同じ疑問を持つ人はやっぱりいらっしゃっいまして、**カスタムヘッダを指定できるようにインターフェースを拡張してよ**って [issue](https://github.com/boostorg/beast/issues/70) が上がってて、それに対して**これでできるでしょ** という回答がまたかっこいい

```C++:
class set_subprotocols
{
    std::string s_;

public:
    explicit
    set_subprotocols(std::string s)
        : s_(s) {}

    template<bool isRequest, class Body, class Headers>
    void
    operator()(beast::http::message<isRequest, Body, Headers>& m) const
    {
        m.fields.replace("Sec-WebSocket-Protocol", s_);
    }
};

using namespace beast::websocket;

stream<boost::ip::tcp::socket> ws;
...
ws.set_option(decorate(set_subprotocols{"mqtt"}));
```

早速、こんなコード書いて X-Custome-Id がつくかためしてみます
あ、いま気がついたんだけどカスタムって e いりましたっけ？

```C++:sample.cpp
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/beast/http.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <cstdlib>
#include <iostream>
#include <string>

#include <boost/beast/websocket/ssl.hpp>
#include <boost/asio/ssl.hpp>

#include <boost/beast/websocket/stream.hpp>

using tcp = boost::asio::ip::tcp;               // from <boost/asio/ip/tcp.hpp>
namespace websocket = boost::beast::websocket;  // from <boost/beast/websocket.hpp>

// https://github.com/boostorg/beast/issues/70
class set_subprotocols
{
    std::string s_;

public:
    explicit
    set_subprotocols(std::string s)
        : s_(s) {}

    template<bool isRequest, class Body, class Headers>
    void
    operator()(boost::beast::http::message<isRequest, Body, Headers>& m) const
    {
        m.fields.replace("X-Custome-Id", s_);
    }
};

// Sends a WebSocket message and prints the response
int main(int argc, char** argv)
{
    try
    {
        // The io_context is required for all I/O
        boost::asio::io_context ioc;

        // These objects perform our I/O
        tcp::resolver resolver{ioc};
//        websocket::stream<tcp::socket> ws{ioc};
        boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
        ctx.set_verify_mode(boost::asio::ssl::verify_none);
        websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> ws{ioc, ctx};


        boost::asio::ip::tcp::endpoint ep{boost::asio::ip::address::from_string("127.0.0.1"), 8888};

        // add custome header X-Custome-Id
        ws.set_option(websocket::decorate(set_subprotocols{"1234"}));

    }
    catch(std::exception const& e)
    {
        std::cerr << "Error: " << e.what() << std::endl;
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}
```

なんと**コンパイル・エラー**...

```bash:
g++-8.1.0 -std=c++17 sample.cpp -o wc -I ../boost_1_69_0 -lstdc++ -lpthread -latomic -lcrypto -lssl
sample.cpp: In function ‘int main(int, char**)’:
sample.cpp:55:34: error: ‘decorate’ is not a member of ‘websocket’
         ws.set_option(websocket::decorate(set_subprotocols{"1234"}));
                                  ^~~~~~~~
```

decorate が websocket のメンバじゃないって？？？
じゃ、どこのメンバなのよ？？？

# boost 1.69 の場合
コード読んでもよくわからない（達人のコードはヤワ C++er にはとってもハード）ので諦めて[聞いてみたら](https://github.com/boostorg/beast/issues/1470) ５分も待たないうちに回答してもらえてびっくり、なんて親切な人なんだろう
**decorate はインターフェース変えたよ** ってお茶目なおっしゃられぶりに好感が持てます

なんだかんだで boost 1.69 での正解はこんな感じになりました

```c++:sample.cpp
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/beast/http.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <cstdlib>
#include <iostream>
#include <string>

#include <boost/beast/websocket/ssl.hpp>
#include <boost/asio/ssl.hpp>

#include <boost/beast/websocket/stream.hpp>

using tcp = boost::asio::ip::tcp;               // from <boost/asio/ip/tcp.hpp>
namespace websocket = boost::beast::websocket;  // from <boost/beast/websocket.hpp>

// https://github.com/boostorg/beast/issues/70
class set_subprotocols
{
    std::string s_;

public:
    explicit
    set_subprotocols(std::string s)
        : s_(s) {}

    template<bool isRequest, class Body, class Headers>
    void
    operator()(boost::beast::http::message<isRequest, Body, Headers>& m) const
    {
        m.set("X-Custome-Id", s_);
    }
};

// Sends a WebSocket message and prints the response
int main(int argc, char** argv)
{
    try
    {
        // The io_context is required for all I/O
        boost::asio::io_context ioc;

        // These objects perform our I/O
        tcp::resolver resolver{ioc};
//        websocket::stream<tcp::socket> ws{ioc};
        boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
        ctx.set_verify_mode(boost::asio::ssl::verify_none);
        websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> ws{ioc, ctx};


        boost::asio::ip::tcp::endpoint ep{boost::asio::ip::address::from_string("127.0.0.1"), 8888};

        // add custome header X-Custome-Id
//        ws.set_option(websocket::decorate(set_subprotocols{"1234"}));

        // connect the underlying TCP/IP socket
        ws.next_layer().next_layer().connect(ep);

        // perform SSL handshake
        ws.next_layer().handshake(boost::asio::ssl::stream_base::client);

        ws.handshake_ex(
            "localhost",
            "/",
            set_subprotocols{"1234"});
    }
    catch(std::exception const& e)
    {
        std::cerr << "Error: " << e.what() << std::endl;
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}
```

# boost 1.70 の場合
さらに、boost 1.70 ではまた変えるからね、との事ですのでこの場を借りて皆様にも御連絡しておきますね
**ごめんね、産みの苦しみってもんさ(Sorry about that, growing pains!!!)** との事でございます

```c++:
ws.set_option(websocket::stream_base::decorator(set_subprotocols{"1234"}));
ws.handshake("localhost", "/");
```