2022-02-25 02:22:50 -05:00
|
|
|
// matrix-appservice-kakaotalk - A Matrix-KakaoTalk puppeting bridge.
|
|
|
|
// Copyright (C) 2022 Tulir Asokan, Andrew Ferrazzutti
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
import net from "net"
|
|
|
|
import fs from "fs"
|
|
|
|
import path from "path"
|
|
|
|
|
|
|
|
import PeerClient from "./client.js"
|
|
|
|
import { promisify } from "./util.js"
|
|
|
|
|
2022-05-15 22:17:28 -04:00
|
|
|
/**
|
|
|
|
* @typedef {object} ListenConfig
|
|
|
|
* @property {string} type
|
|
|
|
* @property {string} path
|
|
|
|
* @property {boolean} force
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @typedef {object} LoggingKeys
|
|
|
|
* @property {[string]} request
|
|
|
|
* @property {[string]} response
|
|
|
|
*/
|
|
|
|
|
2022-03-18 03:52:55 -04:00
|
|
|
|
2022-02-25 02:22:50 -05:00
|
|
|
export default class ClientManager {
|
2022-05-15 22:17:28 -04:00
|
|
|
/**
|
|
|
|
* @param {ListenConfig} listenConfig
|
|
|
|
* @param {number} registerTimeout
|
|
|
|
* @param {?LoggingKeys} loggingKeys
|
|
|
|
*/
|
|
|
|
constructor(listenConfig, registerTimeout, loggingKeys) {
|
|
|
|
if (!listenConfig) {
|
|
|
|
throw new Error("Listen config missing")
|
|
|
|
}
|
2022-02-25 02:22:50 -05:00
|
|
|
this.listenConfig = listenConfig
|
2022-05-10 01:27:31 -04:00
|
|
|
this.registerTimeout = registerTimeout || 3000
|
2022-05-15 22:17:28 -04:00
|
|
|
this.loggingKeys = loggingKeys || {request: [], response: []}
|
2022-02-25 02:22:50 -05:00
|
|
|
this.server = net.createServer(this.acceptConnection)
|
|
|
|
this.connections = []
|
|
|
|
this.clients = new Map()
|
|
|
|
this.connIDSequence = 0
|
|
|
|
this.stopped = false
|
|
|
|
}
|
|
|
|
|
|
|
|
log(...text) {
|
|
|
|
console.log("[API]", ...text)
|
|
|
|
}
|
|
|
|
|
|
|
|
acceptConnection = sock => {
|
|
|
|
if (this.stopped) {
|
|
|
|
sock.end()
|
|
|
|
sock.destroy()
|
|
|
|
} else {
|
|
|
|
const connID = this.connIDSequence++
|
|
|
|
this.connections[connID] = sock
|
2022-05-15 22:17:28 -04:00
|
|
|
new PeerClient(this, sock, connID, this.loggingKeys).start()
|
2022-02-25 02:22:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-20 23:06:37 -04:00
|
|
|
async startUnix(socketPath, force) {
|
2022-02-25 02:22:50 -05:00
|
|
|
try {
|
|
|
|
await fs.promises.access(path.dirname(socketPath))
|
|
|
|
} catch (err) {
|
|
|
|
await fs.promises.mkdir(path.dirname(socketPath), 0o700)
|
|
|
|
}
|
2022-04-20 23:06:37 -04:00
|
|
|
if (force) {
|
|
|
|
try {
|
|
|
|
await fs.promises.unlink(socketPath)
|
|
|
|
} catch (err) {}
|
|
|
|
}
|
2022-02-25 02:22:50 -05:00
|
|
|
await promisify(cb => this.server.listen(socketPath, cb))
|
|
|
|
await fs.promises.chmod(socketPath, 0o700)
|
|
|
|
this.log("Now listening at", socketPath)
|
|
|
|
}
|
|
|
|
|
|
|
|
async startTCP(port, host) {
|
|
|
|
await promisify(cb => this.server.listen(port, host, cb))
|
|
|
|
this.log(`Now listening at ${host || ""}:${port}`)
|
|
|
|
}
|
|
|
|
|
|
|
|
async start() {
|
|
|
|
this.log("Starting server")
|
|
|
|
|
|
|
|
if (this.listenConfig.type === "unix") {
|
2022-04-20 23:06:37 -04:00
|
|
|
await this.startUnix(this.listenConfig.path, this.listenConfig.force)
|
2022-02-25 02:22:50 -05:00
|
|
|
} else if (this.listenConfig.type === "tcp") {
|
|
|
|
await this.startTCP(this.listenConfig.port, this.listenConfig.host)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async stop() {
|
|
|
|
this.stopped = true
|
|
|
|
for (const client of this.clients.values()) {
|
|
|
|
await client.stop("Server is shutting down")
|
|
|
|
}
|
|
|
|
for (const socket of this.connections) {
|
|
|
|
socket.end()
|
|
|
|
socket.destroy()
|
|
|
|
}
|
|
|
|
this.log("Stopping server")
|
|
|
|
await promisify(cb => this.server.close(cb))
|
|
|
|
if (this.listenConfig.type === "unix") {
|
|
|
|
try {
|
|
|
|
await fs.promises.unlink(this.listenConfig.path)
|
|
|
|
} catch (err) {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|