Base Server

This commit is contained in:
Flam3rboy 2021-02-06 17:08:08 +01:00
parent b41ef81ccb
commit 471d1d6b28
12 changed files with 203 additions and 43 deletions

17
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"sourceMaps": true,
"type": "node",
"request": "launch",
"name": "Launch Server",
"program": "${workspaceFolder}/dist/index.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": ["${workspaceFolder}/dist/**/*.js"]
}
]
}

View File

@ -1,9 +1,11 @@
require("missing-native-js-functions");
const WebSocket = require("ws");
const Constants = require("./dist/util/Constants");
const ws = new WebSocket("ws://127.0.0.1:8080");
ws.on("open", () => {
ws.send(JSON.stringify({ req_type: "new_auth" }));
// ws.send(JSON.stringify({ req_type: "new_auth" }));
// ws.send(JSON.stringify({ req_type: "check_auth", token: "" }));
// op: 0,
// d: {},
@ -11,6 +13,33 @@ ws.on("open", () => {
// t: "GATEWAY_EVENT_NAME",
});
ws.on("message", (data) => {
function send(data) {
ws.send(JSON.stringify(data));
}
ws.on("message", (buffer) => {
let data = JSON.parse(buffer.toString());
console.log(data);
switch (data.op) {
case 10:
setIntervalNow(() => {
send({ op: 1 });
}, data.d.heartbeat_interval);
send({
op: 2,
d: {
token: "",
intents: 0n,
properties: {},
},
});
break;
}
});
ws.on("close", (code, reason) => {
console.log(code, reason, Constants.CLOSECODES[code]);
});

BIN
package-lock.json generated

Binary file not shown.

16
src/Server.ts Normal file
View File

@ -0,0 +1,16 @@
import { db } from "discord-server-util";
import { Server as WebSocketServer } from "ws";
import { Connection } from "./events/Connection";
export class Server {
public ws: WebSocketServer;
constructor() {
this.ws = new WebSocketServer({ port: 8080, maxPayload: 4096 });
this.ws.on("connection", Connection);
}
async listen(): Promise<void> {
await db.init();
console.log("listening");
}
}

6
src/events/Close.ts Normal file
View File

@ -0,0 +1,6 @@
import WebSocket from "ws";
import { Message } from "./Message";
export function Close(this: WebSocket, code: number, reason: string) {
this.off("message", Message);
}

46
src/events/Connection.ts Normal file
View File

@ -0,0 +1,46 @@
import WebSocket, { Server } from "../util/WebSocket";
import { IncomingMessage } from "http";
import { Close } from "./Close";
import { Message } from "./Message";
import { setHeartbeat } from "../util/setHeartbeat";
import { Send } from "../util/Send";
import { CLOSECODES, OPCODES } from "../util/Constants";
// TODO: check rate limit
// TODO: specify rate limit in config
export function Connection(this: Server, socket: WebSocket, request: IncomingMessage) {
try {
socket.on("close", Close);
socket.on("message", Message);
const { searchParams } = new URL(`http://localhost${request.url}`);
// @ts-ignore
socket.encoding = searchParams.get("encoding") || "json";
if (!["json", "etf"].includes(socket.encoding)) return socket.close(CLOSECODES.Decode_error);
// @ts-ignore
socket.version = Number(searchParams.get("version")) || 8;
if (socket.version != 8) return socket.close(CLOSECODES.Invalid_API_version);
// @ts-ignore
socket.compression = searchParams.get("compress") || "";
// TODO: compression
setHeartbeat(socket);
Send(socket, {
op: OPCODES.Hello,
d: {
heartbeat_interval: 1000 * 30,
},
});
socket.readyTimeout = setTimeout(() => {
return socket.close(CLOSECODES.Session_timed_out);
}, 1000 * 30);
} catch (error) {
console.error(error);
return socket.close(CLOSECODES.Unknown_error);
}
}

36
src/events/Message.ts Normal file
View File

@ -0,0 +1,36 @@
import WebSocket, { Data } from "../util/WebSocket";
import erlpack from "erlpack";
import OPCodeHandlers from "../opcodes";
import { Payload, CLOSECODES } from "../util/Constants";
import { instanceOf } from "lambert-server";
const PayloadSchema = {
op: Number,
$d: Object,
$s: Number,
$t: String,
};
export async function Message(this: WebSocket, buffer: Data) {
// TODO: compression
var data: Payload;
try {
if (this.encoding === "etf" && buffer instanceof Buffer) data = erlpack.unpack(buffer);
else if (this.encoding === "json" && typeof buffer === "string") data = JSON.parse(buffer);
if (!instanceOf(PayloadSchema, data)) throw "invalid data";
} catch (error) {
return this.close(CLOSECODES.Decode_error);
}
// @ts-ignore
const OPCodeHandler = OPCodeHandlers[data.op];
if (!OPCodeHandler) return this.close(CLOSECODES.Unknown_opcode);
try {
return await OPCodeHandler.call(this, data);
} catch (error) {
console.error(error);
return this.close(CLOSECODES.Unknown_error);
}
}

View File

@ -1,43 +1,8 @@
import WebSocket from "ws";
import DB from "./extras/Database";
import { message_dev } from "./assets/datastru";
import { v4 } from "uuid";
process.on("uncaughtException", console.error);
process.on("unhandledRejection", console.error);
setTimeout(() => {}, 100000000);
class Server {
db: any;
constructor() {
this.db = DB;
}
import { Server } from "./Server";
async listen(): Promise<void> {
await this.db.init();
const wss = new WebSocket.Server({ port: 8080 });
wss.on("connection", (ws) => {
ws.on("message", async (msg: any) => {
const message: message_dev = msg;
if (message.req_type) {
switch (message.req_type) {
case "new_auth":
const token = v4();
await this.db.data.auth.push({ token });
return ws.send({ new_token: token });
case "check_auth":
if (!message.token) {
return ws.send({ error: "token not providen" });
}
return this.db.data.auth({ token: message.token }).get();
}
} else {
ws.send({ error: "req_type not providen" });
}
});
ws.send("connected");
});
}
}
const s = new Server();
s.listen();
const server = new Server();
server.listen();

View File

@ -4,6 +4,7 @@ export enum OPCODES {
Identify,
Presence_Update,
Voice_State_Update,
Dummy_Value, // ? What is opcode 5?
Resume,
Reconnect,
Request_Guild_Members,

21
src/util/Send.ts Normal file
View File

@ -0,0 +1,21 @@
import erlpack from "erlpack";
import { promisify } from "util";
import { Payload } from "../util/Constants";
import WebSocket from "./WebSocket";
export async function Send(socket: WebSocket, data: Payload) {
let buffer: Buffer | string;
if (socket.encoding === "etf") buffer = erlpack.pack(data);
// TODO: encode circular object
else if (socket.encoding === "json") buffer = JSON.stringify(data);
// TODO: compression
return new Promise((res, rej) => {
socket.send(buffer, (err) => {
if (err) return rej(err);
return res(null);
});
});
}

13
src/util/WebSocket.ts Normal file
View File

@ -0,0 +1,13 @@
import WS, { Server, Data } from "ws";
interface WebSocket extends WS {
version: number;
userid: bigint;
encoding: "etf" | "json";
compress?: "zlib-stream";
heartbeatTimeout: NodeJS.Timeout;
readyTimeout: NodeJS.Timeout;
}
export default WebSocket;
export { Server, Data };

10
src/util/setHeartbeat.ts Normal file
View File

@ -0,0 +1,10 @@
import WebSocket from "./WebSocket";
// TODO: make heartbeat timeout configurable
export function setHeartbeat(socket: WebSocket) {
if (socket.heartbeatTimeout) clearTimeout(socket.heartbeatTimeout);
socket.heartbeatTimeout = setTimeout(() => {
return socket.close(4009);
}, 1000 * 30);
}