Ruby Chat/Web Server

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!

This entry was posted in Programming and tagged , , , . Bookmark the permalink.

2 Responses to Ruby Chat/Web Server

  1. Jota says:

    $yo!I $heard $youdawg $like $globals

Leave a Reply

Your email address will not be published. Required fields are marked *