casualjim
9/9/2011 - 9:33 PM

gistfile1.scala

import javax.servlet.annotation._
import grizzled.generator._
import scala.annotation._
import scala.util.Random

trait Request
case object Request extends Request

trait Handler extends (Request => Response)

case class Response(status: Int = 200, headers: Map[String, String] = Map.empty, body: Chunk)
sealed trait Chunk
case class BodyChunk(content: String, next: () => Chunk = () => Trailer())
  extends Chunk
case class Trailer(headers: Map[String, String] = Map.empty) extends Chunk

object FuckYouHandler extends Handler {
  def apply(req: Request): Response = Response(body = BodyChunk("fuck you"))
}

object RandomHandler extends Handler {
  def apply(req: Request): Response = {
    def chunk(i: Int): Chunk = {
      if (Random.nextDouble < 0.9)
        BodyChunk(i.toString, () => chunk(i + 1))
      else
        Trailer()
    }
    Response(body = chunk(0))
  } 
}

object CensorMiddleware {
  def apply(handler: Handler): Handler = new Handler {
    def apply(req: Request) = {
      val res = handler.apply(req)
      res.copy(body = censor(res.body))
    }
  }

  def censor(chunk: Chunk) = chunk match {
    case BodyChunk(content, next) =>
      BodyChunk(content.replace("fuck", "f***"), next)
    case chunk => chunk
  }
}

class SsgiServlet extends HttpServlet {
  val handler = CensorMiddleware(RandomHandler)

  override def service(sReq: HttpServletRequest, sRes: HttpServletResponse) = {
    val req = Request
    val res = handler(req)
    sRes.setStatus(res.status)
    @tailrec
    def renderChunk(chunk: Chunk): Unit = chunk match {
      case chunk: BodyChunk =>
        sRes.getWriter.println(chunk.content)
        sRes.getWriter.flush()
        renderChunk(chunk.next())
      case Trailer(_) =>
        sRes.getWriter.close()
    }
    renderChunk(res.body)
  }
}