LoginSignup
10
9

More than 5 years have passed since last update.

C++でWebSocketハンドシェイク

Posted at

ハンドシェイクのだいたいの流れ。

リクエストヘッダSEC-WEBSOCKET-KEYに設定された文字列に"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"という固定文字列を連結したものをSHA-1エンコードしてからBASE64したものを101レスポンスで返します。

処理自体は難しくないけど、主にCryptoPPの使い方のためにメモ。

あと、地味にboost::base64_from_binaryがパディングの=をくっつけてくれないのも罠でした。

websocket_hanshake.cpp

#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include <cryptopp/sha.h>

using namespace std;
using namespace boost::archive::iterators;

#define HANDSHAKE_MAGIC_STRING "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"

// 中略

    // Upgrade処理
    void HttpHeaderPacketParser::upgrade()
    {
        string encoded_text =
            toBase64( toSHA1( http_request_header_["SEC-WEBSOCKET-KEY"]+HANDSHAKE_MAGIC_STRING ) );

        string http_response_header;

        http_response_header += "HTTP/1.1 101 Switching Protocols\r\n";
        http_response_header += "Upgrade: websocket\r\n";
        http_response_header += "Connection: Upgrade\r\n";
        http_response_header += "Sec-WebSocket-Accept: "+encoded_text+"\r\n";
        http_response_header += "\r\n";

        connection_.write(http_response_header.c_str(), http_response_header.length());

        connection_.switch_packet_parser(boost::shared_ptr<PacketParser>(new WebSocketParser(this)));

        cout << http_response_header << endl;
    }

    // バイナリ配列をBase64エンコードした文字列に変換する
    string HttpHeaderPacketParser::toBase64(const vector<byte>& source)
    {
        typedef base64_from_binary<transform_width<vector<byte>::const_iterator,6,8> > base64;

        string encoded_text(
                base64(source.begin()),
                base64(source.end())
                );
        int padding_count = 4-encoded_text.length()%4;
        for (int i=0; i<padding_count; i++) {
            encoded_text += '=';
        }

        return encoded_text;
    }

    // 文字列をSHA1変換する
    vector<byte> HttpHeaderPacketParser::toSHA1(const string& source)
    {
        byte buf[CryptoPP::SHA1::DIGESTSIZE];

        CryptoPP::SHA1().CalculateDigest( buf, reinterpret_cast<const byte*>(source.c_str()), source.length());

        vector<byte> encoded_bytes;
        encoded_bytes.assign(buf, buf+sizeof(buf));

        return encoded_bytes;
    }
10
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
9