fizzbuzz/http-chat-ruby/PirateServer.rb

172 lines
3.9 KiB
Ruby
Raw Normal View History

2010-09-13 01:47:38 +00:00
#!/usr/bin/env ruby
require 'rubygems'
require 'eventmachine'
require 'evma_httpserver'
require 'cgi'
require 'active_support'
PUBLIC_DIR = './public/'
INDEX = PUBLIC_DIR + 'index.html'
CHAT_CLIENT = PUBLIC_DIR + 'chatroom.html'
NOT_FOUND = PUBLIC_DIR + '404.html'
UNPROCESSABLE_ENTITY = PUBLIC_DIR + '422.html'
class ChatRequest < EventMachine::Connection
# HTTPServer handles preprocessing the HTTP headers and such
include EventMachine::HttpServer
# Setup rooms
def initialize
@@rooms = {} unless defined?(@@rooms)
end
def process_http_request
resp = EventMachine::DelegatedHttpResponse.new( self )
# Callback
callback = proc do |res|
resp.send_response
end
# Handler
handler = proc do
@content = ''
@type = 'text/html'
route_request
#resp.headers['Content-Type'] = @type
resp.status = @status
resp.content = @content
end
# defer starts a new Ruby (user-level, not os-level) thread
# By default will allow 20 Ruby threads
EM.defer(handler, callback)
end
# Do Routing
def route_request
puts @http_request_uri + '?' + @http_query_string.to_s
@status = 200
# Ignoring the http verb and assuming GET
case @http_request_uri
when /^\/$/ then index
when /^\/CHAT$/ then chat
when /^\/chat$/ then gossip
else
find_file
end
end
# Routes
def index
@content = open(INDEX).read
end
def client
@content = open(CHAT_CLIENT).read
end
def chat
if @http_query_string.nil?
return client
end
client_name, client_line, room_name = parse_params
if client_name.nil? || client_line.nil?
return bad_request
end
room = find_or_create_room(room_name)
clients = room[:clients] ||= {}
lines = room[:lines] ||= []
# Trim to the last 100 lines
while lines.size >= 100
lines.shift
end
time = Time.now
lines << "(#{time.strftime("%H:%M:%S")}) #{client_name}: #{client_line}"
# Find or create the client
client = clients[client_name] ||= {}
client[:name] ||= client_name
client[:last_seen] ||= time
# Make client leave from all rooms if not recently seen
if client[:last_seen] < 30.minutes.ago
@@rooms.each do |rkey,rhash|
rkey[:clients].reject! do |ukey,uhash|
ukey == client[:name]
end
end
end
gossip
end
# Create the text of the entire chat
def gossip
puts "In gossip"
client_name, client_line, room_name = parse_params
room = find_or_create_room(room_name)
lines = room[:lines] || ["Welcome"]
lines.inspect
@content = "<p>Room: #{room_name}"
lines.each do |line|
@content += '<br/>' + line
end
@content += "</p>"
puts "Out gossip"
end
def bad_request
@status = 422
@content = open(UNPROCESSABLE_ENTITY).read
end
def find_file
unless File.exists?(PUBLIC_DIR + @http_request_uri)
return not_found
end
@content = open(PUBLIC_DIR + @http_request_uri).read
end
def not_found
@status = 404
@content = open(NOT_FOUND).read
end
# Utility Functions
def parse_params
@http_query_string = CGI::unescape(@http_query_string)
if match = /.*name=([^\;]*)/.match(@http_query_string)
client_name = match[1]
end
if match = /.*line=([^\;]*)/.match(@http_query_string)
client_line = match[1]
end
room_name = 'default'
if match = /.*room=([^\;]*)/.match(@http_query_string)
room_name = match[1]
end
client_name ||= nil
client_line ||= nil
room_name ||= nil
return [client_name, client_line, room_name]
end
def find_or_create_room(room_name)
@@rooms[room_name] ||= {}
@@rooms[room_name]
end
end
EventMachine::run {
EventMachine.epoll
# Uses TCP by default. UDP must be specified
EventMachine::start_server("0.0.0.0", 9020, ChatRequest)
puts "Listening..."
}