fix: admin/monitoring: "input" bandwidth was not counting https traffic

Massimo Melina committed Jun 5, 2025 at 21:38 UTC 6b2a7bc785ce7850b680e54b833f8dcd417dd22b
3 files changed +18 -9
src/connections.ts
+4 -3
@@ -17,6 +17,7 @@ export class Connection {
17 private _cachedIp?: string
18 [rest:symbol]: any // let other modules add extra data, but using symbols to avoid name collision
19
20 + // the sockets we collect are the plain ones, as soon as the tcp layer is connected
21 constructor(public readonly socket: Socket) {
22 all.push(this)
23 socket.on('close', () => {
@@ -45,9 +46,9 @@ export function newConnection(socket: Socket) {
46 const ip = normalizeIp(socket.remoteAddress || '')
47 const res = events.emit('newSocket', { socket, ip })
48 const msg = res?.isDefaultPrevented() ? 'plugin (newSocket)' : res?.find(_.isString)
48 - if (msg)
49 - return disconnect(socket, msg)
50 - new Connection(socket)
49 + if (!msg)
50 + return new Connection(socket)
51 + disconnect(socket, msg)
52 }
53
54 export function getConnections(): Readonly<typeof all> {
src/listen.ts
+9 -3
@@ -6,7 +6,8 @@ import { app } from './index'
6 import * as https from 'https'
7 import { watchLoad } from './watchLoad'
8 import { networkInterfaces } from 'os';
9 -import { newConnection } from './connections'
9 +import { getConnections, newConnection } from './connections'
10 +import { TLSSocket } from 'node:tls'
11 import open from 'open'
12 import {
13 CFG, debounceAsync, ipForUrl, makeNetMatcher, MINUTE, objSameKeys, onlyTruthy, prefix, runAt, wait, xlate
@@ -145,14 +146,19 @@ const considerHttps = debounceAsync(async () => {
146 }
147 port = await startServer(httpsSrv, { port, host: listenInterface.get() })
148 if (!port) return
148 - httpsSrv.on('connection', newConnection)
149 + httpsSrv.on('connection', newConnection) // this event is emitted as soon as the tcp layer is connected
150 + httpsSrv.on('secureConnection', (socket: TLSSocket) => { // emitted when the TLS layer is connected
151 + for (const c of getConnections()) // TLSSocket shares same ip:port, so we can find its matching Connection
152 + if (socket.remoteAddress === c.socket.remoteAddress
153 + && socket.remotePort === c.socket.remotePort)
154 + return c.socket.emit('secure', socket) // let know Connection about the secure socket
155 + })
156 printUrls(httpsSrv.name)
157 events.emit('httpsReady')
158 defaultBaseUrl.proto = 'https'
159 defaultBaseUrl.port = getCurrentPort(httpsSrv) ?? 0
160 }, { wait: 200 }) // give time to have key and cert ready
161
155 -
162 export const cert = defineConfig('cert', '')
163 export const privateKey = defineConfig('private_key', '')
164 const httpsNeeds = [cert, privateKey]
src/throttler.ts
+5 -3
@@ -121,6 +121,8 @@ setInterval(() => {
121 }
122 }, 1000)
123
124 -events.on('connection', (c: Connection) =>
125 - c.socket.on('data', data =>
126 - totalGot.set(x => x + data.length) ))
124 +events.on('connection', (c: Connection) => {
125 + const count = (data: Buffer | string) => totalGot.set(x => x + data.length)
126 + c.socket.on('data', count)
127 + c.socket.on('secure', s => s.on('data', count)) // secure sockets won't forward 'data' events to the plain ones
128 +})