mirror of
https://github.com/faye/websocket-driver-ruby.git
synced 2025-11-01 13:59:38 +00:00
113 lines
3.1 KiB
Ruby
113 lines
3.1 KiB
Ruby
module WebSocket
|
|
class Driver
|
|
|
|
class Client < Hybi
|
|
def self.generate_key
|
|
Base64.encode64((1..16).map { rand(255).chr } * '').strip
|
|
end
|
|
|
|
attr_reader :status, :headers
|
|
|
|
def initialize(socket, options = {})
|
|
super
|
|
|
|
@ready_state = -1
|
|
@key = Client.generate_key
|
|
@accept = Hybi.generate_accept(@key)
|
|
@http = HTTP::Response.new
|
|
end
|
|
|
|
def version
|
|
'hybi-13'
|
|
end
|
|
|
|
def start
|
|
return false unless @ready_state == -1
|
|
@socket.write(Driver.encode(handshake_request, :binary))
|
|
@ready_state = 0
|
|
true
|
|
end
|
|
|
|
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
|
|
|
|
private
|
|
|
|
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 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 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
|
|
end
|
|
|
|
end
|
|
end
|
|
|