/
ShutdownHandler.scala
77 lines (71 loc) · 2.5 KB
/
ShutdownHandler.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
package com.twitter.server.handler
import com.twitter.app.App
import com.twitter.finagle.Service
import com.twitter.finagle.http.Method
import com.twitter.finagle.http.Request
import com.twitter.finagle.http.Response
import com.twitter.finagle.http.Status
import com.twitter.finagle.http.Uri
import com.twitter.io.Buf
import com.twitter.server.util.HttpUtils.newOk
import com.twitter.server.util.HttpUtils.newResponse
import com.twitter.util.logging.Logger
import com.twitter.util.Duration
import com.twitter.util.Future
import com.twitter.util.Local
class ShutdownHandler(app: App) extends Service[Request, Response] {
private[this] val log = Logger[ShutdownHandler]
protected def getGraceParam(request: Request): Option[String] =
Uri.fromRequest(request).params.get("grace")
def apply(req: Request): Future[Response] = {
if (req.method == Method.Post) {
val specifiedGracePeriod = getGraceParam(req).map { d =>
try Duration.parse(d)
catch {
case e: NumberFormatException =>
val msg = s"could not parse 'grace' parameter: $d is not a valid duration"
log.info(
s"[${req.uri}] from ${req.remoteAddress.getHostAddress} " +
s"failed to quit due to invalid grace period format",
e
)
return newResponse(
status = Status.BadRequest,
contentType = "text/plain;charset=UTF-8",
content = Buf.Utf8(msg)
)
}
}
val grace = specifiedGracePeriod.getOrElse(app.defaultCloseGracePeriod)
log.info(
s"[${req.uri}] from ${req.remoteAddress.getHostAddress} " +
s"quitting with grace period $grace"
)
// Isolate a current thread (likely netty IO thread)
// from probable blocking calls inside app.close.
val ctx = Local.save()
new Thread(
() =>
try {
Local.restore(ctx)
app.close(grace)
} catch {
case th: Throwable =>
log.error("Exception when asking an app to close", th)
},
"shutdown-handler" // thread name
).start()
newOk("quitting\n")
} else {
log.info(
s"ignoring [${req.uri}] from ${req.remoteAddress.getHostAddress}, because it is a ${req.method}, not a POST"
)
newResponse(
status = Status.MethodNotAllowed,
headers = Seq(("Allow", "POST")),
contentType = "text/plain;charset=UTF-8",
content = Buf.Empty
)
}
}
}