wsserver/serv.rb

108 lines
2.9 KiB
Ruby
Raw Permalink Normal View History

2019-06-23 10:40:40 -05:00
require 'socket' # Provides TCPServer and TCPSocket classes
require 'digest/sha1'
class WebSocketServer
def initialize(host="localhost",port=2345)
@server=TCPServer.new(host,port)
end
def accept()
# Wait for a connection
@socket = @server.accept
# Read the HTTP request. We know it's finished when we see a line with nothing but \r\n
http_request = ""
while (line = @socket.gets) && (line != "\r\n")
http_request += line
end
# Grab the security key from the headers. If one isn't present, close the connection.
if matches = http_request.match(/^Sec-WebSocket-Key: (\S+)/)
websocket_key = matches[1]
puts "Websocket handshake detected with key: #{websocket_key}"
else
puts "Aborting non-websocket connection"
socket.close
return
end
response_key = Digest::SHA1.base64digest([websocket_key, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"].join)
puts "Responding to handshake with key: #{response_key}"
@socket.write <<-eos
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: #{response_key}
eos
puts "Handshake completed."
end
def recv()
first_byte = @socket.getbyte
fin = first_byte & 0b10000000
opcode = first_byte & 0b00001111
fin = fin >> 7
fin = (fin ? true : false)
puts "Got frame. FIN:#{fin}. Opcode:#{opcode}"
raise "We don't support continuations" unless fin
unless opcode == 1 or opcode == 8
puts "We only support text data and close frame"
send("",3)
end
if opcode == 1
second_byte = @socket.getbyte
is_masked = second_byte & 0b10000000
payload_size = second_byte & 0b01111111
raise "All incoming frames should be masked according to the websocket spec" unless is_masked
raise "We only support payloads < 126 bytes in length" unless payload_size < 126
puts "Payload size: #{payload_size} bytes"
mask = 4.times.map {@socket.getbyte}
puts "Got mask: #{mask.inspect}"
data = payload_size.times.map {@socket.getbyte}
puts "Got masked data: #{data.inspect}"
unmasked_data = data.each_with_index.map { |byte, i| byte ^ mask[i % 4] }
puts "Unmasked the data: #{unmasked_data.inspect}"
string=unmasked_data.pack('C*').force_encoding('utf-8')
puts "Converted to a string: #{string}"
return string
elsif opcode == 8
send("",0)
return false
end
end
def send(message,closetype=false)
if closetype
puts "Sending close frame"
output=[0x88,2,3,0xe8+closetype]
@socket.write output.pack("C*")
@socket.close
else
puts "Sending #{message}"
output = [0x81, message.size, message]
@socket.write output.pack("CCA#{message.size}")
end
end
end
serv=WebSocketServer.new
serv.accept
while true
message=serv.recv
break if message==false
puts message
serv.send("Got #{message}")
end