all repos — min @ 31304768b64e342cfff7d67cb7961c018afa2cc5

A small but practical concatenative programming language.

core/server.nim

 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
import 
  net,
  asynchttpserver,
  asyncdispatch,
  httpclient,
  streams,
  critbits,
  pegs,
  strutils

import
  types,
  parser,
  interpreter,
  utils


proc validUrl(req: Request, url: string): bool =
  return req.url.path == url or req.url.path == url & "/"

proc validMethod(req: Request, meth: string): bool =
  return req.reqMethod == meth or req.reqMethod == meth.toLower

proc exec(req: Request, interpreter: MinInterpreter, hosts: CritBitTree[string]): string {.gcsafe.}=
  let filename = "request"
  let s = newStringStream(req.body)
  var i = interpreter
  i.open(s, filename)
  discard i.parser.getToken() 
  i.interpret()
  result = i.dump()
  i.close()

proc process(req: Request, i: MinInterpreter, hosts: CritBitTree[string]): string {.gcsafe.} =
  var matches = @["", "", ""]
  template route(req, peg: expr, op: stmt): stmt {.immediate.}=
    if req.url.path.find(peg, matches) != -1:
      op
  req.route peg"^\/?$":
    if not req.validMethod("GET"):
      raiseServer(Http405, "Method Not Allowed: " & req.reqMethod)
    return "MiNiM Host '$1'" % [i.link.name]
  req.route peg"^\/exec\/?$":
    if not req.validMethod("POST"):
      raiseServer(Http405, "Method Not Allowed: " & req.reqMethod)
    return exec(req, i, hosts)
  raiseServer(Http400, "Bad Request: POST "& req.url.path)
  

proc init*(link: ref MinLink) {.thread.} =
  proc handleHttpRequest(req: Request): Future[void] {.async.} =
    var res: string
    var code: HttpCode = Http200
    try:
      res = req.process(link.interpreter, link.hosts)
    except MinServerError:
      let e: MinServerError = (MinServerError)getCurrentException()
      res = e.msg
      code = e.code
    await req.respond(code, res)
  asyncCheck link.server.serve(link.port, handleHttpRequest, link.address)

proc remoteExec*(i: MinInterpreter, host, content: string): string {.gcsafe.}=
  if i.link.hosts.hasKey(host):
    let url = "http://" & i.link.hosts[host] & "/exec"
    result = url.postContent(body = content, sslContext = nil)
  else:
    raiseServer(Http404, "Not Found: Host '$1'" % [host])

proc syncHosts*(i: MinInterpreter): CritBitTree[string] {.gcsafe.}=
  var cmd = ""
  for key, val in i.link.hosts.pairs:
    cmd = cmd & """ ($1 "$2")""" % [key, val] 
  cmd = "(" & cmd.strip & ") set-hosts"
  for key, val in i.link.hosts.pairs:
    if key != i.link.name:
      result[key] = i.remoteExec(key, cmd)

proc newMinLink*(name, address: string, port: int, i: var MinInterpreter): ref MinLink =
  var link: ref MinLink = new MinLink
  result = link
  result.server = newAsyncHttpServer()
  result.name = name
  result.address = address
  result.port = port.Port
  i.link = result
  result.interpreter = i