-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathserve.js
executable file
·92 lines (90 loc) · 2.75 KB
/
serve.js
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
import {createServer} from 'node:http';
import {EZCCIP, error_with} from './index.js';
import {id as keccakStr} from 'ethers/hash';
import {computeAddress} from 'ethers/transaction';
import {SigningKey} from 'ethers/crypto';
export function serve(ezccip, {
port = 0,
log = true,
formatError = x => x,
protocol = 'tor',
signingKey = keccakStr('ezccip'),
origin,
parseOrigin,
...a
} = {}) {
if (ezccip instanceof Function) {
let temp = new EZCCIP();
temp.enableENSIP10(ezccip);
ezccip = temp;
}
if (log === true) {
log = (...a) => console.log(new Date(), ...a);
} else if (!log) {
log = undefined;
}
if (!(signingKey instanceof SigningKey)) {
signingKey = new SigningKey(signingKey);
}
if (!parseOrigin) { // backwards compat with old design
parseOrigin = x => a.resolvers?.[x.slice(1)] ?? a.resolvers?.['*'];
}
let killer;
return new Promise(ful => {
let http = createServer(async (req, reply) => {
let ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress;
let {method, url} = req;
try {
reply.setHeader('access-control-allow-origin', '*');
switch (method) {
case 'OPTIONS': return reply.setHeader('access-control-allow-headers', '*').end();
case 'POST': {
let v = [];
for await (let x of req) v.push(x);
let {sender, data: calldata} = JSON.parse(Buffer.concat(v));
let match = url.match(/\/(0x[a-f0-9]{40})(?:\b|\/|\?)/i);
let {data, history} = await ezccip.handleRead(sender, calldata, {
...a, url, ip, protocol, signingKey,
origin: match ? match[1] : (parseOrigin(url) || origin)
});
log?.(ip, url, history.toString());
write_json(reply, {data});
break;
}
default: throw error_with('unsupported http method', {status: 405, method});
}
} catch (err) {
log?.(ip, method, url, formatError(err));
let {status = 500, message} = err;
reply.statusCode = status;
write_json(reply, {message});
}
});
function shutdown() {
if (!killer) {
if (!http.listening) return Promise.resolve();
killer = new Promise(ful => http.close(() => {
killer = null;
ful();
}));
}
return killer;
}
http.listen(port, () => {
port = http.address().port;
let endpoint = `http://localhost:${port}`;
let signer = computeAddress(signingKey);
let context = `${signer} ${endpoint}`;
const t0 = Date.now();
log?.(`Serving "${protocol}" ${context}`); //, {protocol, context});
http.on('close', () => log?.(`Shutdown<${Date.now() - t0}ms>`));
ful({http, port, endpoint, signer, context, shutdown});
});
});
}
function write_json(reply, json) {
let buf = Buffer.from(JSON.stringify(json));
reply.setHeader('content-length', buf.length);
reply.setHeader('content-type', 'application/json');
reply.end(buf);
}