Skip to content

Commit 8f47731

Browse files
author
Eun Woo Song
committed
Content encoding init version
1 parent 01cf492 commit 8f47731

File tree

11 files changed

+281
-123
lines changed

11 files changed

+281
-123
lines changed

project/plugins.sbt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
resolvers += "Sonatype snapshots" at "http://oss.sonatype.org/content/repositories/snapshots/"
2+
13
addSbtPlugin("org.ensime" % "ensime-sbt-cmd" % "0.1.0")
24

3-
addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.1.0")
5+
addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.3.0-SNAPSHOT")

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import akka.util.ByteString
66
* HTTP 상수 모음
77
*/
88
object HttpConstants {
9-
val SP = ByteString(" ")
10-
val HT = ByteString("\t")
11-
val CRLF = ByteString("\r\n")
12-
val COLON = ByteString(":")
13-
val PERCENT = ByteString("%")
14-
val PATH = ByteString("/")
15-
val QUERY = ByteString("?")
9+
val SP = ByteString(" ")
10+
val HT = ByteString("\t")
11+
val CRLF = ByteString("\r\n")
12+
val COLON = ByteString(":")
13+
val PERCENT = ByteString("%")
14+
val PATH = ByteString("/")
15+
val QUERY = ByteString("?")
1616
val RFC1123_DATE_PATTERN = "EEE, dd MMM yyyy HH:mm:ss zzz"
1717
}
1818

Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
/*
22
* This software is licensed under the Apache 2 license, quoted below.
3-
*
3+
*
44
* Copyright 2009-2012 Typesafe Inc. <http://www.typesafe.com>
5-
*
5+
*
66
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
77
* use this file except in compliance with the License. You may obtain a copy of
88
* the License at
9-
*
9+
*
1010
* http://www.apache.org/licenses/LICENSE-2.0
11-
*
11+
*
1212
* Unless required by applicable law or agreed to in writing, software
1313
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1414
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1515
* License for the specific language governing permissions and limitations under
16-
* the License.
16+
* the License.
1717
*/
18-
package com.lascala.http
18+
package com.lascala.http.request
1919

2020
import akka.util.ByteString
2121

@@ -24,15 +24,33 @@ import akka.util.ByteString
2424
* request Header 객체
2525
*/
2626
case class HttpRequest(
27-
meth: String,
28-
path: List[String],
27+
meth: String,
28+
path: List[String],
2929
query: Option[String],
30-
httpver: String,
30+
httpver: String,
3131
headers: Headers,
3232
body: Option[ByteString])
3333

3434
case class Header(name: String, value: String)
3535

36+
object Header {
37+
val VALUE_SEPARATOR = ","
38+
val Q_PARAM_SEPARATOR = ";"
39+
val Q_PARAM = "q="
40+
41+
case class QParamHeader(value: String, qParam: Double)
42+
43+
def parseQparameters(header: Header) = header.value.split(VALUE_SEPARATOR).toSeq.map{
44+
_ match {
45+
case h if h contains(Q_PARAM_SEPARATOR) =>
46+
val Array(value, qParam) = h.split(Q_PARAM_SEPARATOR)
47+
val qRate = if(qParam.contains("q=")) qParam.split("q=").last.toDouble else 1.0
48+
QParamHeader(value.trim, qRate)
49+
case h => QParamHeader(h.trim, 1.0)
50+
}
51+
}.sortWith(_.qParam > _.qParam)
52+
}
53+
3654
case class Headers(headers: List[Header]) {
3755
def get(name: String) = headers.find(_.name.toLowerCase == name.toLowerCase)
3856
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* This software is licensed under the Apache 2 license, quoted below.
3+
*
4+
* Copyright 2009-2012 Typesafe Inc. <http://www.typesafe.com>
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
7+
* use this file except in compliance with the License. You may obtain a copy of
8+
* the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15+
* License for the specific language governing permissions and limitations under
16+
* the License.
17+
*/
18+
package com.lascala.http.response
19+
20+
import akka.util.ByteString
21+
import com.lascala.http.HttpResponse
22+
23+
package error {
24+
case class NotFoundError(body: ByteString = ByteString.empty, shouldKeepAlive: Boolean = false, mimeType: String = "") extends HttpResponse {
25+
val status = ByteString("404")
26+
val reason = ByteString("Not Found")
27+
val lastModified = null
28+
val etag = null
29+
}
30+
31+
case class MethodNotAllowedError(body: ByteString = ByteString.empty, shouldKeepAlive: Boolean = false, mimeType: String = "") extends HttpResponse {
32+
val status = ByteString("405")
33+
val reason = ByteString("Method Not Allowed")
34+
val lastModified = null
35+
val etag = null
36+
}
37+
38+
case class InternalServerError(body: ByteString = ByteString.empty, shouldKeepAlive: Boolean = false, mimeType: String = "") extends HttpResponse {
39+
val status = ByteString("500")
40+
val reason = ByteString("Internal Server Error")
41+
val lastModified = null
42+
val etag = null
43+
}
44+
}
45+
46+
package other {
47+
case class NotModifiedResponse(body: ByteString = ByteString.empty, shouldKeepAlive: Boolean = false, mimeType: String = "") extends HttpResponse {
48+
val status = ByteString("304")
49+
val reason = ByteString("Not Modified")
50+
val lastModified = null
51+
val etag = null
52+
}
53+
}

src/main/scala/com/lascala/http/HttpResponse.scala renamed to src/main/scala/com/lascala/http/response/HttpResponse.scala

Lines changed: 4 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -108,66 +108,15 @@ object HttpResponse {
108108
socket write chunkedMessageBody.compact
109109
}
110110

111-
// According to the HTTP spec, need to write 0 at the end of the chunk data
111+
// According to the HTTP 1.1 spec, need to write 0 at the end of the chunk data
112112
socket write ByteString("0") ++ CRLF ++ CRLF
113113
}
114114
}
115115

