lib/faye/websocket/hybi_parser/handshake.rb



module Faye
  class WebSocket
    class HybiParser

      class Handshake
        GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'

        attr_reader :protocol

        def initialize(uri, protocols)
          @uri       = uri
          @protocols = protocols
          @key       = Base64.encode64((1..16).map { rand(255).chr } * '').strip
          @accept    = Base64.encode64(Digest::SHA1.digest(@key + GUID)).strip
          @buffer    = []
        end

        def request_data
          hostname = @uri.host + (@uri.port ? ":#{@uri.port}" : '')
          path = (@uri.path == '') ? '/' : @uri.path
          headers = [
            "GET #{path}#{@uri.query ? '?' : ''}#{@uri.query} HTTP/1.1",
            "Host: #{hostname}",
            "Upgrade: websocket",
            "Connection: Upgrade",
            "Sec-WebSocket-Key: #{@key}",
            "Sec-WebSocket-Version: 13"
          ]

          if @protocols
            headers << "Sec-WebSocket-Protocol: #{@protocols * ', '}"
          end

          (headers + ['','']).join("\r\n")
        end

        def parse(data)
          message  = []
          complete = false
          data.each_byte do |byte|
            if complete
              message << byte
            else
              @buffer << byte
              complete ||= complete?
            end
          end
          message
        end

        def complete?
          @buffer[-4..-1] == [0x0D, 0x0A, 0x0D, 0x0A]
        end

        def valid?
          data = WebSocket.encode(@buffer)

          response = Net::HTTPResponse.read_new(Net::BufferedIO.new(StringIO.new(data)))
          return false unless response.code.to_i == 101

          upgrade    = response['Upgrade']
          connection = response['Connection']
          protocol   = response['Sec-WebSocket-Protocol']

          @protocol = @protocols && @protocols.include?(protocol) ?
                      protocol :
                      nil

          upgrade and upgrade =~ /^websocket$/i and
          connection and connection.split(/\s*,\s*/).include?('Upgrade') and
          ((!@protocols and !protocol) or @protocol) and
          response['Sec-WebSocket-Accept'] == @accept
        end
      end

    end
  end
end