UNPKG

8.01 kBPlain TextView Raw
1import * as akala from '@akala/core'
2import * as router from './router';
3import * as debug from 'debug';
4import * as jsonrpc from '@akala/json-rpc-ws'
5import * as ws from 'ws'
6import * as worker from './worker-meta'
7export { CoreProperties as Package } from '../src/package';
8import { createServer } from './api/jsonrpc';
9import { Proxy, Api, DualApi } from '@akala/core';
10import { Connection } from '@akala/json-rpc-ws'
11import * as stream from 'stream'
12import * as bodyparser from 'body-parser'
13import * as express from 'express';
14import { api } from '.';
15import * as send from 'send'
16import * as cp from 'child_process'
17
18var log = debug('akala:master');
19
20var httpRouter = router.HttpRouter;
21type request = router.Request & { body?: any };
22type response = router.Response;
23export { httpRouter as Router, request as Request, response as Response };
24
25
26export var metaRouter = new akala.Api()
27 .connection<Connection>()
28 .serverToClient<Partial<worker.Request>, router.CallbackResponse>()({ getContent: true })
29 .clientToServerOneWay<{ path: string, remap: string }>()({ register: true })
30
31export function serveStatic(path, options?: send.SendOptions & { fallthrough?: boolean })
32{
33 if (!options)
34 options = {};
35 if (typeof (options.fallthrough) == 'undefined')
36 options.fallthrough = true;
37 options.root = path;
38 return function (req: request, res: response, ...next: akala.NextFunction[])
39 {
40 var sendstr = send(req, req.url, options);
41 sendstr.on('error', function (error)
42 {
43 if (error && error.code == "ENOENT")
44 if (options.fallthrough)
45 next[next.length - 1]();
46 else
47 res.status(404).end();
48 else
49 next[next.length - 1](error);
50 });
51 sendstr.pipe(res);
52 }
53}
54
55export function expressWrap(handler: express.Handler)
56{
57 return function (req: router.Request, response: router.Response, ...rest)
58 {
59 handler(req as any, response as unknown as express.Response, rest[rest.length - 1]);
60 }
61}
62export function expressWrapError(handler: express.ErrorRequestHandler)
63{
64 return function (error, req: router.Request, response: router.Response, ...rest)
65 {
66 handler(error, req as any, response as unknown as express.Response, rest[rest.length - 1]);
67 }
68}
69
70export function translateRequest(req: router.Request): Partial<worker.Request>
71{
72 return {
73 url: req.url,
74 headers: req.headers,
75 httpVersion: req.httpVersion,
76 httpVersionMajor: req.httpVersionMajor,
77 httpVersionMinor: req.httpVersionMinor,
78 ip: req.ip,
79 method: req.method,
80 params: req.params,
81 path: req.path,
82 protocol: req.protocol,
83 query: req.query,
84 rawHeaders: req.rawHeaders,
85 rawTrailers: req.rawTrailers,
86 statusCode: req.statusCode,
87 statusMessage: req.statusMessage,
88 trailers: req.trailers,
89 body: req['body'],
90 user: req['user']
91 }
92}
93
94export function handleResponse(res: router.Response, locationReplacer: (key: string) => string, defaultStatus: number): (response: worker.CallbackResponse) => void
95{
96 return function (response)
97 {
98 var status = response.statusCode || defaultStatus;
99 if (response.headers)
100 Object.keys(response.headers).forEach(function (header)
101 {
102 if (header.toLowerCase() == 'location' && locationReplacer != null)
103 response.headers[header] = locationReplacer(response.headers[header]);
104 res.setHeader(header, response.headers[header]);
105 });
106 res.writeHead(status, response.statusMessage, response.headers);
107 if (response instanceof stream.Readable)
108 response.pipe(res);
109 else
110 {
111 if (Buffer.isBuffer(response.data))
112 {
113 log('sending buffer');
114 res.write(response.data);
115 }
116 else if (Array.isArray(response.data))
117 {
118 log('sending array');
119 response.data.forEach(function (chunk)
120 {
121 res.write(chunk);
122 });
123 }
124 else
125 {
126 log('sending object');
127 if (typeof (response.data) !== 'string' && typeof response.data != 'number' && typeof (response.data) !== 'undefined')
128 res.write(JSON.stringify(response.data));
129 else if (typeof (response.data) != 'undefined')
130 res.write(response.data);
131 }
132 res.end();
133 }
134 }
135}
136
137export function serveRouter<TOConnection extends Connection,
138 TOServerOneWay,
139 TOServerTwoWay,
140 TOClientOneWay,
141 TOClientTwoWay,
142 TOServerOneWayProxy extends TOServerOneWay,
143 TOServerTwoWayProxy extends TOServerTwoWay,
144 TOClientOneWayProxy extends TOClientOneWay,
145 TOClientTwoWayProxy extends TOClientTwoWay>(router: router.HttpRouter, path: string, other?: Api<TOConnection,
146 TOServerOneWay,
147 TOServerTwoWay,
148 TOClientOneWay,
149 TOClientTwoWay,
150 TOServerOneWayProxy,
151 TOServerTwoWayProxy,
152 TOClientOneWayProxy,
153 TOClientTwoWayProxy>, impl?: TOServerOneWay & TOServerTwoWay): api.Server<typeof other & typeof metaRouter>
154{
155 return serveRouterAdvanced(router, path, path, other);
156}
157
158export function serveRouterAdvanced<TOConnection extends Connection,
159 TOServerOneWay,
160 TOServerTwoWay,
161 TOClientOneWay,
162 TOClientTwoWay,
163 TOServerOneWayProxy extends TOServerOneWay,
164 TOServerTwoWayProxy extends TOServerTwoWay,
165 TOClientOneWayProxy extends TOClientOneWay,
166 TOClientTwoWayProxy extends TOClientTwoWay>(router: router.HttpRouter, pathToRegister: string, pathToServe: string, other?: Api<TOConnection,
167 TOServerOneWay,
168 TOServerTwoWay,
169 TOClientOneWay,
170 TOClientTwoWay,
171 TOServerOneWayProxy,
172 TOServerTwoWayProxy,
173 TOClientOneWayProxy,
174 TOClientTwoWayProxy>, impl?: TOServerOneWay & TOServerTwoWay): api.Server<typeof other & typeof metaRouter>
175{
176 var subRouter = new httpRouter();
177 log(`creating server on ${pathToRegister} for ${pathToServe}`);
178 router.use(pathToServe, subRouter.router);
179
180 return api.jsonrpcws(other && new DualApi(metaRouter, other) || metaRouter).createServer(pathToRegister, akala.extend({
181 register: function (param: { path: string, remap: string }, socket: TOConnection)
182 {
183 var locationReplacer = function (header)
184 {
185 return header.replace(pathToServe, pathToServe + param.path)
186 };
187
188 var client = this.$proxy(socket);
189
190 subRouter.use(param.path,
191 function (_req: router.Request, _res: router.Response, next: akala.NextFunction)
192 {
193 if (socket.socket.readyState == ws.CLOSED || socket.socket.readyState == ws.CLOSING)
194 next('route');
195 else
196 next();
197 },
198 bodyparser.json(),
199 bodyparser.urlencoded({ extended: true }),
200 function (req, res, next)
201 {
202 var translatedReq = translateRequest(req);
203 if (param.remap)
204 {
205 if (translatedReq.url == '/')
206 translatedReq.url = '';
207 translatedReq.url = param.remap + translatedReq.url;
208 }
209 client.getContent(translatedReq).then(handleResponse(res, locationReplacer, 200), next);
210 });
211 }
212 }, impl || {}));
213}
\No newline at end of file