RubyでWebSocketクライアントを書く - @ledsun blog で、ソケットからレスポンス文字列を読み出すところまで実装しました。 今回はレスポンス文字列に含まれているaccept digestを検証します。
module WANDS class HTTPResponse attr_reader :header def parse(stream) @response_string = read(stream) parse_response_string(@response_string) end def to_s @response_string end private def read(stream) response_string = "" while (line = stream.gets) != "\r\n" response_string += line end response_string end def parse_response_string(response_string) headers, _body = response_string.split("\r\n\r\n", 2) headers_lines = headers.split("\r\n") _status_line = headers_lines.shift @header = headers_lines.map do |line| key, value = line.split(': ', 2) [key.downcase, [value]] end.to_h end end class UpgradeWebSocketRequest include ::Protocol::WebSocket::Headers TEMPLATE = <<~REQUEST GET / HTTP/1.1 Host: <%= @host %>:<%= @port %> Connection: Upgrade Upgrade: websocket Sec-WebSocket-Version: 13 Sec-WebSocket-Key: <%= @key %> REQUEST def initialize(host, port) @erb = ERB.new(TEMPLATE) @host = host @port = port @key = Nounce.generate_key end def to_s @erb.result(binding).gsub(/\r?\n/, "\r\n") end def verify(response) accept_digest = response.header[SEC_WEBSOCKET_ACCEPT].first accept_digest == Nounce.accept_digest(@key) || raise("Invalid accept digest") end end end request = WANDS::UpgradeWebSocketRequest.new('localhost', 2345) puts request.to_s socket = TCPSocket.new('localhost', 2345) socket.write(request.to_s) socket.flush puts 'Request sent' response = WANDS::HTTPResponse.new response.parse(socket) puts "Response: #{response.to_s}" request.verify response socket.close
HTTPレスポンス文字列を良い感じにパースしてくれるライブラリが見つからなかったので、パースするクラスを作りました。
WEBrick::HTTPRequest
っぽいインターフェースにしてあります。
accept digestの検証は、サーバーと同一のアルゴリズムで計算して同じ値かどうかを見ています。