1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
13 | if (k2 === undefined) k2 = k;
|
14 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
15 | }) : (function(o, m, k, k2) {
|
16 | if (k2 === undefined) k2 = k;
|
17 | o[k2] = m[k];
|
18 | }));
|
19 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
20 | Object.defineProperty(o, "default", { enumerable: true, value: v });
|
21 | }) : function(o, v) {
|
22 | o["default"] = v;
|
23 | });
|
24 | var __importStar = (this && this.__importStar) || function (mod) {
|
25 | if (mod && mod.__esModule) return mod;
|
26 | var result = {};
|
27 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
28 | __setModuleDefault(result, mod);
|
29 | return result;
|
30 | };
|
31 | Object.defineProperty(exports, "__esModule", { value: true });
|
32 | exports.Server = void 0;
|
33 | const http = __importStar(require("http"));
|
34 | const path = __importStar(require("path"));
|
35 | const Koa = require("koa");
|
36 | const mount = require("koa-mount");
|
37 | const send = require("koa-send");
|
38 | const getStream = require("get-stream");
|
39 | const serve = require("koa-static");
|
40 | const bodyParser = require("koa-bodyparser");
|
41 | const koa_node_resolve_1 = require("koa-node-resolve");
|
42 | const types_1 = require("./types");
|
43 | const clientLib = path.resolve(__dirname, '..', 'client', 'lib');
|
44 | class Server {
|
45 | constructor(server, opts) {
|
46 | this.session = { bytesSent: 0, userAgent: '' };
|
47 | this.deferredResults = new types_1.Deferred();
|
48 | this.urlCache = new Map();
|
49 | this.server = server;
|
50 | const app = new Koa();
|
51 | app.use(bodyParser());
|
52 | app.use(mount('/submitResults', this.submitResults.bind(this)));
|
53 | app.use(this.instrumentRequests.bind(this));
|
54 | if (opts.cache) {
|
55 | app.use(this.cache.bind(this));
|
56 | }
|
57 | app.use(this.serveBenchLib.bind(this));
|
58 | if (opts.resolveBareModules === true) {
|
59 | const npmRoot = opts.npmInstalls.length > 0 ?
|
60 | opts.npmInstalls[0].installDir :
|
61 | opts.root;
|
62 | app.use(koa_node_resolve_1.nodeResolve({
|
63 | root: npmRoot,
|
64 |
|
65 |
|
66 |
|
67 | logger: false,
|
68 | }));
|
69 | }
|
70 | for (const { diskPath, urlPath } of opts.mountPoints) {
|
71 | app.use(mount(urlPath, serve(diskPath, { index: 'index.html' })));
|
72 | }
|
73 | this.server.on('request', app.callback());
|
74 | const address = this.server.address();
|
75 | let host = address.address;
|
76 | if (address.family === 'IPv6') {
|
77 | host = `[${host}]`;
|
78 | }
|
79 | this.port = address.port;
|
80 | this.url = `http://${host}:${this.port}`;
|
81 | }
|
82 | static start(opts) {
|
83 | const server = http.createServer();
|
84 | const ports = [...opts.ports];
|
85 | return new Promise((resolve, reject) => {
|
86 | const tryNextPort = () => {
|
87 | if (ports.length === 0) {
|
88 | reject(`No ports available, tried: ${opts.ports.join(', ')}`);
|
89 | }
|
90 | else {
|
91 | server.listen({ host: opts.host, port: ports.shift() });
|
92 | }
|
93 | };
|
94 | server.on('listening', () => resolve(new Server(server, opts)));
|
95 | server.on('error', (e) => {
|
96 | if (e.code === 'EADDRINUSE' || e.code === 'EACCES') {
|
97 | tryNextPort();
|
98 | }
|
99 | else {
|
100 | reject(e);
|
101 | }
|
102 | });
|
103 | tryNextPort();
|
104 | });
|
105 | }
|
106 | |
107 |
|
108 |
|
109 |
|
110 | endSession() {
|
111 | const session = this.session;
|
112 | this.session = { bytesSent: 0, userAgent: '' };
|
113 | this.deferredResults = new types_1.Deferred();
|
114 | return session;
|
115 | }
|
116 | async nextResults() {
|
117 | return this.deferredResults.promise;
|
118 | }
|
119 | async close() {
|
120 | return new Promise((resolve, reject) => {
|
121 | this.server.close((error) => {
|
122 | if (error) {
|
123 | reject(error);
|
124 | }
|
125 | else {
|
126 | resolve();
|
127 | }
|
128 | });
|
129 | });
|
130 | }
|
131 | async instrumentRequests(ctx, next) {
|
132 | const session = this.session;
|
133 | if (session === undefined) {
|
134 | return next();
|
135 | }
|
136 | session.userAgent = ctx.headers['user-agent'];
|
137 |
|
138 |
|
139 |
|
140 | await next();
|
141 | if (typeof ctx.response.length === 'number') {
|
142 | session.bytesSent += ctx.response.length;
|
143 | }
|
144 | else if (ctx.status === 200) {
|
145 | console.log(`No response length for 200 response for ${ctx.url}, ` +
|
146 | `byte count may be inaccurate.`);
|
147 | }
|
148 | }
|
149 | |
150 |
|
151 |
|
152 |
|
153 |
|
154 |
|
155 | async cache(ctx, next) {
|
156 | const entry = this.urlCache.get(ctx.url);
|
157 | if (entry !== undefined) {
|
158 | ctx.response.set(entry.headers);
|
159 | ctx.response.body = entry.body;
|
160 |
|
161 |
|
162 |
|
163 | ctx.response.status = entry.status;
|
164 | return;
|
165 | }
|
166 | await next();
|
167 | const body = ctx.response.body;
|
168 | let bodyString;
|
169 | if (typeof body === 'string') {
|
170 | bodyString = body;
|
171 | }
|
172 | else if (Buffer.isBuffer(body)) {
|
173 | bodyString = body.toString();
|
174 | }
|
175 | else if (isStream(body)) {
|
176 | bodyString = await getStream(body);
|
177 |
|
178 | ctx.response.body = bodyString;
|
179 | }
|
180 | else if (body === null || body === undefined) {
|
181 |
|
182 |
|
183 |
|
184 | bodyString = body;
|
185 | }
|
186 | else {
|
187 | throw new Error(`Unknown response type ${typeof body} for ${ctx.url}`);
|
188 | }
|
189 | this.urlCache.set(ctx.url, {
|
190 | body: bodyString,
|
191 | status: ctx.response.status,
|
192 | headers: ctx.response.headers,
|
193 | });
|
194 | }
|
195 | async serveBenchLib(ctx, next) {
|
196 | if (ctx.path === '/bench.js') {
|
197 | await send(ctx, 'bench.js', { root: clientLib });
|
198 | }
|
199 | else {
|
200 | await next();
|
201 | }
|
202 | }
|
203 | async submitResults(ctx) {
|
204 | this.deferredResults.resolve(ctx.request.body);
|
205 | ctx.body = 'ok';
|
206 | }
|
207 | }
|
208 | exports.Server = Server;
|
209 | function isStream(value) {
|
210 | return value !== null && typeof value === 'object' &&
|
211 | typeof value.pipe === 'function';
|
212 | }
|
213 |
|
\ | No newline at end of file |