class WebSocket::Driver::Client
def self.generate_key
def self.generate_key Base64.encode64((1..16).map { rand(255).chr } * '').strip end
def fail_handshake(message)
def fail_handshake(message) message = "Error during WebSocket handshake: #{message}" emit(:error, ProtocolError.new(message)) @ready_state = 3 emit(:close, CloseEvent.new(ERRORS[:protocol_error], message)) end
def handshake_request
def handshake_request uri = URI.parse(@socket.url) host = uri.host + (uri.port ? ":#{uri.port}" : '') path = (uri.path == '') ? '/' : uri.path query = uri.query ? "?#{uri.query}" : '' headers = [ "GET #{path}#{query} HTTP/1.1", "Host: #{host}", "Upgrade: websocket", "Connection: Upgrade", "Sec-WebSocket-Key: #{@key}", "Sec-WebSocket-Version: 13" ] if @protocols.size > 0 headers << "Sec-WebSocket-Protocol: #{@protocols * ', '}" end (headers + [@headers.to_s, '']).join("\r\n") end
def initialize(socket, options = {})
def initialize(socket, options = {}) super @ready_state = -1 @key = Client.generate_key @accept = Hybi.generate_accept(@key) @http = HTTP::Response.new end
def parse(buffer)
def parse(buffer) return super if @ready_state > 0 @http.parse(buffer) return fail_handshake('Invalid HTTP response') if @http.error? validate_handshake if @http.complete? parse(@http.body) if @ready_state == 1 end
def start
def start return false unless @ready_state == -1 @socket.write(Driver.encode(handshake_request, :binary)) @ready_state = 0 true end
def validate_handshake
def validate_handshake @status = @http.code @headers = Headers.new(@http.headers) unless @http.code == 101 return fail_handshake("Unexpected response code: #{@http.code}") end upgrade = @http['Upgrade'] || '' connection = @http['Connection'] || '' accept = @http['Sec-WebSocket-Accept'] || '' protocol = @http['Sec-WebSocket-Protocol'] || '' if upgrade == '' return fail_handshake("'Upgrade' header is missing") elsif upgrade.downcase != 'websocket' return fail_handshake("'Upgrade' header value is not 'WebSocket'") end if connection == '' return fail_handshake("'Connection' header is missing") elsif connection.downcase != 'upgrade' return fail_handshake("'Connection' header value is not 'Upgrade'") end unless accept == @accept return fail_handshake('Sec-WebSocket-Accept mismatch') end unless protocol == '' if @protocols.include?(protocol) @protocol = protocol else return fail_handshake('Sec-WebSocket-Protocol mismatch') end end open end
def version
def version 'hybi-13' end