File size: 2,693 Bytes
4cadbaf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106

import path from "path";
import websocket from "websocket";
// eslint-disable-next-line
import { Server } from "net";

import FileProxy from "./fileProxy";
import { command } from "yargs";



interface ServerOptions {
	rootDir: string;
};



const acceptFrontendConnection = (request, options: ServerOptions) => {
	const connection = request.accept("editor-frontend", request.origin);
	console.log("[web-editor] frontend accepted:", request.origin, connection.remoteAddress);

	let file = null;

	connection.on("close", (reasonCode, description) => {
		console.log("[web-editor] frontend quit:", connection.remoteAddress, reasonCode, description);

		if (file) {
			file.dispose();
			file = null;
		}
	});

	const sendCommand = (command: string, data: object) => connection.sendUTF(JSON.stringify({
		command,
		...data,
	}));

	connection.on("message", message => {
		const json = JSON.parse(message.utf8Data);
		switch (json.command) {
		case "bindFile":
			try {
				const filePath = path.resolve(options.rootDir, json.filePath);
				file = new FileProxy(filePath);

				file.on("error", err => sendCommand("failure", err));
				file.on("fullSync", data => sendCommand("fullSync", data));
				file.on("increase", data => sendCommand("increase", data));
			}
			catch (err) {
				console.warn("bindFile failed:", err);
				connection.sendUTF(JSON.stringify({ command: "failure", description: err.toString() }));
			}

			break;
		case "increase":
			if (!file)
				sendCommand("failure", {description: "no file bound yet"});
			else {
				file.increase({
					timestamp: json.timestamp,
					fromHash: json.fromHash,
					toHash: json.toHash,
					patch: json.patch,
				});
			}

			break;
		case "requestFullSync":
			if (!file)
				sendCommand("failure", {description: "no file bound yet"});
			else {
				console.assert(json.timestamp < file.timestamp, "[web-editor] requestFullSync sent from a newer peer:", new Date(json.timestamp), new Date(file.timestamp));
				file.fullSync();
			}

			break;
		default:
			console.warn("[web-editor] unexpected frontend command:", json);
		}
	});

	return connection;
};


export function createServer (httpServer: Server, options: ServerOptions) {
	const server = new websocket.server({
		httpServer,
		maxReceivedFrameSize: 0x1000000,
		maxReceivedMessageSize: 0x1000000,
		closeTimeout: 30e+3,
	});

	server.on("request", request => {
		//console.log("[Synchronizer] request received:", request.origin);

		if (request.requestedProtocols.includes("editor-frontend"))
			acceptFrontendConnection(request, options);
		else
			console.warn("Unexpected subprotocol request:", request.origin, request.requestedProtocols);
	});

	return server;
};