PageRenderTime 7ms CodeModel.GetById 2ms app.highlight 2ms RepoModel.GetById 1ms app.codeStats 0ms

/src/fan/Dev.fan

https://bitbucket.org/afrankvt/draft/
Unknown | 203 lines | 176 code | 27 blank | 0 comment | 0 complexity | cee3c91711a85b22c511ef3707d66953 MD5 | raw file
  1//
  2// Copyright (c) 2011, Andy Frank
  3// Licensed under the MIT License
  4//
  5// History:
  6//   6 Jun 2011  Andy Frank  Creation
  7//
  8
  9using concurrent
 10using util
 11using web
 12using wisp
 13
 14//////////////////////////////////////////////////////////////////////////
 15// DevRestarter
 16//////////////////////////////////////////////////////////////////////////
 17
 18** DevRestarter
 19const class DevRestarter : Actor
 20{
 21  new make(ActorPool p, |This| f) : super(p)
 22  {
 23    f(this)
 24  }
 25
 26  ** Check if pods have been modified.
 27  Void checkPods() { send("verify").get(30sec) }
 28
 29  override Obj? receive(Obj? msg)
 30  {
 31    if (msg == "verify")
 32    {
 33      map := Actor.locals["ts"] as Pod:DateTime
 34      if (map == null)
 35      {
 36        startProc
 37        Actor.locals["ts"] = update
 38      }
 39      else if (podsModified(map))
 40      {
 41        log.info("Pods modified, restarting WispService")
 42        stopProc; startProc; Actor.sleep(2sec)
 43        Actor.locals["ts"] = update
 44      }
 45    }
 46    return null
 47  }
 48
 49  ** Update pod modified timestamps.
 50  private Pod:DateTime update()
 51  {
 52    map := Pod:DateTime[:]
 53    Pod.list.each |p| { map[p] = podFile(p).modified }
 54    log.debug("Update pod timestamps ($map.size pods)")
 55    return map
 56  }
 57
 58  ** Return pod file for this Pod.
 59  private File podFile(Pod pod)
 60  {
 61    Env? env := Env.cur
 62    file := env.workDir + `_doesnotexist_`
 63
 64    // walk envs looking for pod file
 65    while (!file.exists && env != null)
 66    {
 67      if (env is PathEnv)
 68      {
 69        ((PathEnv)env).path.eachWhile |p|
 70        {
 71          file = p + `lib/fan/${pod.name}.pod`
 72          return file.exists ? true : null
 73        }
 74      }
 75      else
 76      {
 77        file = env.workDir + `lib/fan/${pod.name}.pod`
 78      }
 79      env = env.parent
 80    }
 81
 82    // verify exists and return
 83    if (!file.exists) throw Err("Pod file not found $pod.name")
 84    return file
 85  }
 86
 87  ** Return true if any pods have been modified since startup.
 88  private Bool podsModified(Pod:DateTime map)
 89  {
 90    true == Pod.list.eachWhile |p|
 91    {
 92      if (podFile(p).modified > map[p])
 93      {
 94        log.debug("$p.name pod has been modified")
 95        return true
 96      }
 97      return null
 98    }
 99  }
100
101  ** Start DraftMod process.
102  private Void startProc()
103  {
104    home := Env.cur.homeDir.osPath
105    args := ["java", "-cp", "${home}/lib/java/sys.jar", "-Dfan.home=$home",
106             "fanx.tools.Fan", "draft", "-port", "$port", "-prod"]
107    if (props != null) args.addAll(["-props", props.osPath])
108    args.add(type.qname)
109    proc := Process(args).run
110
111    Actor.locals["proc"] = proc
112    log.debug("Start external process")
113  }
114
115  ** Stop DraftMod process.
116  private Void stopProc()
117  {
118    proc := Actor.locals["proc"] as Process
119    if (proc == null) return
120    proc.kill
121    log.debug("Stop external process")
122  }
123
124  const Type type
125  const Int port
126  const File? props
127  const Log log := Log.get("draft")
128}
129
130//////////////////////////////////////////////////////////////////////////
131// DevMod
132//////////////////////////////////////////////////////////////////////////
133
134** DevMod
135const class DevMod : WebMod
136{
137  ** Constructor.
138  new make(DevRestarter r)
139  {
140    this.restarter = r
141    this.port = r.port
142  }
143
144  ** Target port to proxy requests to.
145  const Int port
146
147  ** DevRestarter instance.
148  const DevRestarter restarter
149
150  override Void onService()
151  {
152    // check pods
153    restarter.checkPods
154
155    // 13-Jan-2013
156    // Safari seems to have trouble creating seesion cookie
157    // with proxy server - create session here as a workaround
158    dummy := req.session
159
160    // proxy request
161    c := WebClient()
162    c.followRedirects = false
163    c.reqUri = `http://localhost:${port}${req.uri.relToAuth}`
164    c.reqMethod = req.method
165    req.headers.each |v,k|
166    {
167// TODO FIXIT: 4-Jun-2014
168// WebClient does not support sending multiple Set-Cookie headers
169// so can break applications behind the proxy.  This is likley the
170// same problem as the above Safari hack.
171      if (k == "Host") return
172      c.reqHeaders[k] = v
173    }
174    c.writeReq
175
176    is100Continue := c.reqHeaders["Expect"] == "100-continue"
177
178    if (req.method == "POST" && ! is100Continue)
179      c.reqOut.writeBuf(req.in.readAllBuf).flush
180
181    // proxy response
182    c.readRes
183
184    if (is100Continue && c.resCode == 100)
185    {
186      c.reqOut.writeBuf(req.in.readAllBuf).flush
187      c.readRes // final response after the 100continue
188    }
189
190    res.statusCode = c.resCode
191    c.resHeaders.each |v,k|
192    {
193      // we don't re-gzip responses
194      if (k == "Content-Encoding" && v == "gzip") return
195      res.headers[k] = v
196    }
197
198    if (c.resHeaders["Content-Type"]   != null ||
199        c.resHeaders["Content-Length"] != null)
200      res.out.writeBuf(c.resIn.readAllBuf).flush
201    c.close
202  }
203}