1
1
/**
2
2
* la-scala http server
3
3
* source from http://doc.akka.io/docs/akka/2.1.0/scala/io.html
4
- */
4
+ */
5
5
6
6
/*
7
7
* This software is licensed under the Apache 2 license, quoted below.
@@ -63,7 +63,7 @@ object HttpIteratees {
63
63
body <- readBody(headers)
64
64
} yield Request (meth, path, query, httpver, headers, body)
65
65
}
66
-
66
+
67
67
def ascii (bytes : ByteString ): String = bytes.decodeString(" US-ASCII" ).trim
68
68
69
69
def readRequestLine = {
@@ -74,25 +74,17 @@ object HttpIteratees {
74
74
httpver <- IO takeUntil CRLF
75
75
} yield (ascii(meth), uri, ascii(httpver))
76
76
}
77
-
78
- def readRequestURI = IO peek 1 flatMap {
79
- case PATH => {
80
- for {
81
- path <- readPath
82
- query <- readQuery
83
- } yield (path, query)
84
- }
85
-
86
- case _ => {
87
- sys.error(" Not Implemented" )
77
+
78
+ def readRequestURI = {
79
+ IO peek 1 flatMap {
80
+ case PATH =>
81
+ for {
82
+ path <- readPath
83
+ query <- readQuery
84
+ } yield (path, query)
85
+ case _ => sys.error(" Not Implemented" )
88
86
}
89
87
}
90
-
91
- def readBody (headers : List [Header ]) =
92
- if (headers.exists(header => header.name == " Content-Length" || header.name == " Transfer-Encoding" ))
93
- IO .takeAll map (Some (_))
94
- else
95
- IO Done None
96
88
97
89
def readPath = {
98
90
def step (segments : List [String ]): IO .Iteratee [List [String ]] = IO peek 1 flatMap {
@@ -104,30 +96,36 @@ object HttpIteratees {
104
96
}
105
97
step(Nil )
106
98
}
107
-
108
- def readQuery : IO .Iteratee [Option [String ]] = IO peek 1 flatMap {
109
- case QUERY => IO drop 1 flatMap (_ => readUriPart(querychar) map (Some (_)))
110
- case _ => IO Done None
99
+
100
+ def readQuery : IO .Iteratee [Option [String ]] = {
101
+ IO peek 1 flatMap {
102
+ case QUERY => IO drop 1 flatMap (_ => readUriPart(querychar) map (Some (_)))
103
+ case _ => IO Done None
104
+ }
111
105
}
112
-
106
+
113
107
val alpha = Set .empty ++ ('a' to 'z' ) ++ ('A' to 'Z' ) map (_.toByte)
114
108
val digit = Set .empty ++ ('0' to '9' ) map (_.toByte)
115
109
val hexdigit = digit ++ (Set .empty ++ ('a' to 'f' ) ++ ('A' to 'F' ) map (_.toByte))
116
110
val subdelim = Set ('!' , '$' , '&' , '\' ' , '(' , ')' , '*' , '+' , ',' , ';' , '=' ) map (_.toByte)
117
111
val pathchar = alpha ++ digit ++ subdelim ++ (Set (':' , '@' ) map (_.toByte))
118
112
val querychar = pathchar ++ (Set ('/' , '?' ) map (_.toByte))
119
113
120
- def readUriPart (allowed : Set [Byte ]): IO .Iteratee [String ] = for {
121
- str <- IO takeWhile allowed map ascii
122
- pchar <- IO peek 1 map (_ == PERCENT )
123
- all <- if (pchar) readPChar flatMap (ch => readUriPart(allowed) map (str + ch + _)) else IO Done str
124
- } yield all
114
+ def readUriPart (allowed : Set [Byte ]): IO .Iteratee [String ] = {
115
+ for {
116
+ str <- IO takeWhile allowed map ascii
117
+ pchar <- IO peek 1 map (_ == PERCENT )
118
+ all <- if (pchar) readPChar flatMap (ch => readUriPart(allowed) map (str + ch + _)) else IO Done str
119
+ } yield all
120
+ }
125
121
126
- def readPChar = IO take 3 map {
127
- case Seq ('%' , rest @ _* ) if rest forall hexdigit =>
128
- java.lang.Integer .parseInt(rest map (_.toChar) mkString, 16 ).toChar
122
+ def readPChar = {
123
+ IO take 3 map {
124
+ case Seq ('%' , rest @ _* ) if rest forall hexdigit =>
125
+ java.lang.Integer .parseInt(rest map (_.toChar) mkString, 16 ).toChar
126
+ }
129
127
}
130
-
128
+
131
129
def readHeaders = {
132
130
def step (found : List [Header ]): IO .Iteratee [List [Header ]] = {
133
131
IO peek 2 flatMap {
@@ -139,22 +137,23 @@ object HttpIteratees {
139
137
}
140
138
141
139
def readHeader = {
142
- for {
140
+ for {
143
141
name <- IO takeUntil COLON
144
142
value <- IO takeUntil CRLF flatMap readMultiLineValue
145
143
} yield Header (ascii(name), ascii(value))
146
144
}
147
145
148
- def readMultiLineValue (initial : ByteString ): IO .Iteratee [ByteString ] = {
149
- IO peek 1 flatMap {
150
- case SP => IO takeUntil CRLF flatMap (bytes => readMultiLineValue(initial ++ bytes))
151
- }
146
+ def readMultiLineValue (initial : ByteString ): IO .Iteratee [ByteString ] = IO peek 1 flatMap {
147
+ case SP => IO takeUntil CRLF flatMap (bytes => readMultiLineValue(initial ++ bytes))
148
+ case _ => IO Done initial
149
+ }
150
+
151
+ def readBody (headers : List [Header ]) = {
152
+ if (headers.exists(header => header.name == " Content-Length" || header.name == " Transfer-Encoding" )) IO .takeAll map (Some (_))
153
+ else IO Done None
152
154
}
153
155
}
154
156
155
- /**
156
- * ok 응답
157
- */
158
157
object OKResponse {
159
158
import HttpConstants .CRLF
160
159
@@ -176,74 +175,70 @@ object OKResponse {
176
175
date ++= ByteString (new java.util.Date ().toString) ++= CRLF ++=
177
176
server ++= CRLF ++=
178
177
contentLength ++= ByteString (rsp.body.length.toString) ++= CRLF ++=
179
- connection ++= (if (rsp.keepAlive) keepAlive else close) ++= CRLF ++=
180
- CRLF ++= rsp.body result
178
+ connection ++= (if (rsp.keepAlive) keepAlive else close) ++= CRLF ++= CRLF ++= rsp.body result
181
179
}
182
180
}
183
181
184
182
case class OKResponse (body : ByteString , keepAlive : Boolean )
185
183
186
184
/**
187
- * http server companion object
188
- */
185
+ * http server companion object
186
+ */
189
187
object HttpServer {
190
- import HttpIteratees ._
191
-
192
- def processRequest (socket : IO .SocketHandle ): IO .Iteratee [Unit ] = {
193
- IO repeat {
194
- for {
195
- request <- readRequest
196
- } yield {
197
- val rsp = request match {
198
- case Request (" GET" , " ping" :: Nil , _, _, headers, _) => {
199
- OKResponse (ByteString (" <p>pong</p>" ), request.headers.exists {
200
- case Header (n, v) => n.toLowerCase == " connection" && v.toLowerCase == " keep-alive" })
188
+ import HttpIteratees ._
189
+
190
+ def processRequest (socket : IO .SocketHandle ): IO .Iteratee [Unit ] = {
191
+ IO repeat {
192
+ for {
193
+ request <- readRequest
194
+ } yield {
195
+ val rsp = request match {
196
+ case Request (" GET" , " ping" :: Nil , _, _, headers, _) => {
197
+ OKResponse (ByteString (" <p>pong</p>" ), request.headers.exists { case Header (n, v) => n.toLowerCase == " connection" && v.toLowerCase == " keep-alive" })
201
198
}
202
- case req => {
203
- OKResponse (ByteString (" <p>" + req.toString + " </p>" ), request.headers.exists {
204
- case Header (n, v) => n.toLowerCase == " connection" && v.toLowerCase == " keep-alive" })
199
+ case req => {
200
+ OKResponse (ByteString (" <p>" + req.toString + " </p>" ), request.headers.exists { case Header (n, v) => n.toLowerCase == " connection" && v.toLowerCase == " keep-alive" })
205
201
}
206
- }
207
- socket write OKResponse .bytes(rsp).compact
208
- if (! rsp.keepAlive) socket.close()
209
- }
210
- }
202
+ }
203
+ socket write OKResponse .bytes(rsp).compact
204
+ if (! rsp.keepAlive) socket.close()
205
+ }
206
+ }
211
207
}
212
208
}
213
209
214
210
/**
215
211
* http server main
216
212
*/
217
213
class HttpServer (port : Int ) extends Actor {
218
- val state = IO .IterateeRef .Map .async[IO .Handle ]()(context.dispatcher)
214
+ val state = IO .IterateeRef .Map .async[IO .Handle ]()(context.dispatcher)
219
215
220
- override def preStart {
221
- IOManager (context.system) listen new InetSocketAddress (port)
222
- }
216
+ override def preStart {
217
+ IOManager (context.system) listen new InetSocketAddress (port)
218
+ }
223
219
224
- def receive = {
225
- case IO .NewClient (server) => {
220
+ def receive = {
221
+ case IO .NewClient (server) => {
226
222
val socket = server.accept()
227
223
state(socket) flatMap (_ => HttpServer .processRequest(socket))
228
224
}
229
-
230
- case IO .Read (socket, bytes) => {
225
+
226
+ case IO .Read (socket, bytes) => {
231
227
state(socket)(IO Chunk bytes)
232
228
}
233
229
234
- case IO .Closed (socket, cause) => {
230
+ case IO .Closed (socket, cause) => {
235
231
state(socket)(IO EOF )
236
232
state -= socket
237
233
}
238
- }
234
+ }
239
235
}
240
236
241
-
242
237
/**
243
238
* Main
244
239
*/
245
240
object Main extends App {
246
- val port = Option (System .getenv(" PORT" )) map (_.toInt) getOrElse 8080
247
- val system = ActorSystem ()
248
- val server = system.actorOf(Props (new HttpServer (port)))
241
+ val port = Option (System .getenv(" PORT" )) map (_.toInt) getOrElse 8080
242
+ val system = ActorSystem ()
243
+ val server = system.actorOf(Props (new HttpServer (port)))
249
244
}
0 commit comments