Skip to content

Commit 8cbc9c6

Browse files
committed
cache: Handle Last-Modified and If-Modified-Since.
This solves #13.
1 parent 43ad2f6 commit 8cbc9c6

File tree

2 files changed

+53
-38
lines changed

2 files changed

+53
-38
lines changed

src/main/scala/com/lascala/http/HttpResponse.scala

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,47 +17,85 @@
1717
*/
1818
package com.lascala.http
1919

20+
import com.lascala.http.HttpConstants._
21+
2022
import akka.util.{ ByteString, ByteStringBuilder }
21-
import HttpConstants._
23+
import java.io.File
24+
import org.apache.tika.Tika
25+
import java.io.FileInputStream
26+
import java.util.Date
27+
import java.util.Locale
28+
import java.text.SimpleDateFormat
29+
import java.util.TimeZone
2230

2331
trait HttpResponse {
32+
def lastModified: Date = null
2433
def body: ByteString
2534
def status: ByteString
2635
def reason: ByteString
2736
def mimeType: String
2837
def shouldKeepAlive: Boolean
2938

30-
// default charset to utf-8 for now but should be editable in the future
31-
def contentType = if (!mimeType.isEmpty) ByteString(s"Content-Type: ${mimeType}") else ByteString("")
39+
def contentType = ByteString(s"Content-Type: ${mimeType}")
3240
def cacheControl = ByteString("Cache-Control: no-cache")
3341
def contentLength = ByteString(s"Content-Length: ${body.length.toString}")
3442
}
3543

3644
object HttpResponse {
3745
val version = ByteString("HTTP/1.1")
38-
val date = ByteString("Date: ")
3946
val server = ByteString("Server: lascala-http")
4047
val connection = ByteString("Connection: ")
4148
val keepAlive = ByteString("Keep-Alive")
4249
val close = ByteString("Close")
43-
50+
val date = ByteString("Date: ")
51+
val lastModified = ByteString("Last-Modified: ")
52+
53+
def httpDateFormat = {
54+
val dateFormat = new SimpleDateFormat(RFC1123_DATE_PATTERN, Locale.ENGLISH)
55+
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"))
56+
dateFormat
57+
}
58+
59+
def httpDate(date: Date) = ByteString(httpDateFormat.format(date))
60+
4461
def bytes(rsp: HttpResponse) = {
4562
(new ByteStringBuilder ++=
4663
version ++= SP ++= rsp.status ++= SP ++= rsp.reason ++= CRLF ++=
47-
(if(rsp.body.nonEmpty) rsp.contentType ++ CRLF else ByteString.empty) ++=
64+
(if(rsp.body.nonEmpty || rsp.mimeType.nonEmpty) rsp.contentType ++ CRLF else ByteString.empty) ++=
4865
rsp.cacheControl ++= CRLF ++=
49-
date ++= ByteString(new java.util.Date().toString) ++= CRLF ++=
66+
date ++= httpDate(new Date) ++= CRLF ++=
67+
Option(rsp.lastModified).map(lastModified ++ httpDate(_) ++ CRLF).getOrElse(ByteString("")) ++=
5068
server ++= CRLF ++=
5169
rsp.contentLength ++= CRLF ++=
5270
connection ++= (if (rsp.shouldKeepAlive) keepAlive else close) ++= CRLF ++= CRLF ++= rsp.body).result
5371
}
5472
}
5573

74+
case class OKFileResponse(file: File, shouldKeepAlive: Boolean = true) extends HttpResponse {
75+
def readFile(file: File) = {
76+
val resource = new Array[Byte](file.length.toInt)
77+
val in = new FileInputStream(file)
78+
in.read(resource)
79+
in.close()
80+
ByteString(resource)
81+
}
82+
val body = readFile(file)
83+
val mimeType = new Tika().detect(file)
84+
val status = ByteString("200")
85+
val reason = ByteString("OK")
86+
override def lastModified = new Date(file.lastModified)
87+
}
88+
5689
case class OKResponse(body: ByteString, shouldKeepAlive: Boolean = true, mimeType: String = "text/html") extends HttpResponse {
5790
val status = ByteString("200")
5891
val reason = ByteString("OK")
5992
}
6093

94+
case class NotModifiedResponse(body: ByteString = ByteString.empty, shouldKeepAlive: Boolean = false, mimeType: String = "") extends HttpResponse {
95+
val status = ByteString("304")
96+
val reason = ByteString("Not Modified")
97+
}
98+
6199
case class NotFoundError(body: ByteString = ByteString.empty, shouldKeepAlive: Boolean = false, mimeType: String = "") extends HttpResponse {
62100
val status = ByteString("404")
63101
val reason = ByteString("Not Found")
@@ -72,18 +110,3 @@ case class InternalServerError(body: ByteString = ByteString.empty, shouldKeepAl
72110
val status = ByteString("500")
73111
val reason = ByteString("Internal Server Error")
74112
}
75-
76-
/**
77-
* HTTP 상수 모음
78-
*/
79-
object HttpConstants {
80-
val SP = ByteString(" ")
81-
val HT = ByteString("\t")
82-
val CRLF = ByteString("\r\n")
83-
val COLON = ByteString(":")
84-
val PERCENT = ByteString("%")
85-
val PATH = ByteString("/")
86-
val QUERY = ByteString("?")
87-
}
88-
89-

src/main/scala/common/main.scala

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,9 @@ package common
2626
import akka.actor._
2727
import com.lascala.http._
2828
import com.lascala.http.HttpResponse._
29+
import com.lascala.http.HttpConstants._
2930
import akka.util.ByteString
3031
import java.io.File
31-
import org.apache.tika.Tika
32-
import java.io.FileInputStream
3332

3433
/**
3534
* Sample Demo Application
@@ -43,24 +42,17 @@ object Main extends App {
4342
class RequestHandler extends Actor {
4443
val docroot = "."
4544

46-
def readFile(file: File) = {
47-
val resource = new Array[Byte](file.length.toInt)
48-
val in = new FileInputStream(file)
49-
in.read(resource)
50-
in.close()
51-
ByteString(resource)
52-
}
53-
54-
def mimeType(file: File) = new Tika().detect(file)
55-
5645
def receive = {
57-
case HttpRequest("GET", pathSegments, _, _, _, _) =>
46+
case HttpRequest("GET", pathSegments, _, _, headers, _) =>
5847
new File(docroot, "/" + pathSegments.mkString(File.separator)) match {
5948
case file if file.isFile() =>
60-
sender ! OKResponse(readFile(file), true, mimeType(file))
49+
headers.find(_.name.toLowerCase == "if-modified-since") match {
50+
case Some(d) if HttpResponse.httpDateFormat.parse(d.value).compareTo(new java.util.Date(file.lastModified)) != -1 => sender ! NotModifiedResponse()
51+
case _ => sender ! OKFileResponse(file, true)
52+
}
6153
case _ =>
62-
sender ! NotFoundError
54+
sender ! NotFoundError()
6355
}
64-
case _ => sender ! MethodNotAllowedError
56+
case _ => sender ! MethodNotAllowedError()
6557
}
6658
}

0 commit comments

Comments
 (0)