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 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
import asynchttpserver2, asyncdispatch, times, strutils, pegs, strtabs, #logging, cgi 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 processApiUrl(req: Request, LS: LiteStore, info: ResourceInfo): Response = 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) proc process(req: Request, LS: LiteStore): Response {.gcsafe.}= var matches = @["", "", ""] template route(req, peg: expr, op: stmt): stmt {.immediate.}= if req.url.path.find(peg, matches) != -1: op try: var info: ResourceInfo = (version: "", resource: "", id: "") req.route peg"^\/?$": info.version = "v1" info.resource = "info" return req.processApiUrl(LS, info) req.route peg"^\/favicon.ico$": result.code = Http200 result.content = LS.favicon result.headers = {"Content-Type": "image/x-icon"}.newStringTable return result req.route PEG_DEFAULT_URL: info.version = "v1" info.resource = matches[0] info.id = matches[1] return req.processApiUrl(LS, info) req.route PEG_URL: info.version = matches[0] info.resource = matches[1] info.id = matches[2] return req.processApiUrl(LS, info) raise newException(EInvalidRequest, req.getReqInfo()) except EInvalidRequest: let e = (ref EInvalidRequest)(getCurrentException()) let trace = e.getStackTrace() return resError(Http400, "Bad Request: $1" % getCurrentExceptionMsg(), trace) 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, gcsafe, closure.} = info(getReqInfo(req).replace("$", "$$")) let res = req.process(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) |