summaryrefslogtreecommitdiff
path: root/app/lib/router.js
blob: 7d30ccfe08488b28c460c1334067d1cc8eb4167b (plain)
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
import {URL} from "node:url";
const degroup = path => Object.assign(path, path.groups);

export default class Router {
  constructor() {
    this.routes = [];
  }

  route(req, res) {
    const pathname = new URL(req.url, "file:").pathname; // TODO double-check
    return this.routes.some(route => {
      const isMatch = route.method === req.method && route.re.test(pathname);
      if (isMatch) route.cb(req, res, degroup(route.re.exec(pathname)));
      return isMatch;
    });
  }

  gather(cb, max) { // Handle POST data
    return (req, res, match) => {
      let data = "";
      req.on("data", chunk => {
        if ((data += chunk).length > (max||1e6)) // ~1MB
          req.connection.destroy();
      }).on("end", () => cb(req, res, match, data));
    };
  }

  gpost(re, cb, max) {
    this.post(re, this.gather(cb, max));
  }

  jpost(re, cb, max) {
    // TODO check req content-type? set accepts?
    this.gpost(re, (req, res, match, data) => {
      try {
        data = JSON.parse(data);
      } catch (e) {
        res.writeHead(400, {"Content-Type": "text/plain"});
        return res.end("400 Bad Request");
      }

      cb(req, res, match, data);
    }, max);
  }
}

["get", "post", "put", "delete"].forEach(method =>
  Router.prototype[method] = function(re, cb) {
    this.routes.push({method: method.toUpperCase(), cb,
      re: (re instanceof RegExp)? re : new RegExp(`^${re}$`)})});