Kurose’s book exercises (labs) — HTTP Web Server

object WebServer {
  def main(args: Array[String]) {
    println("Server listening...")
    val port = getPort(args)
    val socket = new ServerSocket(port)
    process(port, socket)
  }
 
  private def process(port: Int, socket: ServerSocket) {
    while (true) {
      val connection = socket.accept
      val request = new HttpRequest(connection)
      (new Thread(request)).start
    }
  }
 
  private def getPort(args: Array[String]) = {
    if (args.length > 0)
      args(0).toInt
    else
      Configs.HTTP_PORT
  }
}
object Configs {
  val CRLF = "\r\n"
  val HTTP_PORT = 8080
  val BUF_SIZE = 8192
  val MAX_OBJECT_SIZE = 100000
  val HTTP_404 = """
<html>
  <head><title>Object not found</title></head>
  <body><h1>Object not found!</h1>
    <h2>Error 404</h2>
    <address>
      <a href="http://www.sawp.com.br">www.sawp.com.br</a><br />
    </address>
  </body>
</html>"""
}
class HttpRequest(s: Socket) extends Runnable {
  private val is = s.getInputStream
  private val os = new DataOutputStream(s.getOutputStream)
  private val br = new BufferedReader(new InputStreamReader(is))
  private val requestLineHTTPRequest = br.readLine
 
  override def run {
    showServerMessage()
    val fileName = extractFilenameFromRequestLine(requestLineHTTPRequest)
    try {
      val fis = new FileInputStream(fileName)
      processRequestedFile(fis, fileName)
    } catch {
      case e: FileNotFoundException =>
        processFileNotFound()
      case e: Exception => println("Error -> HttpRequest: " + e)
    }
    closeStreamsAndSockets()
    println("-.-" * 20)
    this.finalize
  }
 
  private def processRequestedFile(fis: FileInputStream, fn: String) {
    println("Request file: " + fn)
    val statusLine = "HTTP/1.0 200 OK" + Configs.CRLF
    val contentTypeLine = "Content-Type: " + contentType(fn) + Configs.CRLF
    sendContents(statusLine: String, contentTypeLine: String)
    sendBytes(fis, os)
    fis.close
  }
 
  private def processFileNotFound() {
    println("File not found request")
    val statusLine = "HTTP/1.0 404 Not Found" + Configs.CRLF
    val contentTypeLine = "Content-Type: text/html" + Configs.CRLF
    val entityBody = Configs.HTTP_404
    sendContents(statusLine, contentTypeLine)
    sendEntityBody(entityBody)
  }
 
  private def sendContents(statusLine: String, contentTypeLine: String) {
    os.writeBytes(statusLine)
    os.writeBytes(contentTypeLine)
    os.writeBytes(Configs.CRLF)
  }
 
  private def sendEntityBody(entityBody: String) {
    os.writeBytes(entityBody)
  }
 
  private def closeStreamsAndSockets() {
    os.close
    br.close
    s.close
  }
 
  private def showServerMessage() {
    println("Request received:")
    println(requestLineHTTPRequest)
    var headerLine = br.readLine
    while (headerLine.length != 0) {
      println(headerLine)
      headerLine = br.readLine
    }
  }
 
  private def extractFilenameFromRequestLine(request: String) = {
    val tokens = new StringTokenizer(request)
    tokens.nextToken
    val fileName = "." + tokens.nextToken
    fileName
  }
 
  private def sendBytes(fis: FileInputStream, os: OutputStream) {
    val buffer = new Array[Byte](1024)
    var bytes = 0
    do {
      os.write(buffer, 0, bytes)
      bytes = fis.read(buffer)
    } while (bytes >= 0)
  }
 
  private def contentType(fileName: String): String =
    if (fileName.endsWith(".htm") || fileName.endsWith(".html"))
      "text/html"
    else
      "application/octet-stream"
}