module Tri
Public Class Methods
get_client()
click to toggle source
Accept the connection from client
# File lib/tri.rb, line 34 def self.get_client if is_running? client = @server.accept # Got a connection from a client if key = validWebSocketRequest?(client) # Send the respone back for handshaking phase client.print caculateRequestReply(key) else # Close the connection due to invalid WebSocket requests client.close client = nil end end # Return the client client end
get_client_with_keep_alive(second)
click to toggle source
Accept the connection from client with a keep alive
# File lib/tri.rb, line 54 def self.get_client_with_keep_alive(second) if is_running? client = @server.accept # Got a connection from a client if key = validWebSocketRequest?(client) # Send the respone back for handshaking phase client.print caculateRequestReply(key) # Sending PING, for every 28 sec Thread.new { while client ping_to(client) sleep(second) end } else # Close the connection due to invalid WebSocket requests client.close client = nil end end # Return the client client end
is_running?()
click to toggle source
Check if the server is till running
# File lib/tri.rb, line 29 def self.is_running? !@server.nil? end
ping_to(client)
click to toggle source
Send a PING frame to client
# File lib/tri.rb, line 100 def self.ping_to(client) pingData = "CHAT4NINJA" firstByte = "10001001".to_i(2).chr secondByte = pingData.size.chr client.write(firstByte + secondByte + pingData) end
receive_from(client)
click to toggle source
Trying to get data from client
# File lib/tri.rb, line 81 def self.receive_from(client) data = getData(client) case data when -2 [-2,"[ERROR] Huge payload - should close the connection."] when -1 [-2,"[ERROR] No data due to connection timeout."] when 8 [8, "[CLOSE] Close frame sent from the client."] when 9 [8, "[OPCODE] PING frame sent from the client."] when 10 [9, "[OPCODE] PONG frame sent from the client."] else [1, data] end end
send_to(client,data)
click to toggle source
Send data to client
# File lib/tri.rb, line 108 def self.send_to(client,data) firstByte = "10000001".to_i(2).chr if data.size <=127 secondByte = data.size.chr client.write(firstByte + secondByte + data) else binaryString= data.size.to_s(2) extraZero = "" (16 - binaryString.size).times do extraZero += "0" end extraZero = extraZero + binaryString secondByte = "01111110".to_i(2).chr thirdByte = extraZero[0..7].to_i(2).chr forthByte = extraZero[8..15].to_i(2).chr begin client.write(firstByte + secondByte + thirdByte + forthByte + data) # Catch err raised by the command above rescue SystemCallError => err # Return error code "[ERROR] Trying to send {" + firstByte + secondByte + thirdByte + forthByte + data + "} " + err.to_s else # Return success message end end end
start(input1="localhost", input2 = 888)
click to toggle source
Start a WetSocket with TCP/IP layer
# File lib/tri.rb, line 6 def self.start(input1="localhost", input2 = 888) # Check input parameters if !input1.is_a? String hostname = "localhost" port = input1 else hostname = input1 port = input2 end # Trying to init a new TCPserver begin @server = TCPServer.new(hostname,port) # Catch err raised by the command above rescue SystemCallError => err # Return error code "[ERROR] Trying to start the server @" + hostname + ":" + port.to_s + ".\r\n[ERROR] " + err.to_s else # Return success message "[SERVER] The server is now listening @" + hostname + ":" + port.to_s + "." end end
Private Class Methods
caculateRequestReply(key)
click to toggle source
Respond the request of the WebSocket client
# File lib/tri.rb, line 158 def self.caculateRequestReply(key) "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: #{sha_1(key)}\r\n\r\n" end
decodeData(input, mask)
click to toggle source
Decode payload data using the mask-keys
# File lib/tri.rb, line 171 def self.decodeData(input, mask) data = "" for i in 0..7 do if input[i]==mask[i] data += "0" else data += "1" end end data.to_i(2).chr end
getData(client)
click to toggle source
Receiving all payload data from client
# File lib/tri.rb, line 184 def self.getData(client) frame = Hash.new # First 8 bits after connected receiveByte = client.recv(1).unpack("B*").join if receiveByte != "" frame["fin"] = receiveByte[0].to_i(2) frame["rsv"] = receiveByte[1..3].to_i(2) frame["opcode"] = receiveByte[4..7].to_i(2) # Second 8 bits receiveByte = client.recv(1).unpack("B*").join frame["mask"] = receiveByte[0].to_i(2) frame["payload"]= receiveByte[1..7].to_i(2) # Load more frame["payload"]? if frame["payload"] >= 126 loadMore = 0 newPayload = "" if frame["payload"] == 126 loadMore = 2 end if frame["payload"] == 127 loadMore = 8 end loadMore.times do newPayload += client.recv(1).unpack("B*").join end frame["payload"] = newPayload.to_i(2) end if frame["payload"] <= 500 # Load the MASKEY or not if frame["mask"] == 1 frame["mask-key"] = Array.new for i in 0..3 frame["mask-key"].push client.recv(1).unpack("B*").join end end # Load data based-on the payload frame["data"] = "" for i in 0..(frame["payload"]-1) frame["data"] += decodeData(client.recv(1).unpack("B*").join,frame["mask-key"][i % 4]) end if frame["opcode"] != 1 frame["opcode"] else frame["data"] end else -2 end else -1 # no data end end
sha_1(input)
click to toggle source
Return SHA1 for the base64 digest
# File lib/tri.rb, line 166 def self.sha_1(input) Digest::SHA1.base64digest(input+"258EAFA5-E914-47DA-95CA-C5AB0DC85B11") end
validWebSocketRequest?(client)
click to toggle source
Check if the client sent right request for WebSocket protocol
# File lib/tri.rb, line 140 def self.validWebSocketRequest?(client) handshakeKeys = Hash.new if (client.gets.include?("GET / HTTP/1.1")) while (requests = client.gets) && (requests != "\r\n") if requests.include?(": ") requests = requests.split(": ") if requests.size == 2 handshakeKeys[requests[0]] = requests[1].chomp end end end handshakeKeys["sec-websocket-key"] # return the key else "" # return nil end end