all repos — litestore @ 284ede3eb68ba64f4350a3c2c2f9812c1b8d0860

A minimalist nosql document store.

lib/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
import asynchttpserver2, asyncdispatch, times, strutils, pegs, strtabs, cgi, logging
import types, utils, api_v1

proc getReqInfo(req: Request): string =
  var url = req.url.path
  if req.url.anchor != "":
    url = url & "#" & req.url.anchor
  if req.url.query != "":
    url = url & "?" & req.url.query
  return req.hostname & " " & req.reqMethod & " " & url

proc handleCtrlC() {.noconv.} =
  echo ""
  info("Exiting...")
  quit()

proc parseApiUrl(req: Request): ResourceInfo =
  var matches = @["", "", ""]
  if req.url.path.find(PEG_URL, matches) != -1:
    result.version = matches[0]
    result.resource = matches[1]
    result.id = matches[2]
  else:
    raise newException(EInvalidRequest, req.getReqInfo())

proc route(req: Request, LS: LiteStore): Response =
  if req.url.path == "/favicon.ico":
    result.code = Http200
    result.content = LS.favicon
    result.headers = {"Content-Type": "image/x-icon"}.newStringTable
    return result
  try:
    var info = req.parseApiUrl
    if info.version == "docs" or info.version == "info":
      info.version = "v1"
      info.id = info.resource
      info.resource = "docs"
    if info.version == "v1" and info.resource.match(peg"^docs / info$"):
      return api_v1.route(req, LS, info.resource, info.id)
    else:
      if info.version != "v1":
        return resError(Http400, "Bad request - Invalid API version: $1" % info.version)
      else:
        if info.resource.decodeURL.strip == "":
          return resError(Http400, "Bad request - No resource specified" % info.resource)
        else:
          return resError(Http400, "Bad request - Invalid resource: $1" % info.resource)
  except:
    let e = getCurrentException()
    let trace = e.getStackTrace()
    return resError(Http500, "Internal Server Error: $1" % getCurrentExceptionMsg(), trace)

setControlCHook(handleCtrlC)

proc serve*(LS: LiteStore) =
  var server = newAsyncHttpServer()
  proc handleHttpRequest(req: Request): Future[void] {.async.} =
    info(getReqInfo(req).replace("$", "$$"))
    let res = req.route(LS)
    await req.respond(res.code, res.content, res.headers)
  info(LS.appname & " v" & LS.appversion & " started on " & LS.address & ":" & $LS.port & ".")
  if LS.mount:
    info("Mirroring datastore changes to: " & LS.directory)
  asyncCheck server.serve(LS.port.Port, handleHttpRequest, LS.address)