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 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
import asynchttpserver, asyncdispatch, times, strutils, pegs, strtabs, logger, cgi, os import types, utils, api_v1, api_v2 export api_v2 proc getReqInfo(req: LSRequest): 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 "" LOG.info("Exiting...") quit() proc processApiUrl(req: LSRequest, LS: LiteStore, info: ResourceInfo): LSResponse = if info.version == "v2": if info.resource.match(peg"^docs / info$"): return api_v2.route(req, LS, info.resource, info.id) elif info.resource.match(peg"^dir$"): if LS.directory != nil: return api_v2.serveFile(req, LS, info.id) else: return resError(Http400, "Bad request - Not serving any directory." % info.version) else: return resError(Http400, "Bad request - Invalid resource: $1" % info.resource) elif info.version == "v1": if info.resource.match(peg"^docs / info$"): return api_v1.route(req, LS, info.resource, info.id) elif info.resource.match(peg"^dir$"): if LS.directory != nil: return api_v1.serveFile(req, LS, info.id) else: return resError(Http400, "Bad request - Not serving any directory." % info.version) else: return resError(Http400, "Bad request - Invalid resource: $1" % info.resource) else: if info.version == "v1" or info.version == "v2": 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: LSRequest, LS: LiteStore): LSResponse {.gcsafe.}= var matches = @["", "", ""] template route(req: LSRequest, peg: Peg, op: untyped): untyped = if req.url.path.find(peg, matches) != -1: op try: var info: ResourceInfo req.route peg"^\/?$": info.version = "v2" info.resource = "info" return req.processApiUrl(LS, info) req.route peg"^\/favicon.ico$": result.code = Http200 result.content = LS.favicon result.headers = ctHeader("image/x-icon") return result req.route PEG_DEFAULT_URL: info.version = "v2" 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 LSRequest: $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: LSRequest): Future[void] {.async, gcsafe, closure.} = LOG.info(getReqInfo(req).replace("$", "$$")) let res = req.process(LS) let areq = asynchttpserver.Request(req) await areq.respond(res.code, res.content, res.headers) echo(LS.appname & " v" & LS.appversion & " started on " & LS.address & ":" & $LS.port & ".") if LS.mount: echo("Mirroring datastore changes to: " & LS.directory) asyncCheck server.serve(LS.port.Port, handleHttpRequest, LS.address) |