/src/fan/Dev.fan
https://bitbucket.org/afrankvt/draft/ · Unknown · 203 lines · 176 code · 27 blank · 0 comment · 0 complexity · cee3c91711a85b22c511ef3707d66953 MD5 · raw file
- //
- // Copyright (c) 2011, Andy Frank
- // Licensed under the MIT License
- //
- // History:
- // 6 Jun 2011 Andy Frank Creation
- //
- using concurrent
- using util
- using web
- using wisp
- //////////////////////////////////////////////////////////////////////////
- // DevRestarter
- //////////////////////////////////////////////////////////////////////////
- ** DevRestarter
- const class DevRestarter : Actor
- {
- new make(ActorPool p, |This| f) : super(p)
- {
- f(this)
- }
- ** Check if pods have been modified.
- Void checkPods() { send("verify").get(30sec) }
- override Obj? receive(Obj? msg)
- {
- if (msg == "verify")
- {
- map := Actor.locals["ts"] as Pod:DateTime
- if (map == null)
- {
- startProc
- Actor.locals["ts"] = update
- }
- else if (podsModified(map))
- {
- log.info("Pods modified, restarting WispService")
- stopProc; startProc; Actor.sleep(2sec)
- Actor.locals["ts"] = update
- }
- }
- return null
- }
- ** Update pod modified timestamps.
- private Pod:DateTime update()
- {
- map := Pod:DateTime[:]
- Pod.list.each |p| { map[p] = podFile(p).modified }
- log.debug("Update pod timestamps ($map.size pods)")
- return map
- }
- ** Return pod file for this Pod.
- private File podFile(Pod pod)
- {
- Env? env := Env.cur
- file := env.workDir + `_doesnotexist_`
- // walk envs looking for pod file
- while (!file.exists && env != null)
- {
- if (env is PathEnv)
- {
- ((PathEnv)env).path.eachWhile |p|
- {
- file = p + `lib/fan/${pod.name}.pod`
- return file.exists ? true : null
- }
- }
- else
- {
- file = env.workDir + `lib/fan/${pod.name}.pod`
- }
- env = env.parent
- }
- // verify exists and return
- if (!file.exists) throw Err("Pod file not found $pod.name")
- return file
- }
- ** Return true if any pods have been modified since startup.
- private Bool podsModified(Pod:DateTime map)
- {
- true == Pod.list.eachWhile |p|
- {
- if (podFile(p).modified > map[p])
- {
- log.debug("$p.name pod has been modified")
- return true
- }
- return null
- }
- }
- ** Start DraftMod process.
- private Void startProc()
- {
- home := Env.cur.homeDir.osPath
- args := ["java", "-cp", "${home}/lib/java/sys.jar", "-Dfan.home=$home",
- "fanx.tools.Fan", "draft", "-port", "$port", "-prod"]
- if (props != null) args.addAll(["-props", props.osPath])
- args.add(type.qname)
- proc := Process(args).run
- Actor.locals["proc"] = proc
- log.debug("Start external process")
- }
- ** Stop DraftMod process.
- private Void stopProc()
- {
- proc := Actor.locals["proc"] as Process
- if (proc == null) return
- proc.kill
- log.debug("Stop external process")
- }
- const Type type
- const Int port
- const File? props
- const Log log := Log.get("draft")
- }
- //////////////////////////////////////////////////////////////////////////
- // DevMod
- //////////////////////////////////////////////////////////////////////////
- ** DevMod
- const class DevMod : WebMod
- {
- ** Constructor.
- new make(DevRestarter r)
- {
- this.restarter = r
- this.port = r.port
- }
- ** Target port to proxy requests to.
- const Int port
- ** DevRestarter instance.
- const DevRestarter restarter
- override Void onService()
- {
- // check pods
- restarter.checkPods
- // 13-Jan-2013
- // Safari seems to have trouble creating seesion cookie
- // with proxy server - create session here as a workaround
- dummy := req.session
- // proxy request
- c := WebClient()
- c.followRedirects = false
- c.reqUri = `http://localhost:${port}${req.uri.relToAuth}`
- c.reqMethod = req.method
- req.headers.each |v,k|
- {
- // TODO FIXIT: 4-Jun-2014
- // WebClient does not support sending multiple Set-Cookie headers
- // so can break applications behind the proxy. This is likley the
- // same problem as the above Safari hack.
- if (k == "Host") return
- c.reqHeaders[k] = v
- }
- c.writeReq
- is100Continue := c.reqHeaders["Expect"] == "100-continue"
- if (req.method == "POST" && ! is100Continue)
- c.reqOut.writeBuf(req.in.readAllBuf).flush
- // proxy response
- c.readRes
- if (is100Continue && c.resCode == 100)
- {
- c.reqOut.writeBuf(req.in.readAllBuf).flush
- c.readRes // final response after the 100continue
- }
- res.statusCode = c.resCode
- c.resHeaders.each |v,k|
- {
- // we don't re-gzip responses
- if (k == "Content-Encoding" && v == "gzip") return
- res.headers[k] = v
- }
- if (c.resHeaders["Content-Type"] != null ||
- c.resHeaders["Content-Length"] != null)
- res.out.writeBuf(c.resIn.readAllBuf).flush
- c.close
- }
- }