116+
/**
117+
* Represents chunked data response
118+
*/
116119
trait ChunkedEncodable extends HttpResponse {
117120
def chunkedData: Enumerator[ByteString]
118121
}
119122

120-
case class OKResponse(body: ByteString = ByteString.empty, shouldKeepAlive: Boolean = true, mimeType: String = "text/html", lastModified: Date = null, etag: ByteString = null) extends HttpResponse {
121-
val status = ByteString("200")
122-
val reason = ByteString("OK")
123-
124-
def withMimeType(mimeType: String) = this match {
125-
// In case of ChunkedEncodable, need to manually instantiate a new OKResponse with ChunkedEncodable
126-
// Instead of just using copy method in order to preserve the ChunkedEncodable type.
127-
case t: ChunkedEncodable => new OKResponse(this.body, this.shouldKeepAlive, mimeType) with ChunkedEncodable {
128-
def chunkedData: Enumerator[ByteString] = t.chunkedData
129-
}
130-
case _ => this.copy(mimeType = mimeType)
131-
}
132-
}
133-
134-
object OKResponse {
135-
def stream(chunk: Enumerator[ByteString]) = new OKResponse(body = ByteString.empty, mimeType = "text/html") with ChunkedEncodable {
136-
def chunkedData: Enumerator[ByteString] = chunk
137-
}
138-
139-
def fromFile(file: File) = new OKResponse(
140-
body = HttpResponse.readFile(file),
141-
shouldKeepAlive = true,
142-
mimeType = new Tika().detect(file),
143-
lastModified = new Date(file.lastModified),
144-
etag = ByteString(HttpResponse.computeETag(file)))
145-
}
146-
147-
case class NotModifiedResponse(body: ByteString = ByteString.empty, shouldKeepAlive: Boolean = false, mimeType: String = "") extends HttpResponse {
148-
val status = ByteString("304")
149-
val reason = ByteString("Not Modified")
150-
val lastModified = null
151-
val etag = null
152-
}
153-
154-
case class NotFoundError(body: ByteString = ByteString.empty, shouldKeepAlive: Boolean = false, mimeType: String = "") extends HttpResponse {
155-
val status = ByteString("404")
156-
val reason = ByteString("Not Found")
157-
val lastModified = null
158-
val etag = null
159-
}
160-
161-
case class MethodNotAllowedError(body: ByteString = ByteString.empty, shouldKeepAlive: Boolean = false, mimeType: String = "") extends HttpResponse {
162-
val status = ByteString("405")
163-
val reason = ByteString("Method Not Allowed")
164-
val lastModified = null
165-
val etag = null
166-
}
167-
168-
case class InternalServerError(body: ByteString = ByteString.empty, shouldKeepAlive: Boolean = false, mimeType: String = "") extends HttpResponse {
169-
val status = ByteString("500")
170-
val reason = ByteString("Internal Server Error")
171-
val lastModified = null
172-
val etag = null
173-
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* This software is licensed under the Apache 2 license, quoted below.
3+
*
4+
* Copyright 2009-2012 Typesafe Inc. <http://www.typesafe.com>
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
7+
* use this file except in compliance with the License. You may obtain a copy of
8+
* the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15+
* License for the specific language governing permissions and limitations under
16+
* the License.
17+
*/
18+
package com.lascala.http.response
19+
20+
import akka.util.ByteString
21+
import com.lascala.http.{ChunkedEncodable, HttpResponse}
22+
import com.lascala.libs.Enumerator
23+
import java.io.File
24+
import java.util.Date
25+
import org.apache.tika.Tika
26+
27+
case class OKResponse(body: ByteString = ByteString.empty, shouldKeepAlive: Boolean = true,
28+
mimeType: String = "text/html", lastModified: Date = null,
29+
etag: ByteString = null) extends HttpResponse {
30+
val status = ByteString("200")
31+
val reason = ByteString("OK")
32+
33+
def withMimeType(mimeType: String) = this match {
34+
// In case of ChunkedEncodable, need to manually instantiate a new OKResponse with ChunkedEncodable
35+
// Instead of just using copy method in order to preserve the ChunkedEncodable type.
36+
case t: ChunkedEncodable =>
37+
new OKResponse(this.body, this.shouldKeepAlive, mimeType) with ChunkedEncodable {
38+
def chunkedData: Enumerator[ByteString] = t.chunkedData
39+
}
40+
case _ => this.copy(mimeType = mimeType)
41+
}
42+
}
43+
44+
object OKResponse {
45+
46+
def stream(chunk: Enumerator[ByteString]) =
47+
new OKResponse(body = ByteString.empty, mimeType = "text/html") with ChunkedEncodable {
48+
def chunkedData: Enumerator[ByteString] = chunk
49+
}
50+
51+
def fromFile(file: File) = new OKResponse(
52+
body = HttpResponse.readFile(file),
53+
shouldKeepAlive = true,
54+
mimeType = new Tika().detect(file),
55+
lastModified = new Date(file.lastModified),
56+
etag = ByteString(HttpResponse.computeETag(file)))
57+
}

0 commit comments

Comments
 (0)