2013年1月2日星期三

How Tomcat Works(Scala语言) 01 一个简单的Web服务器


1 简介
此文算是《How Tomcat Works》的Scala代码解析版。主要从代码角度来分析Tomcat的结构。使用Scala来进行说明。 采用简单说明+代码注释的方式来描述,代码也尽量简单化,做到以最少的文字来进行表述。
2 一个简单的Web服务器
一个静态服务器,根据请求,返回请求的静态文件内容
三个类HttpServer,Response,Request
Request类表示一个请求,根据HTTP协议的请求信息,获取到请求的文件
Response类表示一个响应,将找到的文件内容返回,或者返回404信息
HttpServer类为服务器类,不停的等待请求。当获得一个请求,就组装成Resquest来获取文件,并通过Response返回响应
import java.net.ServerSocket
import java.net.InetAddress
import java.io.File

/**
 * Created with IntelliJ IDEA.
 * User: Ivan
 * Date: 12-11-24
 * Time: 上午10:41
 */
//静态服务器类
class HttpServer {
  var shutdown = false

  //核心方法,接受请求,返回响应
  def await() {
    val port = 8080
    val serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"))

    while (!shutdown) {
      try {
        val socket = serverSocket.accept()
        val input = socket.getInputStream()
        val output = socket.getOutputStream()

        //根据请求构造Request,获取资源文件
        val request: Request = new Request(input)
        request.parse()

        //返回资源文件内容
        val response: Response = new Response(output)
        response.request = request
        response.sendStaticResource()

        socket.close()

        shutdown = request.uri.equals(HttpServer.SHUTDOWN_COMMAND)
      } catch {
        case e: Exception => e.printStackTrace()
      }
    }
  }
}

object HttpServer {
  val WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot"
  val SHUTDOWN_COMMAND = "/SHUTDOWN"

  def main(args: Array[String]) {
    val server = new HttpServer()
    server.await()
  }
}
import io.Source
import java.io.{IOException, InputStream}

/**
 * Created with IntelliJ IDEA.
 * User: Ivan
 * Date: 12-11-24
 * Time: 上午11:36
 */
//请求类,表示一个请求
//主要功能是从输入流中找到请求的文件名
class Request(input: InputStream) {

  var uri: String = _

  //此处本来使用mkString,但是会死这
  //应该是input是流导致的
  def parse() {
    val lines = Source.fromInputStream(input).getLines()
    if (lines.hasNext) {
      uri = parseUri(lines.next())
    }
  }

  /**
   * 从请求中获得请求的资源链接
   * 使用正则表达式组来匹配
   * @param requestString
   * @return
   */
  def parseUri(requestString: String): String = {
    val pattern = """[^ ]* *([^ ]*) *[\s\S]*""".r
    val pattern(result) = requestString
    result
  }
}
import io.Source
import java.io.{File, FileInputStream, OutputStream}

/**
 * Created with IntelliJ IDEA.
 * User: Ivan
 * Date: 12-11-24
 * Time: 上午11:49
 */
//相应类,表示一个相应
//主要功能是向响应流中写入结果
class Response(output: OutputStream) {

  var request: Request = _

  //返回访问的文件,或者404
  def sendStaticResource() {

    val file = new File(HttpServer.WEB_ROOT, request.uri)
    var fis: FileInputStream = null
    try {
      if (file.exists()) {
        fis = new FileInputStream(file)
        //将文件内容写到响应中
        writeToResponse(fis)
      } else {
        //文件未找到,写入404信息
        output.write(Response.FileNotFoundMessage.getBytes())
      }
    } finally {
      if (fis != null)
        fis.close()
    }
  }

  //递归读取文件,写入到响应流
  def writeToResponse(fis:FileInputStream){
      val ch =  fis.read(Response.bytes, 0, Response.BUFFER_SIZE)
      if(ch != -1){
        output.write(Response.bytes, 0, ch)
        writeToResponse(fis)
      }
  }
}

object Response {
  val BUFFER_SIZE = 1024
  //bytes需要在BUFFER_SIZE后定义,否则大小为0
  val bytes = new Array[Byte](Response.BUFFER_SIZE)
  val FileNotFoundMessage = """HTTP/1.1 404 File Not Found
                       |Content-Type: text/html
                       |Content-Length: 23
                       |
                       |<h1>File Not Found</h1>"""
}
Blog URL:http://www.ivanpig.com/blog/?p=485

没有评论:

发表评论