@ripeseed/rs-tunnel 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +57 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +20 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/list.d.ts +1 -0
- package/dist/commands/list.js +18 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/login.d.ts +1 -0
- package/dist/commands/login.js +45 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +1 -0
- package/dist/commands/logout.js +19 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/stop.d.ts +1 -0
- package/dist/commands/stop.js +15 -0
- package/dist/commands/stop.js.map +1 -0
- package/dist/commands/up.d.ts +32 -0
- package/dist/commands/up.js +319 -0
- package/dist/commands/up.js.map +1 -0
- package/dist/commands/up.test.d.ts +1 -0
- package/dist/commands/up.test.js +467 -0
- package/dist/commands/up.test.js.map +1 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.js +135 -0
- package/dist/config.js.map +1 -0
- package/dist/config.test.d.ts +1 -0
- package/dist/config.test.js +67 -0
- package/dist/config.test.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +75 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api-client.d.ts +73 -0
- package/dist/lib/api-client.js +146 -0
- package/dist/lib/api-client.js.map +1 -0
- package/dist/lib/cloudflared.d.ts +3 -0
- package/dist/lib/cloudflared.js +165 -0
- package/dist/lib/cloudflared.js.map +1 -0
- package/dist/lib/local-callback.d.ts +9 -0
- package/dist/lib/local-callback.js +68 -0
- package/dist/lib/local-callback.js.map +1 -0
- package/dist/lib/local-proxy.d.ts +27 -0
- package/dist/lib/local-proxy.js +200 -0
- package/dist/lib/local-proxy.js.map +1 -0
- package/dist/lib/local-proxy.test.d.ts +1 -0
- package/dist/lib/local-proxy.test.js +181 -0
- package/dist/lib/local-proxy.test.js.map +1 -0
- package/dist/lib/pkce.d.ts +4 -0
- package/dist/lib/pkce.js +10 -0
- package/dist/lib/pkce.js.map +1 -0
- package/dist/lib/session.d.ts +4 -0
- package/dist/lib/session.js +47 -0
- package/dist/lib/session.js.map +1 -0
- package/dist/lib/tunnel-stats.d.ts +19 -0
- package/dist/lib/tunnel-stats.js +75 -0
- package/dist/lib/tunnel-stats.js.map +1 -0
- package/dist/lib/tunnel-stats.test.d.ts +1 -0
- package/dist/lib/tunnel-stats.test.js +44 -0
- package/dist/lib/tunnel-stats.test.js.map +1 -0
- package/dist/lib/up-dashboard.d.ts +19 -0
- package/dist/lib/up-dashboard.js +170 -0
- package/dist/lib/up-dashboard.js.map +1 -0
- package/dist/lib/version.d.ts +1 -0
- package/dist/lib/version.js +18 -0
- package/dist/lib/version.js.map +1 -0
- package/dist/store/credentials.d.ts +4 -0
- package/dist/store/credentials.js +99 -0
- package/dist/store/credentials.js.map +1 -0
- package/dist/types.d.ts +10 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type ProxyRequestEvent = {
|
|
2
|
+
startedAtEpochMs: number;
|
|
3
|
+
method: string;
|
|
4
|
+
path: string;
|
|
5
|
+
statusCode: number;
|
|
6
|
+
statusMessage: string;
|
|
7
|
+
durationMs: number;
|
|
8
|
+
responseBytes: number | null;
|
|
9
|
+
error: boolean;
|
|
10
|
+
protocol: 'http' | 'ws';
|
|
11
|
+
};
|
|
12
|
+
export type ProxyConnectionSnapshot = {
|
|
13
|
+
totalConnections: number;
|
|
14
|
+
openConnections: number;
|
|
15
|
+
};
|
|
16
|
+
export type LocalProxy = {
|
|
17
|
+
port: number;
|
|
18
|
+
stop: () => Promise<void>;
|
|
19
|
+
};
|
|
20
|
+
type StartLocalProxyInput = {
|
|
21
|
+
targetPort: number;
|
|
22
|
+
targetHost?: string;
|
|
23
|
+
onRequest?: (event: ProxyRequestEvent) => void;
|
|
24
|
+
onConnectionChange?: (snapshot: ProxyConnectionSnapshot) => void;
|
|
25
|
+
};
|
|
26
|
+
export declare function startLocalProxy(input: StartLocalProxyInput): Promise<LocalProxy>;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
import net from 'node:net';
|
|
3
|
+
function toDurationMs(startedAtHrTimeNs) {
|
|
4
|
+
return Number(process.hrtime.bigint() - startedAtHrTimeNs) / 1_000_000;
|
|
5
|
+
}
|
|
6
|
+
function getStatusText(statusCode) {
|
|
7
|
+
return http.STATUS_CODES[statusCode] ?? 'Unknown';
|
|
8
|
+
}
|
|
9
|
+
function emitConnectionSnapshot(onConnectionChange, totalConnections, openConnections) {
|
|
10
|
+
onConnectionChange?.({
|
|
11
|
+
totalConnections,
|
|
12
|
+
openConnections,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function buildUpgradeRequest(req) {
|
|
16
|
+
const requestLine = `${req.method ?? 'GET'} ${req.url ?? '/'} HTTP/${req.httpVersion}`;
|
|
17
|
+
let headers = '';
|
|
18
|
+
for (let i = 0; i < req.rawHeaders.length; i += 2) {
|
|
19
|
+
const headerName = req.rawHeaders[i];
|
|
20
|
+
const headerValue = req.rawHeaders[i + 1];
|
|
21
|
+
if (!headerName || headerValue === undefined) {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
headers += `${headerName}: ${headerValue}\r\n`;
|
|
25
|
+
}
|
|
26
|
+
return `${requestLine}\r\n${headers}\r\n`;
|
|
27
|
+
}
|
|
28
|
+
export async function startLocalProxy(input) {
|
|
29
|
+
const targetHost = input.targetHost ?? '127.0.0.1';
|
|
30
|
+
let totalConnections = 0;
|
|
31
|
+
let openConnections = 0;
|
|
32
|
+
const sockets = new Set();
|
|
33
|
+
const server = http.createServer((req, res) => {
|
|
34
|
+
const startedAtEpochMs = Date.now();
|
|
35
|
+
const startedAtHrTimeNs = process.hrtime.bigint();
|
|
36
|
+
const method = req.method ?? 'GET';
|
|
37
|
+
const path = req.url ?? '/';
|
|
38
|
+
let responseBytes = 0;
|
|
39
|
+
let finalized = false;
|
|
40
|
+
let statusCode = 500;
|
|
41
|
+
const finalize = (event) => {
|
|
42
|
+
if (finalized) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
finalized = true;
|
|
46
|
+
const finalStatusCode = event.statusCode ?? statusCode;
|
|
47
|
+
const responseBytesValue = event.responseBytes ?? responseBytes;
|
|
48
|
+
input.onRequest?.({
|
|
49
|
+
startedAtEpochMs,
|
|
50
|
+
method,
|
|
51
|
+
path,
|
|
52
|
+
statusCode: finalStatusCode,
|
|
53
|
+
statusMessage: getStatusText(finalStatusCode),
|
|
54
|
+
durationMs: toDurationMs(startedAtHrTimeNs),
|
|
55
|
+
responseBytes: responseBytesValue,
|
|
56
|
+
error: event.error ?? finalStatusCode >= 500,
|
|
57
|
+
protocol: 'http',
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
const upstream = http.request({
|
|
61
|
+
host: targetHost,
|
|
62
|
+
port: input.targetPort,
|
|
63
|
+
method,
|
|
64
|
+
path,
|
|
65
|
+
headers: req.headers,
|
|
66
|
+
}, (upstreamResponse) => {
|
|
67
|
+
statusCode = upstreamResponse.statusCode ?? 502;
|
|
68
|
+
res.writeHead(statusCode, upstreamResponse.statusMessage, upstreamResponse.headers);
|
|
69
|
+
upstreamResponse.on('data', (chunk) => {
|
|
70
|
+
responseBytes += Buffer.isBuffer(chunk) ? chunk.length : Buffer.byteLength(chunk);
|
|
71
|
+
});
|
|
72
|
+
upstreamResponse.pipe(res);
|
|
73
|
+
});
|
|
74
|
+
upstream.on('error', () => {
|
|
75
|
+
statusCode = 502;
|
|
76
|
+
if (!res.headersSent) {
|
|
77
|
+
res.writeHead(statusCode, getStatusText(statusCode));
|
|
78
|
+
}
|
|
79
|
+
res.end('Bad Gateway');
|
|
80
|
+
finalize({
|
|
81
|
+
statusCode,
|
|
82
|
+
error: true,
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
req.pipe(upstream);
|
|
86
|
+
res.once('finish', () => {
|
|
87
|
+
finalize({
|
|
88
|
+
statusCode: res.statusCode,
|
|
89
|
+
error: res.statusCode >= 500,
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
res.once('close', () => {
|
|
93
|
+
if (!finalized) {
|
|
94
|
+
finalize({
|
|
95
|
+
statusCode: res.statusCode || statusCode,
|
|
96
|
+
error: true,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
server.on('connection', (socket) => {
|
|
102
|
+
sockets.add(socket);
|
|
103
|
+
totalConnections += 1;
|
|
104
|
+
openConnections += 1;
|
|
105
|
+
emitConnectionSnapshot(input.onConnectionChange, totalConnections, openConnections);
|
|
106
|
+
socket.on('close', () => {
|
|
107
|
+
sockets.delete(socket);
|
|
108
|
+
openConnections = Math.max(0, openConnections - 1);
|
|
109
|
+
emitConnectionSnapshot(input.onConnectionChange, totalConnections, openConnections);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
server.on('upgrade', (req, clientSocket, head) => {
|
|
113
|
+
const startedAtEpochMs = Date.now();
|
|
114
|
+
const startedAtHrTimeNs = process.hrtime.bigint();
|
|
115
|
+
const method = req.method ?? 'GET';
|
|
116
|
+
const path = req.url ?? '/';
|
|
117
|
+
const upstreamSocket = net.connect({
|
|
118
|
+
host: targetHost,
|
|
119
|
+
port: input.targetPort,
|
|
120
|
+
});
|
|
121
|
+
let finalized = false;
|
|
122
|
+
const finalize = (statusCode, isError) => {
|
|
123
|
+
if (finalized) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
finalized = true;
|
|
127
|
+
input.onRequest?.({
|
|
128
|
+
startedAtEpochMs,
|
|
129
|
+
method,
|
|
130
|
+
path,
|
|
131
|
+
statusCode,
|
|
132
|
+
statusMessage: getStatusText(statusCode),
|
|
133
|
+
durationMs: toDurationMs(startedAtHrTimeNs),
|
|
134
|
+
responseBytes: null,
|
|
135
|
+
error: isError,
|
|
136
|
+
protocol: 'ws',
|
|
137
|
+
});
|
|
138
|
+
};
|
|
139
|
+
upstreamSocket.once('connect', () => {
|
|
140
|
+
upstreamSocket.write(buildUpgradeRequest(req));
|
|
141
|
+
if (head.length > 0) {
|
|
142
|
+
upstreamSocket.write(head);
|
|
143
|
+
}
|
|
144
|
+
clientSocket.pipe(upstreamSocket);
|
|
145
|
+
upstreamSocket.pipe(clientSocket);
|
|
146
|
+
finalize(101, false);
|
|
147
|
+
});
|
|
148
|
+
upstreamSocket.once('error', () => {
|
|
149
|
+
if (!clientSocket.destroyed) {
|
|
150
|
+
clientSocket.write('HTTP/1.1 502 Bad Gateway\r\nConnection: close\r\n\r\n');
|
|
151
|
+
}
|
|
152
|
+
clientSocket.destroy();
|
|
153
|
+
finalize(502, true);
|
|
154
|
+
});
|
|
155
|
+
clientSocket.once('error', () => {
|
|
156
|
+
upstreamSocket.destroy();
|
|
157
|
+
finalize(499, true);
|
|
158
|
+
});
|
|
159
|
+
clientSocket.once('close', () => {
|
|
160
|
+
upstreamSocket.destroy();
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
await new Promise((resolve, reject) => {
|
|
164
|
+
server.once('error', reject);
|
|
165
|
+
server.listen(0, '127.0.0.1', () => {
|
|
166
|
+
server.off('error', reject);
|
|
167
|
+
resolve();
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
const address = server.address();
|
|
171
|
+
if (!address || typeof address === 'string') {
|
|
172
|
+
await new Promise((resolve) => {
|
|
173
|
+
server.close(() => resolve());
|
|
174
|
+
});
|
|
175
|
+
throw new Error('Local proxy failed to bind to a TCP port.');
|
|
176
|
+
}
|
|
177
|
+
let stopped = false;
|
|
178
|
+
return {
|
|
179
|
+
port: address.port,
|
|
180
|
+
stop: async () => {
|
|
181
|
+
if (stopped) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
stopped = true;
|
|
185
|
+
for (const socket of sockets) {
|
|
186
|
+
socket.destroy();
|
|
187
|
+
}
|
|
188
|
+
await new Promise((resolve, reject) => {
|
|
189
|
+
server.close((error) => {
|
|
190
|
+
if (error) {
|
|
191
|
+
reject(error);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
resolve();
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=local-proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-proxy.js","sourceRoot":"","sources":["../../src/lib/local-proxy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
import net from 'node:net';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
import { startLocalProxy } from './local-proxy.js';
|
|
5
|
+
async function listenHttpServer(handler) {
|
|
6
|
+
const server = http.createServer(handler);
|
|
7
|
+
await new Promise((resolve, reject) => {
|
|
8
|
+
server.once('error', reject);
|
|
9
|
+
server.listen(0, '127.0.0.1', () => {
|
|
10
|
+
server.off('error', reject);
|
|
11
|
+
resolve();
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
const address = server.address();
|
|
15
|
+
if (!address || typeof address === 'string') {
|
|
16
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
17
|
+
throw new Error('Failed to bind HTTP server.');
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
server,
|
|
21
|
+
port: address.port,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
async function closeServer(server) {
|
|
25
|
+
await new Promise((resolve) => {
|
|
26
|
+
server.close(() => resolve());
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
async function reserveUnusedPort() {
|
|
30
|
+
const reservation = net.createServer();
|
|
31
|
+
await new Promise((resolve, reject) => {
|
|
32
|
+
reservation.once('error', reject);
|
|
33
|
+
reservation.listen(0, '127.0.0.1', () => {
|
|
34
|
+
reservation.off('error', reject);
|
|
35
|
+
resolve();
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
const address = reservation.address();
|
|
39
|
+
if (!address || typeof address === 'string') {
|
|
40
|
+
await new Promise((resolve) => reservation.close(() => resolve()));
|
|
41
|
+
throw new Error('Failed to reserve port.');
|
|
42
|
+
}
|
|
43
|
+
const port = address.port;
|
|
44
|
+
await new Promise((resolve) => reservation.close(() => resolve()));
|
|
45
|
+
return port;
|
|
46
|
+
}
|
|
47
|
+
async function wait(ms) {
|
|
48
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
49
|
+
}
|
|
50
|
+
describe('startLocalProxy', () => {
|
|
51
|
+
it('forwards HTTP requests, emits request events, and tracks connections', async () => {
|
|
52
|
+
const requestEvents = [];
|
|
53
|
+
const connectionEvents = [];
|
|
54
|
+
const backend = await listenHttpServer((req, res) => {
|
|
55
|
+
if (req.url === '/ok') {
|
|
56
|
+
res.writeHead(200, { 'content-type': 'text/plain' });
|
|
57
|
+
res.end('ok');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
res.writeHead(404, { 'content-type': 'text/plain' });
|
|
61
|
+
res.end('not found');
|
|
62
|
+
});
|
|
63
|
+
const proxy = await startLocalProxy({
|
|
64
|
+
targetPort: backend.port,
|
|
65
|
+
onRequest: (event) => requestEvents.push(event),
|
|
66
|
+
onConnectionChange: (snapshot) => connectionEvents.push(snapshot),
|
|
67
|
+
});
|
|
68
|
+
try {
|
|
69
|
+
const okResponse = await fetch(`http://127.0.0.1:${proxy.port}/ok`, {
|
|
70
|
+
headers: {
|
|
71
|
+
connection: 'close',
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
const notFoundResponse = await fetch(`http://127.0.0.1:${proxy.port}/missing`, {
|
|
75
|
+
headers: {
|
|
76
|
+
connection: 'close',
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
expect(okResponse.status).toBe(200);
|
|
80
|
+
expect(await okResponse.text()).toBe('ok');
|
|
81
|
+
expect(notFoundResponse.status).toBe(404);
|
|
82
|
+
expect(await notFoundResponse.text()).toBe('not found');
|
|
83
|
+
await wait(60);
|
|
84
|
+
expect(requestEvents.length).toBe(2);
|
|
85
|
+
expect(requestEvents[0]?.statusCode).toBe(200);
|
|
86
|
+
expect(requestEvents[0]?.path).toBe('/ok');
|
|
87
|
+
expect(requestEvents[1]?.statusCode).toBe(404);
|
|
88
|
+
expect(requestEvents[1]?.path).toBe('/missing');
|
|
89
|
+
const maxOpenConnections = Math.max(...connectionEvents.map((event) => event.openConnections));
|
|
90
|
+
const finalOpenConnections = connectionEvents.at(-1)?.openConnections ?? -1;
|
|
91
|
+
expect(maxOpenConnections).toBeGreaterThan(0);
|
|
92
|
+
expect(finalOpenConnections).toBe(0);
|
|
93
|
+
expect(connectionEvents.at(-1)?.totalConnections).toBeGreaterThanOrEqual(1);
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
await proxy.stop();
|
|
97
|
+
await closeServer(backend.server);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
it('returns 502 and emits error events when upstream target is unreachable', async () => {
|
|
101
|
+
const requestEvents = [];
|
|
102
|
+
const unreachablePort = await reserveUnusedPort();
|
|
103
|
+
const proxy = await startLocalProxy({
|
|
104
|
+
targetPort: unreachablePort,
|
|
105
|
+
onRequest: (event) => requestEvents.push(event),
|
|
106
|
+
});
|
|
107
|
+
try {
|
|
108
|
+
const response = await fetch(`http://127.0.0.1:${proxy.port}/will-fail`, {
|
|
109
|
+
headers: {
|
|
110
|
+
connection: 'close',
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
expect(response.status).toBe(502);
|
|
114
|
+
expect(await response.text()).toContain('Bad Gateway');
|
|
115
|
+
await wait(20);
|
|
116
|
+
expect(requestEvents).toHaveLength(1);
|
|
117
|
+
expect(requestEvents[0]?.statusCode).toBe(502);
|
|
118
|
+
expect(requestEvents[0]?.error).toBe(true);
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
await proxy.stop();
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
it('forwards websocket upgrades', async () => {
|
|
125
|
+
const backendServer = http.createServer((_, res) => {
|
|
126
|
+
res.writeHead(426);
|
|
127
|
+
res.end('upgrade required');
|
|
128
|
+
});
|
|
129
|
+
backendServer.on('upgrade', (_req, socket) => {
|
|
130
|
+
socket.write('HTTP/1.1 101 Switching Protocols\r\n' +
|
|
131
|
+
'Upgrade: websocket\r\n' +
|
|
132
|
+
'Connection: Upgrade\r\n' +
|
|
133
|
+
'\r\n');
|
|
134
|
+
socket.end();
|
|
135
|
+
});
|
|
136
|
+
await new Promise((resolve, reject) => {
|
|
137
|
+
backendServer.once('error', reject);
|
|
138
|
+
backendServer.listen(0, '127.0.0.1', () => {
|
|
139
|
+
backendServer.off('error', reject);
|
|
140
|
+
resolve();
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
const backendAddress = backendServer.address();
|
|
144
|
+
if (!backendAddress || typeof backendAddress === 'string') {
|
|
145
|
+
await closeServer(backendServer);
|
|
146
|
+
throw new Error('Backend server failed to bind.');
|
|
147
|
+
}
|
|
148
|
+
const proxy = await startLocalProxy({
|
|
149
|
+
targetPort: backendAddress.port,
|
|
150
|
+
});
|
|
151
|
+
try {
|
|
152
|
+
const rawResponse = await new Promise((resolve, reject) => {
|
|
153
|
+
const socket = net.connect({ host: '127.0.0.1', port: proxy.port });
|
|
154
|
+
let data = '';
|
|
155
|
+
socket.setEncoding('utf8');
|
|
156
|
+
socket.once('connect', () => {
|
|
157
|
+
socket.write('GET /socket HTTP/1.1\r\n' +
|
|
158
|
+
`Host: 127.0.0.1:${proxy.port}\r\n` +
|
|
159
|
+
'Connection: Upgrade\r\n' +
|
|
160
|
+
'Upgrade: websocket\r\n' +
|
|
161
|
+
'\r\n');
|
|
162
|
+
});
|
|
163
|
+
socket.on('data', (chunk) => {
|
|
164
|
+
data += chunk;
|
|
165
|
+
if (data.includes('\r\n\r\n')) {
|
|
166
|
+
resolve(data);
|
|
167
|
+
socket.end();
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
socket.on('error', reject);
|
|
171
|
+
});
|
|
172
|
+
expect(rawResponse).toContain('101 Switching Protocols');
|
|
173
|
+
expect(rawResponse).toContain('Upgrade: websocket');
|
|
174
|
+
}
|
|
175
|
+
finally {
|
|
176
|
+
await proxy.stop();
|
|
177
|
+
await closeServer(backendServer);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
//# sourceMappingURL=local-proxy.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-proxy.test.js","sourceRoot":"","sources":["../../src/lib/local-proxy.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,GAAG,MAAM,UAAU,CAAC;AAE3B,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,eAAe,EAAwD,MAAM,kBAAkB,CAAC;AAEzG,KAAK,UAAU,gBAAgB,CAC7B,OAAsE;IAEtE,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IAE1C,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1C,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE;YACjC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC5B,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;IACjC,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC5C,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACpE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;IAED,OAAO;QACL,MAAM;QACN,IAAI,EAAE,OAAO,CAAC,IAAI;KACnB,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,MAAmB;IAC5C,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAClC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,iBAAiB;IAC9B,MAAM,WAAW,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC;IAEvC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1C,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAClC,WAAW,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE;YACtC,WAAW,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YACjC,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC;IACtC,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC5C,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACzE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC1B,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACzE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,IAAI,CAAC,EAAU;IAC5B,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,EAAE,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;QACpF,MAAM,aAAa,GAAwB,EAAE,CAAC;QAC9C,MAAM,gBAAgB,GAA8B,EAAE,CAAC;QAEvD,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAClD,IAAI,GAAG,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;gBACtB,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAC;gBACrD,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBACd,OAAO;YACT,CAAC;YAED,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAC;YACrD,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACvB,CAAC,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC;YAClC,UAAU,EAAE,OAAO,CAAC,IAAI;YACxB,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC;YAC/C,kBAAkB,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC;SAClE,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,KAAK,CAAC,oBAAoB,KAAK,CAAC,IAAI,KAAK,EAAE;gBAClE,OAAO,EAAE;oBACP,UAAU,EAAE,OAAO;iBACpB;aACF,CAAC,CAAC;YACH,MAAM,gBAAgB,GAAG,MAAM,KAAK,CAAC,oBAAoB,KAAK,CAAC,IAAI,UAAU,EAAE;gBAC7E,OAAO,EAAE;oBACP,UAAU,EAAE,OAAO;iBACpB;aACF,CAAC,CAAC;YAEH,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACpC,MAAM,CAAC,MAAM,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAE3C,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC1C,MAAM,CAAC,MAAM,gBAAgB,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAExD,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC;YAEf,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACrC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC/C,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC3C,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC/C,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAEhD,MAAM,kBAAkB,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;YAC/F,MAAM,oBAAoB,GAAG,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,eAAe,IAAI,CAAC,CAAC,CAAC;YAE5E,MAAM,CAAC,kBAAkB,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;YAC9C,MAAM,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACrC,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QAC9E,CAAC;gBAAS,CAAC;YACT,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;YACnB,MAAM,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACpC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wEAAwE,EAAE,KAAK,IAAI,EAAE;QACtF,MAAM,aAAa,GAAwB,EAAE,CAAC;QAC9C,MAAM,eAAe,GAAG,MAAM,iBAAiB,EAAE,CAAC;QAElD,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC;YAClC,UAAU,EAAE,eAAe;YAC3B,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC;SAChD,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,oBAAoB,KAAK,CAAC,IAAI,YAAY,EAAE;gBACvE,OAAO,EAAE;oBACP,UAAU,EAAE,OAAO;iBACpB;aACF,CAAC,CAAC;YAEH,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;YAEvD,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC;YAEf,MAAM,CAAC,aAAa,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACtC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC/C,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7C,CAAC;gBAAS,CAAC;YACT,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;QACrB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;QAC3C,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE;YACjD,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACnB,GAAG,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;QAEH,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;YAC3C,MAAM,CAAC,KAAK,CACV,sCAAsC;gBACpC,wBAAwB;gBACxB,yBAAyB;gBACzB,MAAM,CACT,CAAC;YACF,MAAM,CAAC,GAAG,EAAE,CAAC;QACf,CAAC,CAAC,CAAC;QAEH,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC1C,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YACpC,aAAa,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE;gBACxC,aAAa,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBACnC,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,MAAM,cAAc,GAAG,aAAa,CAAC,OAAO,EAAE,CAAC;QAC/C,IAAI,CAAC,cAAc,IAAI,OAAO,cAAc,KAAK,QAAQ,EAAE,CAAC;YAC1D,MAAM,WAAW,CAAC,aAAa,CAAC,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QACpD,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC;YAClC,UAAU,EAAE,cAAc,CAAC,IAAI;SAChC,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,MAAM,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAChE,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;gBACpE,IAAI,IAAI,GAAG,EAAE,CAAC;gBAEd,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;gBAE3B,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE;oBAC1B,MAAM,CAAC,KAAK,CACV,0BAA0B;wBACxB,mBAAmB,KAAK,CAAC,IAAI,MAAM;wBACnC,yBAAyB;wBACzB,wBAAwB;wBACxB,MAAM,CACT,CAAC;gBACJ,CAAC,CAAC,CAAC;gBAEH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;oBAClC,IAAI,IAAI,KAAK,CAAC;oBACd,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;wBAC9B,OAAO,CAAC,IAAI,CAAC,CAAC;wBACd,MAAM,CAAC,GAAG,EAAE,CAAC;oBACf,CAAC;gBACH,CAAC,CAAC,CAAC;gBAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC7B,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;YACzD,MAAM,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;QACtD,CAAC;gBAAS,CAAC;YACT,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;YACnB,MAAM,WAAW,CAAC,aAAa,CAAC,CAAC;QACnC,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/lib/pkce.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
+
export function createPkcePair() {
|
|
3
|
+
const verifier = randomBytes(64).toString('base64url');
|
|
4
|
+
const challenge = createHash('sha256').update(verifier).digest('base64url');
|
|
5
|
+
return {
|
|
6
|
+
verifier,
|
|
7
|
+
challenge,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=pkce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkce.js","sourceRoot":"","sources":["../../src/lib/pkce.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtD,MAAM,UAAU,cAAc;IAC5B,MAAM,QAAQ,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACvD,MAAM,SAAS,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAC5E,OAAO;QACL,QAAQ;QACR,SAAS;KACV,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { StoredSession } from '../types.js';
|
|
2
|
+
import { ApiClient } from './api-client.js';
|
|
3
|
+
export declare function requireSession(apiClient: ApiClient): Promise<StoredSession>;
|
|
4
|
+
export declare function withAuthenticatedSession<T>(apiClient: ApiClient, run: (session: StoredSession) => Promise<T>): Promise<T>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { clearSession, loadSession, saveSession } from '../store/credentials.js';
|
|
2
|
+
import { ApiClientError } from './api-client.js';
|
|
3
|
+
const REFRESH_HEADROOM_SEC = 30;
|
|
4
|
+
function isExpired(session) {
|
|
5
|
+
return session.expiresAtEpochSec <= Math.floor(Date.now() / 1000) + REFRESH_HEADROOM_SEC;
|
|
6
|
+
}
|
|
7
|
+
async function refreshSession(apiClient, session) {
|
|
8
|
+
const refreshed = await apiClient.refreshTokens(session.refreshToken);
|
|
9
|
+
const next = {
|
|
10
|
+
accessToken: refreshed.accessToken,
|
|
11
|
+
refreshToken: refreshed.refreshToken,
|
|
12
|
+
expiresAtEpochSec: Math.floor(Date.now() / 1000) + refreshed.expiresInSec,
|
|
13
|
+
profile: refreshed.profile,
|
|
14
|
+
};
|
|
15
|
+
await saveSession(next);
|
|
16
|
+
return next;
|
|
17
|
+
}
|
|
18
|
+
export async function requireSession(apiClient) {
|
|
19
|
+
const session = await loadSession();
|
|
20
|
+
if (!session) {
|
|
21
|
+
throw new Error('Not logged in. Run: rs-tunnel login --email <you@example.com>');
|
|
22
|
+
}
|
|
23
|
+
if (isExpired(session)) {
|
|
24
|
+
try {
|
|
25
|
+
return await refreshSession(apiClient, session);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
await clearSession();
|
|
29
|
+
throw new Error('Session expired. Please login again.');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return session;
|
|
33
|
+
}
|
|
34
|
+
export async function withAuthenticatedSession(apiClient, run) {
|
|
35
|
+
let session = await requireSession(apiClient);
|
|
36
|
+
try {
|
|
37
|
+
return await run(session);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
if (!(error instanceof ApiClientError) || error.status !== 401) {
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
session = await refreshSession(apiClient, session);
|
|
44
|
+
return run(session);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/lib/session.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,EAAa,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAE5D,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEhC,SAAS,SAAS,CAAC,OAAsB;IACvC,OAAO,OAAO,CAAC,iBAAiB,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,oBAAoB,CAAC;AAC3F,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,SAAoB,EAAE,OAAsB;IACxE,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,aAAa,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAEtE,MAAM,IAAI,GAAkB;QAC1B,WAAW,EAAE,SAAS,CAAC,WAAW;QAClC,YAAY,EAAE,SAAS,CAAC,YAAY;QACpC,iBAAiB,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,SAAS,CAAC,YAAY;QACzE,OAAO,EAAE,SAAS,CAAC,OAAO;KAC3B,CAAC;IAEF,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC;IACxB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,SAAoB;IACvD,MAAM,OAAO,GAAG,MAAM,WAAW,EAAE,CAAC;IACpC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;IACnF,CAAC;IAED,IAAI,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;QACvB,IAAI,CAAC;YACH,OAAO,MAAM,cAAc,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,YAAY,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,SAAoB,EACpB,GAA2C;IAE3C,IAAI,OAAO,GAAG,MAAM,cAAc,CAAC,SAAS,CAAC,CAAC;IAE9C,IAAI,CAAC;QACH,OAAO,MAAM,GAAG,CAAC,OAAO,CAAC,CAAC;IAC5B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,CAAC,KAAK,YAAY,cAAc,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC/D,MAAM,KAAK,CAAC;QACd,CAAC;QAED,OAAO,GAAG,MAAM,cAAc,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACnD,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC;IACtB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ProxyConnectionSnapshot, ProxyRequestEvent } from './local-proxy.js';
|
|
2
|
+
export type TunnelStatsSnapshot = {
|
|
3
|
+
ttl: number;
|
|
4
|
+
opn: number;
|
|
5
|
+
rt1Ms: number | null;
|
|
6
|
+
rt5Ms: number | null;
|
|
7
|
+
p50Ms: number | null;
|
|
8
|
+
p90Ms: number | null;
|
|
9
|
+
};
|
|
10
|
+
export declare class TunnelStats {
|
|
11
|
+
private totalConnections;
|
|
12
|
+
private openConnections;
|
|
13
|
+
private readonly latencySamples;
|
|
14
|
+
private sampleStartIndex;
|
|
15
|
+
updateConnections(snapshot: ProxyConnectionSnapshot): void;
|
|
16
|
+
recordRequest(event: Pick<ProxyRequestEvent, 'startedAtEpochMs' | 'durationMs'>): void;
|
|
17
|
+
getSnapshot(nowEpochMs?: number): TunnelStatsSnapshot;
|
|
18
|
+
private prune;
|
|
19
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const ONE_MINUTE_MS = 60_000;
|
|
2
|
+
const FIVE_MINUTES_MS = 300_000;
|
|
3
|
+
function average(values) {
|
|
4
|
+
if (values.length === 0) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
const total = values.reduce((sum, value) => sum + value, 0);
|
|
8
|
+
return total / values.length;
|
|
9
|
+
}
|
|
10
|
+
function percentile(sortedValues, rank) {
|
|
11
|
+
if (sortedValues.length === 0) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
const index = Math.max(0, Math.ceil(sortedValues.length * rank) - 1);
|
|
15
|
+
return sortedValues[index] ?? null;
|
|
16
|
+
}
|
|
17
|
+
export class TunnelStats {
|
|
18
|
+
totalConnections = 0;
|
|
19
|
+
openConnections = 0;
|
|
20
|
+
latencySamples = [];
|
|
21
|
+
sampleStartIndex = 0;
|
|
22
|
+
updateConnections(snapshot) {
|
|
23
|
+
this.totalConnections = snapshot.totalConnections;
|
|
24
|
+
this.openConnections = snapshot.openConnections;
|
|
25
|
+
}
|
|
26
|
+
recordRequest(event) {
|
|
27
|
+
this.latencySamples.push({
|
|
28
|
+
timestampEpochMs: event.startedAtEpochMs,
|
|
29
|
+
durationMs: Math.max(0, event.durationMs),
|
|
30
|
+
});
|
|
31
|
+
this.prune(event.startedAtEpochMs);
|
|
32
|
+
}
|
|
33
|
+
getSnapshot(nowEpochMs = Date.now()) {
|
|
34
|
+
this.prune(nowEpochMs);
|
|
35
|
+
const oneMinuteCutoff = nowEpochMs - ONE_MINUTE_MS;
|
|
36
|
+
const durationsIn1m = [];
|
|
37
|
+
const durationsIn5m = [];
|
|
38
|
+
for (let i = this.sampleStartIndex; i < this.latencySamples.length; i += 1) {
|
|
39
|
+
const sample = this.latencySamples[i];
|
|
40
|
+
if (!sample) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
durationsIn5m.push(sample.durationMs);
|
|
44
|
+
if (sample.timestampEpochMs >= oneMinuteCutoff) {
|
|
45
|
+
durationsIn1m.push(sample.durationMs);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const sortedIn5m = [...durationsIn5m].sort((a, b) => a - b);
|
|
49
|
+
return {
|
|
50
|
+
ttl: this.totalConnections,
|
|
51
|
+
opn: this.openConnections,
|
|
52
|
+
rt1Ms: average(durationsIn1m),
|
|
53
|
+
rt5Ms: average(durationsIn5m),
|
|
54
|
+
p50Ms: percentile(sortedIn5m, 0.5),
|
|
55
|
+
p90Ms: percentile(sortedIn5m, 0.9),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
prune(nowEpochMs) {
|
|
59
|
+
const oldestAllowed = nowEpochMs - FIVE_MINUTES_MS;
|
|
60
|
+
while (this.sampleStartIndex < this.latencySamples.length) {
|
|
61
|
+
const sample = this.latencySamples[this.sampleStartIndex];
|
|
62
|
+
if (!sample || sample.timestampEpochMs >= oldestAllowed) {
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
this.sampleStartIndex += 1;
|
|
66
|
+
}
|
|
67
|
+
const shouldCompact = this.sampleStartIndex > 0 &&
|
|
68
|
+
(this.sampleStartIndex >= 1024 || this.sampleStartIndex > Math.floor(this.latencySamples.length / 2));
|
|
69
|
+
if (shouldCompact) {
|
|
70
|
+
this.latencySamples.splice(0, this.sampleStartIndex);
|
|
71
|
+
this.sampleStartIndex = 0;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=tunnel-stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel-stats.js","sourceRoot":"","sources":["../../src/lib/tunnel-stats.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|