1 | import * as akala from '@akala/core'
|
2 | import * as router from './router';
|
3 | import * as debug from 'debug';
|
4 | import * as jsonrpc from '@akala/json-rpc-ws'
|
5 | import * as ws from 'ws'
|
6 | import * as worker from './worker-meta'
|
7 | export { CoreProperties as Package } from '../src/package';
|
8 | import { createServer } from './api/jsonrpc';
|
9 | import { Proxy, Api, DualApi } from '@akala/core';
|
10 | import { Connection } from '@akala/json-rpc-ws'
|
11 | import * as stream from 'stream'
|
12 | import * as bodyparser from 'body-parser'
|
13 | import * as express from 'express';
|
14 | import { api } from '.';
|
15 | import * as send from 'send'
|
16 | import * as cp from 'child_process'
|
17 |
|
18 | var log = debug('akala:master');
|
19 |
|
20 | var httpRouter = router.HttpRouter;
|
21 | type request = router.Request & { body?: any };
|
22 | type response = router.Response;
|
23 | export { httpRouter as Router, request as Request, response as Response };
|
24 |
|
25 |
|
26 | export 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 |
|
31 | export 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 |
|
55 | export 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 | }
|
62 | export 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 |
|
70 | export 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 |
|
94 | export 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 |
|
137 | export 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 |
|
158 | export 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 |