ハンドシェイクのだいたいの流れ。
リクエストヘッダ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;
}