@ledsun blog

無味の味は佳境に入らざればすなわち知れず

RubyでつくるWebSocketサーバーにprotocol-websocket gemを取り入れる その3

RubyでつくるWebSocketサーバーにprotocol-websocket gemを取り入れる その2 - @ledsun blog でリクエスト文字列の解析をWEBrickに置き換えました。 今度はレスポンス文字列の作成にWEBrick::HTTPResponseを使います。

require 'socket'
require 'webrick'
require 'protocol/websocket/headers'

def read_headers_from(socket)
  request = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
  request.parse(socket)
  request.header
end

def calculate_accept_nonce_from(headers)
  key = headers[::Protocol::WebSocket::Headers::SEC_WEBSOCKET_KEY].first
  ::Protocol::WebSocket::Headers::Nounce.accept_digest(key)
end

server = TCPServer.new 'localhost',
                       2345

loop do
  # ここら辺はecho_server.rbと同じ。純粋なTCP通信
  socket = server.accept

  # HTTPリクエストheaderを読み込む
  headears = read_headers_from socket

  # WebSocketリクエストかどうかを判定する
  unless headears["upgrade"] = ::Protocol::WebSocket::Headers::PROTOCOL
    puts "Not a websocket request"
    socket.close
    next
  end

  response_key = calculate_accept_nonce_from headears
  puts "response_key: #{response_key}"

  response = WEBrick::HTTPResponse.new(WEBrick::Config::HTTP)
  response.status = 101
  response.upgrade! ::Protocol::WebSocket::Headers::PROTOCOL
  response['Sec-WebSocket-Accept'] = response_key

  # ソケットそのものはHTTP通信で使われているものと同じ
  response.send_response socket
  puts 'Handshake response sent'

  # ここからはWebSocket通信
  first_byte = socket.getbyte
  fin = first_byte & 0b10000000
  opcode = first_byte & 0b00001111

  raise 'fin bit is not set' unless fin
  raise 'opcode is not a text' unless opcode == 0x1

  second_byte = socket.getbyte
  is_masked = second_byte & 0b10000000
  payload_size = second_byte & 0b01111111

  raise 'mask bit is not set' unless is_masked
  raise 'payload size > 125 is not supported' unless payload_size <= 125

  puts "Payload size: #{payload_size}"

  mask = 4.times.map { socket.getbyte }
  puts "Mask: #{mask}"

  data = payload_size.times.map.with_index { socket.getbyte ^ mask[_2 % 4] }
  puts "Data: #{data.pack('C*')}"

  # クライアントにデータを返す
  response_message = "Loud and clear!"
  response = [0b10000001,
              response_message.size,
              response_message
            ].pack("CCA#{response_message.size}")
  socket.write response

  socket.close
end

今の時点ではWEBrick::HTTPResponseProtocol::HTTP::Responseの違いを理解できていません。