Skip to content

Instantly share code, notes, and snippets.

@toretore
Last active December 12, 2015 07:08
Show Gist options
  • Save toretore/35eb74a2cac3f214fd4b to your computer and use it in GitHub Desktop.
Save toretore/35eb74a2cac3f214fd4b to your computer and use it in GitHub Desktop.
Mongrel2 handlers
handler = Handler(
send_spec = "tcp://*:9999",
send_ident = "7B0A2BF9-0DB2-4FEB-AD90-75C649B859FC",
recv_spec = "tcp://*:9998",
recv_ident = ""
)
main = Server(
uuid="242DABD4-D5BE-4D16-A042-D4985C8095BD",
access_log="/logs/access.log",
error_log="/logs/error.log",
chroot="./",
default_host="localhost",
name="test",
pid_file="/run/mongrel2.pid",
port=6767,
hosts = [
Host(name="localhost", routes={
"/": handler
})
]
)
servers = [main]
require 'eventmachine'
require 'em-zeromq'
require 'json'
require 'securerandom'
EM.run do
context = EM::ZeroMQ::Context.new(1)
requests = context.socket(ZMQ::PULL)
requests.connect('tcp://127.0.0.1:9999')
responses = context.socket(ZMQ::PUB)
responses.connect('tcp://127.0.0.1:9998')
responses.setsockopt(ZMQ::IDENTITY, SecureRandom.uuid)
requests.on :message do |msg|
#SERVER_UUID CLIENT_ID PATH N:HEADERS_JSON, N:BODY,
#7B0A2BF9-0DB2-4FEB-AD90-75C649B859FC 3 / 29:{"Content-Type":"text/plain"}, 13:Hello handler
uuid, id, path, rest = msg.copy_out_string.split(' ', 4)
netstrings = []
until rest.empty? #6:{JSON},15:hello my friend,
length = rest[/\A\d+/] #6 #15
rest.slice!(0, length.length+1) #6: #15:
netstrings << rest.slice!(0, length.to_i) #{JSON} #hello my friend
rest.slice!(0) #, #,
end
headers, body = netstrings
headers = JSON.parse(headers)
p headers
p body
response_body = 'Hello, Mongrel2!'
response_body = "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: #{response_body.bytesize}\r\n\r\n#{response_body}"
#Response format: SERVER_UUID N:CLIENT_IDS, BODY
#7B0A2BF9-0DB2-4FEB-AD90-75C649B859FC 1:3, Hello Mongrel2!
response = '%s %d:%s, %s' % [uuid, id.size, id, response_body]
responses.send_msg(response)
end
end#EM.run
require 'eventmachine'
require 'em-zeromq'
require 'json'
require 'securerandom'
EM.run do
context = EM::ZeroMQ::Context.new(1)
requests = context.socket(ZMQ::PULL)
requests.connect('tcp://127.0.0.1:9999')
responses = context.socket(ZMQ::PUB)
responses.connect('tcp://127.0.0.1:9998')
responses.setsockopt(ZMQ::IDENTITY, SecureRandom.uuid)
clients = []
requests.on :message do |msg|
uuid, id, path, rest = msg.copy_out_string.split(' ', 4)
netstrings = []
until rest.empty? #6:{JSON},15:hello my friend,
length = rest[/\A\d+/] #6 #15
rest.slice!(0, length.length+1) #6: #15:
netstrings << rest.slice!(0, length.to_i) #{JSON} #hello my friend
rest.slice!(0) #, #,
end
headers, body = netstrings
headers = JSON.parse(headers)
#When a client disconnects, a special METHOD header with value JSON is sent by Mongrel2
#The body is a JSON document with "type":"disconnect"
if headers['METHOD'] == 'JSON' && JSON.parse(body)['type'] == 'disconnect'
clients.delete(id) #Remove client's id from list of connected clients
puts "Client #{id} disconnected (#{clients.size} clients left)"
else
clients << id #First contact, add client's id to list of connected clients
response_headers = {'Content-Type' => 'text/event-stream'} #SSE MIME type
response_body = "HTTP/1.1 200 OK\r\n#{response_headers.map{|k,v| "#{k}: #{v}" }.join("\r\n")}\r\n\r\n"
response = '%s %d:%s, %s' % [uuid, id.size, id, response_body]
responses.send_msg(response)
puts "Client #{id} connected (currently #{clients.size} clients)"
end
end
c = 0
EM.add_periodic_timer 1 do
event = "event: counter\r\ndata: #{c+=1}\r\n\r\n" #SSE event
ids = clients.join(' ')
# UUID of server from config Send to all connected clients
responses.send_msg('%s %d:%s, %s' % ['7B0A2BF9-0DB2-4FEB-AD90-75C649B859FC', ids.size, ids, event])
end
end#EM.run
require 'eventmachine'
require 'em-zeromq'
require 'json'
require 'securerandom'
require 'base64'
EM.run do
context = EM::ZeroMQ::Context.new(1)
requests = context.socket(ZMQ::PULL)
requests.connect('tcp://127.0.0.1:9999')
responses = context.socket(ZMQ::PUB)
responses.connect('tcp://127.0.0.1:9998')
responses.setsockopt(ZMQ::IDENTITY, SecureRandom.uuid)
clients = {}
require 'em-websocket'
requests.on :message do |msg|
uuid, id, path, rest = msg.copy_out_string.split(' ', 4)
netstrings = []
until rest.empty? #6:{JSON},15:hello my friend,
length = rest[/\A\d+/] #6 #15
rest.slice!(0, length.length+1) #6: #15:
netstrings << rest.slice!(0, length.to_i) #{JSON} #hello my friend
rest.slice!(0) #, #,
end
headers, body = netstrings
headers = JSON.parse(headers)
if headers['METHOD'] == 'WEBSOCKET_HANDSHAKE'
clients[id] = {websocket: EM::WebSocket::Connection.new(id, {})} #One per client
#Override send_data to use responses ZMQ socket
(class << clients[id][:websocket];self;end).send(:define_method, :send_data){|d| responses.send_msg('%s %d:%s, %s' % [uuid, id.size, id, d]) }
headers.delete_if{|k,v| k =~ /\A[A-Z]+\Z/ }#Delete Mongrel2 custom headers
http = "GET / HTTP/1.1\r\n#{headers.map{|k,v| "#{k}: #{v}" }.join("\r\n")}\r\n\r\n#{body}"
clients[id][:websocket].receive_data(http)#Pass HTTP on to em-websocket connection instance
elsif headers['METHOD'] == 'WEBSOCKET'
data = JSON.parse(body)
if data['type'] == 'join'
clients[id][:name] = data['name']
clients.each{|i,h| h[:websocket].send(JSON.generate(type: 'join', name:clients[id][:name])) }
puts "#{data['name']} joined"
elsif data['type'] == 'message'
clients.each{|i,h| h[:websocket].send(JSON.generate(type: 'message', name:clients[id][:name], message:data['message'])) }
puts "#{clients[id][:name]} said: #{data['message']}"
end
else
puts "Received HTTP request"
response_body = "HTTP/1.1 418 I'm a teapot\r\nContent-Length: 0\r\nConnection: close\r\n\r\nNot really, I'm a WebSocket"
responses.send_msg('%s %d:%s, %s' % [uuid, id.size, id, response_body])
end
end
end#EM.run
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment