Skip to content

Instantly share code, notes, and snippets.

Created June 3, 2016 12:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/ae63011651aee0500f0d9237fe2a76c4 to your computer and use it in GitHub Desktop.
Save anonymous/ae63011651aee0500f0d9237fe2a76c4 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
# Author: Romi Strub
# Last Edited: 02-06-2016
# WebSocket chat script.
puts "RUBY_VERSION " + RUBY_VERSION
puts "Process.pid " + Process.pid.to_s
class MyRequest
def self.parseHttpHeaders(headerString)
returnHash = {}
lines = headerString.split("\r\n")
firstLine = lines.shift
firstLineWords = firstLine.split(" ")
returnHash["Method"] = firstLineWords[0]
returnHash["URI"] = firstLineWords[1]
returnHash["Protocol"] = firstLineWords[2]
lines.each {|line|
keyval = line.split(": ")
returnHash[keyval[0]] = keyval[1]
}
returnHash
end
def self.unmaskPayload(mask, payload)
#mask expects string of 4 chars
#payload expects string of n chars
mask = mask.bytes
payload = payload.bytes
payload = payload.map.with_index{|byte, i|
byte^mask[i%4] ## if either bit is 1, result is 1
}
payload.map{|c|c.chr}.join
end
def self.decodeFrame(connection)
header = connection.recv(2)
bytes = header.bytes
puts "header-bytes " + bytes.to_s
byteone = bytes.shift
# whittle down the first byte to get the opcode
# first bit is set to one if message is final
final = byteone[7]
puts "final #{final}"
byteone = final == 1 ? byteone-128 : byteone
reserveone = byteone[6]
puts "reserveone #{reserveone}"
byteone = reserveone == 1 ? byteone-64 : byteone
reservetwo = byteone[5]
puts "reservetwo #{reservetwo}"
byteone = reservetwo == 1 ? byteone-32 : byteone
reservethree = byteone[4]
puts "reservethree #{reservethree}"
byteone = reservethree == 1 ? byteone-16 : byteone
opcode = byteone
puts "opcode #{opcode}"
bytetwo = bytes.shift
# first bit (of second byte) is set to one if message is masked
masked = bytetwo[7]
puts "masked #{masked}"
if masked
bytetwo = bytetwo-128 # negate mask flag bit
mask = connection.recv(4) # receieve mask string (4 bytes)
puts "mask #{mask}"
end
payloadlength = bytetwo
puts "payloadlength #{payloadlength}"
# receieve payload
payload = connection.recv(payloadlength)
if masked
payload = MyRequest.unmaskPayload(mask, payload)
end
puts "payload #{payload}"
{"opcode"=> opcode,
"masked"=> masked,
"payloadlength"=> payloadlength,
"mask"=>mask,
"payload"=>payload}
end
def self.encodeFrame(opcode, payload)
byteone = 128 + opcode # assumes message is final
bytetwo = payload.length # assumes message is not masked
body = payload
byteone.chr << bytetwo.chr << body
end
end
require 'digest/sha1'
require 'socket'
require 'awesome_print'
port = 9292
s = TCPServer.new '',port
puts "server started on port #{port}" ##
i=0
connections = []
loop {
i = i+1
connection = s.accept
puts "connection #{i} on port #{port}" ##
puts "local address: " + connection.local_address.inspect
puts "remote address: " + connection.remote_address.inspect
connections << connection
puts "connections:" ##
ap connections ##
string = connection.recv(1024)
inHeaders = MyRequest.parseHttpHeaders(string)
ap inHeaders ##
if defined? inHeaders["Sec-WebSocket-Key"]
# compute response for WebSocket
websocketkey = inHeaders["Sec-WebSocket-Key"]
keysuffix = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
computedkey = Digest::SHA1.base64digest(websocketkey + keysuffix)
outHeaders = "HTTP/1.1 101 Switching Protocols\r\n"
outHeaders << "Connection: Upgrade\r\n"
outHeaders << "Date: #{Time.now}\r\n"
outHeaders << "Sec-Websocket-Accept: #{computedkey}\r\n"
outHeaders << "Server: Ruby Websocket\r\n"
outHeaders << "Upgrade: websocket\r\n"
outHeaders << "\r\n"
connection.write outHeaders
puts "RESPONSE SENT:" ##
puts outHeaders ##
end
Thread.new {
thisconnection = connection
j=0
loop {
j=j+1
# loopback
payload = MyRequest.decodeFrame(thisconnection)["payload"]
puts "j = " + j.to_s
returnMessage = MyRequest.encodeFrame(1,payload)
ap connections
connections.each {|c|
puts "connection " + c
c.write returnMessage
}
}
}
##connection.close
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment