For another class I had to write a threaded server that would serve up a chat server on port 9020. The server had to give a new user the last 100 lines of chat in that room, and had to update one user’s page even if they didn’t send anything (ie. so they could see the other people’s chat).
This page is OLD and probably contains errors, out of date information, security flaws or other problems. I am keeping it around because it might be helpful to someone.
This server:
- Serves up normal web pages on port 9020
- Searching for index.html if no file is given
- Has a special CHAT resource (visit servername:9020/CHAT to access it) with a chat room
- Has five default chat rooms, more can be added by the users
- Supports smilies
- Uses Ajax for the updates so it is a seamless user experience
If you’re going to try to run it, you probably want to download the HTML and JavaScript for the chat page
#!/usr/bin/env ruby
# By Michael Moore, Brad Yates, and Sean Moe
require 'webrick' # require needed libraries (included in ruby)
include WEBrick
require 'cgi' # easy parsing of QUERY_STRING
def roomlist(whowantsin)
retstring = "<ul>"
$chatrooms.each_key { |key| retstring = retstring + "<li><a href=\"/CHAT?name=#{whowantsin}&room=#{key}\">#{key}</a></li>"}
retstring = retstring + "</ul>"
return retstring
end
def newchatroom(roomname)
$chatrooms[roomname] = Array.new()
$chatrooms[roomname] << ""
end
# map of ascii string to the png number
$smileys = { ":-)" => 1, ":-(" => 2, ";-)" => 3, ":-D" => 5, ":-x" => 7, ":-o" => 12}
$smileystring = ""
$smileys.each_key {|key| $smileystring = $smileystring + "<img src=\"/AIM/#{$smileys[key]}.png\" onclick=\"put('#{key}');\">"}
def smile(line) # do some substitution here to put in smileys
$smileys.each_key {|key| line.gsub!(key,"<img src=\"/AIM/#{$smileys[key]}.png\">")}
line.gsub!("@wesome","<img src=\"/AIM/renshaw.png\">")
return line
end
$chatrooms = Hash.new()#Hash of chat arrays. Each room gets an array.
["default", "Cars", "Dogs", "Science", "Linux"].each { |x| newchatroom(x)}#Create these chat rooms by default
$userhash = Hash.new() # One user per name
class User
def initialize(name)
@myname = name
stamp()
setroom('default') # calls function below to not duplicate functionality
end
def stamp() # update user's timestamp
@last = Time.now
end
def tooold() # Tell if still active
return (Time.now - @last > 1800) # Return true if they've been active in the last 30 mins
end
def setroom(newroom) #change the user's room and their index
if ($chatrooms[newroom] == nil) # Create the chatroom if needed
newchatroom(newroom)
end
@chatroom = $chatrooms[newroom]
@roomname = newroom
@index = @chatroom.length - 100 # give starting point of this user's chat log.
@index = 0 if @index < 0 #if the chat log isn't big, set the index to 0
end
def lines() # return user's chat lines
return smile(@chatroom[@index..(@chatroom.length - 1)].join)
end
def chat(newlines) # Append new chat to whatever room the user is in
@chatroom << "<b>#{@myname}: </b>#{newlines}<br>"
end
def roomname()
return @roomname
end
end
$docroot = "/home/bradkarhu/Desktop/Chatlab" # Set document root as a global variable
server = HTTPServer.new(:Port => 9020,:DocumentRoot => $docroot) # new HTTP server on localhost:9020
class SuperServlet < HTTPServlet::AbstractServlet # Normal HTTP server mode, with index.html as the default file type
def do_GET(req, res) # req == HTTP request object, res == HTTP response object
filename = req.meta_vars["PATH_INFO"].gsub(req.meta_vars["QUERY_STRING"],"") # Get just the requested filename
if filename[filename.length - 1] == '/' # Requested a folder, with no file
filename = req.meta_vars["PATH_INFO"] + "index.html"
elsif filename.index('.') == nil # Requested something, probably a folder
filename = req.meta_vars["PATH_INFO"] + "/index.html"
end
res['content-length'] = File::stat($docroot + filename).size # without this, the browser doesn't know when to quit?
res.body = open($docroot + filename, "rb") # return the file contents
end
end
server.mount("/", SuperServlet) # This applet (SuperServlet) will handle everything by default
class ChatServer < HTTPServlet::AbstractServlet
def do_GET(req,res)
res['Content-Type'] = "text/html" # this applet always returns HTML. JPG and other requests go through SuperServlet
$form_vars = CGI.parse(req.meta_vars["QUERY_STRING"]) # parse QUERY_STRING vars into nice array
uname = $form_vars['name'].to_s
line = $form_vars['line'].to_s
room = $form_vars['room'].to_s
who = $form_vars['chatlines'].to_s
roomlist = $form_vars['roomlist'].to_s
# Return list of chatrooms
if (roomlist != "" && roomlist != nil)
res.body = roomlist(roomlist)
return
end
if (uname != "" && uname != nil) #Got a user var
if($userhash[uname] == nil || $userhash[uname].tooold()) # create user, and assign to room
$userhash[uname] = User.new(uname)
res.body = "<html><head><script type='text/javascript'>window.location='/CHAT?name=#{uname}';</script></head></html>"
return
end
$userhash[uname].stamp() # Always stamp user
if(room != "" && room != nil) #change rooms
$userhash[uname].setroom(room)
end
if(line != "" && line != nil)
$userhash[uname].chat(line)
res.body = "<html><head><script type='text/javascript'>window.location='/CHAT?name=#{uname}';</script></head></html>"
return
end
# Set page body content to mainpage.html, with some substitutions
slurp = File.readlines('mainpage.html').to_s
slurp.gsub!("chattycontent",$userhash[uname].lines())
slurp.gsub!("userformline","<input type=\"hidden\" id=\"name\" name=\"name\" value=\"#{uname}\">")
slurp.gsub!("roomname",$userhash[uname].roomname())
slurp.gsub!("roomlist",roomlist(uname))
slurp.gsub!("happyfaces",$smileystring)
slurp.gsub!("username","<div id=\"hiddenuser\" style=\"visibility: hidden;\">#{uname}</div>")
res.body = slurp
elsif (who != "") # just send back the chatlines
res.body = $userhash[who].lines()
else
res.body = File.readlines('chatform.html').join
end
end
end
server.mount("/CHAT",ChatServer)
trap("INT"){server.shutdown} # Catch ctrl-c to do a clean shutdown
server.start # Start the server!









$yo!I $heard $youdawg $like $globals
Yes. Yes I sure did.
I’ve got treatment though, and I’m better now.