Kurose’s book exercises (labs) — HTTP Proxy Cache

These codes are implementations of the exercise described here: http://www.cs.uic.edu/~troy/spring05/cs450/mp1.html

HttpResponse

class HttpResponse(fromServer: DataInputStream) {
  var version = ""
  var status = 0
  var statusLine = ""
  var headers = ""
  var body = new Array[Byte](Configs.MAX_OBJECT_SIZE)
  var length = -1
  try {
    var line = fromServer.readLine
    var gotStatusLine = false
    while (line.length != 0) {
      if (!gotStatusLine) {
        statusLine = line
        gotStatusLine = true
      } else {
        headers += line + Configs.CRLF
      }
      if (line.startsWith("Content-Length:") ||
        line.startsWith("Content-length:")) {
        val tmp = line.split(" ")
        length = tmp(1).toInt
      }
      line = fromServer.readLine
    }
  } catch {
    case e: IOException =>
      println("Error reading headers from server: " + e)
  }
  try {
    var bytesRead = 0
    var buf = new Array[Byte](Configs.BUF_SIZE)
    var loop = false
    if (length == -1)
      loop = true
    breakable {
      while (bytesRead < length || loop) {
        val res = fromServer.read(buf, 0, Configs.BUF_SIZE)
        if (res == 1)
          break
        var i = 0
        while (i < res && (i + bytesRead) < Configs.MAX_OBJECT_SIZE) {
          body(bytesRead + i) = buf(i)
          i += 1
        }
        bytesRead += res
      }
    }
  } catch {
    case e: IOException =>
      println("Error reading headers from body: " + e)
  }
 
  override def toString = {
    val res = statusLine + Configs.CRLF + headers + Configs.CRLF
    res
  }
}

HttpRequest

class HttpRequest(from: BufferedReader) {
  private var method = ""
  private var URI = ""
  private var version = ""
  private var headers = ""
  private var _host = ""
  private var _port = Configs.HTTP_PORT
  try {
    val firstLine = from.readLine
    val tmp = firstLine.split(" ")
    method = tmp(0)
    URI = tmp(1)
    version = tmp(2)
  } catch {
    case e: IOException =>
      println("Error reading request line: " + e)
  }
  println("URI:" + URI)
  if (!(method equals "GET"))
    println("Error: Method not GET")
  try {
    var line = from.readLine
    while (line.length != 0) {
      headers += line + Configs.CRLF
      if (line startsWith "Host:") {
        val tmp = line.split(" ")
        if (tmp(1).indexOf(':') > 0) {
          val tmp2 = tmp(1).split(":")
          _host = tmp2(0)
          _port = tmp2(1).toInt
        } else {
          _host = tmp(1)
          _port = Configs.HTTP_PORT
        }
      }
    }
  } catch {
    case e: IOException =>
      println("Error reading from socket: " + e)
  }
 
  def host = _host
 
  def port = _port
 
  override def toString = {
    var req = method + " " + URI + " " + version + Configs.CRLF
    req += headers
    req += "Connection: close" + Configs.CRLF + Configs.CRLF
    req
  }
}

ProxyCache

class ProxyCache(port: Int) {
  var socket: ServerSocket = null.asInstanceOf[ServerSocket]
  try {
    socket = new ServerSocket(port)
  } catch {
    case e: IOException =>
      println("Socket creation error: " + e)
      exit(-1)
  }
 
  def handle(client: Socket) {
    var server: Socket = null.asInstanceOf[Socket]
    var request: HttpRequest = null.asInstanceOf[HttpRequest]
    try {
      val fromClient =
        new BufferedReader(new InputStreamReader(client.getInputStream))
      println("Reading request...")
      request = new HttpRequest(fromClient)
      println("Got request...")
    } catch {
      case e: IOException =>
        println("Error reading request from client: " + e)
        return
    }
 
    try {
      server = new Socket(request.host, request.port)
      val toServer = new DataOutputStream(server.getOutputStream)
      toServer.writeBytes(request.toString)
    } catch {
      case e: UnknownHostException =>
        println("Unknown host: " + request.host)
        println(e)
        return
      case e: IOException =>
        println("Error writing request to server: " + e)
        return
    }
 
    try {
      val fromServer = new DataInputStream(server.getInputStream)
      val response = new HttpResponse(fromServer)
      val toClient = new DataOutputStream(client.getOutputStream)
      toClient.writeBytes(response.toString)
      toClient.write(response.body)
      client.close
      server.close
    } catch {
      case e: IOException =>
        println("Error writing response to client: " + e)
    }
  }
}

Configs

object Configs {
  val CRLF = "\r\n"
  val HTTP_PORT = 8080
  val BUF_SIZE = 8192
  val MAX_OBJECT_SIZE = 100000
}

The Main

object Main {
  def main(args: Array[String]) {
    val myPort = args(0).toInt
    val proxyCache = new ProxyCache(myPort)
    println("Cache started... Port: " + myPort)
    while (true) {
      try {
        val client = proxyCache.socket.accept
        println("Got connection " + client)
        proxyCache.handle(client)
      } catch {
        case e: IOException =>
          println("Error reading request from client: " + e)
      }
    }
  }
}

These codes can be found here: ProxyCache.scala