This repository has been archived by the owner on Apr 24, 2024. It is now read-only.
/
DemoService.scala
131 lines (109 loc) · 4.68 KB
/
DemoService.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package spray.examples
import scala.concurrent.duration._
import akka.pattern.ask
import akka.util.Timeout
import akka.actor._
import spray.can.Http
import spray.can.server.Stats
import spray.util._
import spray.http._
import HttpMethods._
import MediaTypes._
class DemoService extends Actor with ActorLogging {
implicit val timeout: Timeout = 1.second // for the actor 'asks'
import context.dispatcher // ExecutionContext for the futures and scheduler
def receive = {
// when a new connection comes in we register ourselves as the connection handler
case _: Http.Connected => sender ! Http.Register(self)
case HttpRequest(GET, Uri.Path("/"), _, _, _) =>
sender ! index
case HttpRequest(GET, Uri.Path("/ping"), _, _, _) =>
sender ! HttpResponse(entity = "PONG!")
case HttpRequest(GET, Uri.Path("/stream"), _, _, _) =>
val peer = sender // since the Props creator is executed asyncly we need to save the sender ref
context actorOf Props(new Streamer(peer, 25))
case HttpRequest(GET, Uri.Path("/server-stats"), _, _, _) =>
val client = sender
context.actorFor("/user/IO-HTTP/listener-0") ? Http.GetStats onSuccess {
case x: Stats => client ! statsPresentation(x)
}
case HttpRequest(GET, Uri.Path("/crash"), _, _, _) =>
sender ! HttpResponse(entity = "About to throw an exception in the request handling actor, " +
"which triggers an actor restart")
sys.error("BOOM!")
case HttpRequest(GET, Uri.Path(path), _, _, _) if path startsWith "/timeout" =>
log.info("Dropping request, triggering a timeout")
case HttpRequest(GET, Uri.Path("/stop"), _, _, _) =>
sender ! HttpResponse(entity = "Shutting down in 1 second ...")
context.system.scheduler.scheduleOnce(1.second) { context.system.shutdown() }
case _: HttpRequest => sender ! HttpResponse(status = 404, entity = "Unknown resource!")
case Timedout(HttpRequest(_, Uri.Path("/timeout/timeout"), _, _, _)) =>
log.info("Dropping Timeout message")
case Timedout(HttpRequest(method, uri, _, _, _)) =>
sender ! HttpResponse(
status = 500,
entity = "The " + method + " request to '" + uri + "' has timed out..."
)
}
////////////// helpers //////////////
lazy val index = HttpResponse(
entity = HttpEntity(`text/html`,
<html>
<body>
<h1>Say hello to <i>spray-can</i>!</h1>
<p>Defined resources:</p>
<ul>
<li><a href="/ping">/ping</a></li>
<li><a href="/stream">/stream</a></li>
<li><a href="/server-stats">/server-stats</a></li>
<li><a href="/crash">/crash</a></li>
<li><a href="/timeout">/timeout</a></li>
<li><a href="/timeout/timeout">/timeout/timeout</a></li>
<li><a href="/stop">/stop</a></li>
</ul>
</body>
</html>.toString()
)
)
def statsPresentation(s: Stats) = HttpResponse(
entity = HttpEntity(`text/html`,
<html>
<body>
<h1>HttpServer Stats</h1>
<table>
<tr><td>uptime:</td><td>{s.uptime.formatHMS}</td></tr>
<tr><td>totalRequests:</td><td>{s.totalRequests}</td></tr>
<tr><td>openRequests:</td><td>{s.openRequests}</td></tr>
<tr><td>maxOpenRequests:</td><td>{s.maxOpenRequests}</td></tr>
<tr><td>totalConnections:</td><td>{s.totalConnections}</td></tr>
<tr><td>openConnections:</td><td>{s.openConnections}</td></tr>
<tr><td>maxOpenConnections:</td><td>{s.maxOpenConnections}</td></tr>
<tr><td>requestTimeouts:</td><td>{s.requestTimeouts}</td></tr>
</table>
</body>
</html>.toString()
)
)
class Streamer(client: ActorRef, count: Int) extends Actor with ActorLogging {
log.debug("Starting streaming response ...")
// we use the successful sending of a chunk as trigger for scheduling the next chunk
client ! ChunkedResponseStart(HttpResponse(entity = " " * 2048)).withAck(Ok(count))
def receive = {
case Ok(0) =>
log.info("Finalizing response stream ...")
client ! MessageChunk("\nStopped...")
client ! ChunkedMessageEnd
context.stop(self)
case Ok(remaining) =>
log.info("Sending response chunk ...")
context.system.scheduler.scheduleOnce(100 millis span) {
client ! MessageChunk(DateTime.now.toIsoDateTimeString + ", ").withAck(Ok(remaining - 1))
}
case x: Http.ConnectionClosed =>
log.info("Canceling response stream due to {} ...", x)
context.stop(self)
}
// simple case class whose instances we use as send confirmation message for streaming chunks
case class Ok(remaining: Int)
}
}