Now with more WebSockets.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3197 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
		
							
								
								
									
										152
									
								
								core/client.js
									
									
									
									
									
								
							
							
						
						
									
										152
									
								
								core/client.js
									
									
									
									
									
								
							@@ -108,83 +108,58 @@ function split(container, children) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function receive() {
 | 
					function receive(data) {
 | 
				
			||||||
	$.ajax({
 | 
						for (var i in data.lines) {
 | 
				
			||||||
		url: url() + "/receive?sessionId=" + gSessionId,
 | 
							var line = data.lines[i];
 | 
				
			||||||
			method: "POST",
 | 
					 | 
				
			||||||
			data: gHaveIndex.toString(),
 | 
					 | 
				
			||||||
			dataType: "json",
 | 
					 | 
				
			||||||
	}).then(function(data) {
 | 
					 | 
				
			||||||
		for (var i in data.lines) {
 | 
					 | 
				
			||||||
			var line = data.lines[i];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			var target = document.getElementsByClassName("terminal")[0].id;
 | 
							var target = document.getElementsByClassName("terminal")[0].id;
 | 
				
			||||||
			if (line && line.terminal) {
 | 
							if (line && line.terminal) {
 | 
				
			||||||
				if (document.getElementById("terminal_" + line.terminal)) {
 | 
								if (document.getElementById("terminal_" + line.terminal)) {
 | 
				
			||||||
					target = "terminal_" + line.terminal;
 | 
									target = "terminal_" + line.terminal;
 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				line = line.value;
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if (line && line.action == "ping") {
 | 
								line = line.value;
 | 
				
			||||||
				// PONG
 | 
							}
 | 
				
			||||||
			} else if (line && line.action == "session") {
 | 
							if (line && line.action == "ping") {
 | 
				
			||||||
				gSessionId = line.session.sessionId;
 | 
								gSocket.send(JSON.stringify({action: "pong"}));
 | 
				
			||||||
				gCredentials = line.session.credentials;
 | 
							} else if (line && line.action == "session") {
 | 
				
			||||||
				updateLogin();
 | 
								gSessionId = line.session.sessionId;
 | 
				
			||||||
			} else if (line && line[0] && line[0].action == "ready") {
 | 
								gCredentials = line.session.credentials;
 | 
				
			||||||
				if (window.location.hash) {
 | 
								updateLogin();
 | 
				
			||||||
					send({event: "hashChange", hash: window.location.hash});
 | 
							} else if (line && line[0] && line[0].action == "ready") {
 | 
				
			||||||
				}
 | 
								if (window.location.hash) {
 | 
				
			||||||
			} else if (line && line[0] && line[0].action == "notify") {
 | 
									send({event: "hashChange", hash: window.location.hash});
 | 
				
			||||||
				new Notification(line[0].title, line[0].options);
 | 
								}
 | 
				
			||||||
			} else if (line && line[0] && line[0].action == "title") {
 | 
							} else if (line && line[0] && line[0].action == "notify") {
 | 
				
			||||||
				window.document.title = line[0].value;
 | 
								new Notification(line[0].title, line[0].options);
 | 
				
			||||||
			} else if (line && line[0] && line[0].action == "prompt") {
 | 
							} else if (line && line[0] && line[0].action == "title") {
 | 
				
			||||||
				var prompt = document.getElementById("prompt");
 | 
								window.document.title = line[0].value;
 | 
				
			||||||
				while (prompt.firstChild) {
 | 
							} else if (line && line[0] && line[0].action == "prompt") {
 | 
				
			||||||
					prompt.removeChild(prompt.firstChild);
 | 
								var prompt = document.getElementById("prompt");
 | 
				
			||||||
				}
 | 
								while (prompt.firstChild) {
 | 
				
			||||||
				prompt.appendChild(document.createTextNode(line[0].value));
 | 
									prompt.removeChild(prompt.firstChild);
 | 
				
			||||||
			} else if (line && line[0] && line[0].action == "password") {
 | 
								}
 | 
				
			||||||
				var prompt = document.getElementById("input");
 | 
								prompt.appendChild(document.createTextNode(line[0].value));
 | 
				
			||||||
				prompt.setAttribute("type", line[0].value ? "password" : "text");
 | 
							} else if (line && line[0] && line[0].action == "password") {
 | 
				
			||||||
			} else if (line && line[0] && line[0].action == "hash") {
 | 
								var prompt = document.getElementById("input");
 | 
				
			||||||
				window.location.hash = line[0].value;
 | 
								prompt.setAttribute("type", line[0].value ? "password" : "text");
 | 
				
			||||||
			} else if (line && line[0] && line[0].action == "update") {
 | 
							} else if (line && line[0] && line[0].action == "hash") {
 | 
				
			||||||
				document.getElementById("update").setAttribute("Style", "display: inline");
 | 
								window.location.hash = line[0].value;
 | 
				
			||||||
			} else if (line && line[0] && line[0].action == "split") {
 | 
							} else if (line && line[0] && line[0].action == "update") {
 | 
				
			||||||
				split(document.getElementById("terminals"), line[0].options);
 | 
								document.getElementById("update").setAttribute("Style", "display: inline");
 | 
				
			||||||
			} else if (line && line[0] && line[0].action == "postMessageToIframe") {
 | 
							} else if (line && line[0] && line[0].action == "split") {
 | 
				
			||||||
				var iframe = document.getElementById("iframe_" + line[0].name);
 | 
								split(document.getElementById("terminals"), line[0].options);
 | 
				
			||||||
				if (iframe) {
 | 
							} else if (line && line[0] && line[0].action == "postMessageToIframe") {
 | 
				
			||||||
					iframe.contentWindow.postMessage(line[0].message, "*");
 | 
								var iframe = document.getElementById("iframe_" + line[0].name);
 | 
				
			||||||
				}
 | 
								if (iframe) {
 | 
				
			||||||
			} else {
 | 
									iframe.contentWindow.postMessage(line[0].message, "*");
 | 
				
			||||||
				print(document.getElementById(target), line);
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if ("index" in data) {
 | 
					 | 
				
			||||||
			gHaveIndex = data.index;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		receive();
 | 
					 | 
				
			||||||
		if (gErrorCount) {
 | 
					 | 
				
			||||||
			document.getElementById("status").setAttribute("style", "display: none");
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		gErrorCount = 0;
 | 
					 | 
				
			||||||
	}).fail(function(xhr, message, error) {
 | 
					 | 
				
			||||||
		var node = document.getElementById("status");
 | 
					 | 
				
			||||||
		while (node.firstChild) {
 | 
					 | 
				
			||||||
			node.removeChild(node.firstChild);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		node.appendChild(document.createTextNode("ERROR: " + JSON.stringify([message, error])));
 | 
					 | 
				
			||||||
		node.setAttribute("style", "display: inline; color: #dc322f");
 | 
					 | 
				
			||||||
		if (gErrorCount < 60) {
 | 
					 | 
				
			||||||
			setTimeout(receive, 1000);
 | 
					 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			setTimeout(receive, 60 * 1000);
 | 
								print(document.getElementById(target), line);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		gErrorCount++;
 | 
						}
 | 
				
			||||||
	});
 | 
						if ("index" in data) {
 | 
				
			||||||
 | 
							gHaveIndex = data.index;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function autoNewLine(terminal) {
 | 
					function autoNewLine(terminal) {
 | 
				
			||||||
@@ -282,19 +257,16 @@ function send(command) {
 | 
				
			|||||||
		value = prefix + $("#input").val();
 | 
							value = prefix + $("#input").val();
 | 
				
			||||||
		$("#input").val("");
 | 
							$("#input").val("");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	$.ajax({
 | 
						try {
 | 
				
			||||||
		url: url() + "/send?sessionId=" + gSessionId,
 | 
							gSocket.send(JSON.stringify({action: "command", command: value}));
 | 
				
			||||||
			method: "POST",
 | 
						} catch (error) {
 | 
				
			||||||
			data: JSON.stringify(value),
 | 
					 | 
				
			||||||
			dataType: "text",
 | 
					 | 
				
			||||||
	}).fail(function(xhr, status, error) {
 | 
					 | 
				
			||||||
		var node = document.getElementById("status");
 | 
							var node = document.getElementById("status");
 | 
				
			||||||
		while (node.firstChild) {
 | 
							while (node.firstChild) {
 | 
				
			||||||
			node.removeChild(node.firstChild);
 | 
								node.removeChild(node.firstChild);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		node.appendChild(document.createTextNode("Send failed: " + JSON.stringify([status, error])));
 | 
							node.appendChild(document.createTextNode("Send failed: " + error));
 | 
				
			||||||
		node.setAttribute("style", "display: inline; color: #dc322f");
 | 
							node.setAttribute("style", "display: inline; color: #dc322f");
 | 
				
			||||||
	});
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function updateLogin() {
 | 
					function updateLogin() {
 | 
				
			||||||
@@ -437,6 +409,8 @@ function onMessage(event) {
 | 
				
			|||||||
	send({event: "onWindowMessage", message: event.data});
 | 
						send({event: "onWindowMessage", message: event.data});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var gSocket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(document).ready(function() {
 | 
					$(document).ready(function() {
 | 
				
			||||||
	if (Notification) {
 | 
						if (Notification) {
 | 
				
			||||||
		Notification.requestPermission();
 | 
							Notification.requestPermission();
 | 
				
			||||||
@@ -448,10 +422,18 @@ $(document).ready(function() {
 | 
				
			|||||||
	window.addEventListener("blur", blur);
 | 
						window.addEventListener("blur", blur);
 | 
				
			||||||
	window.addEventListener("message", onMessage, false);
 | 
						window.addEventListener("message", onMessage, false);
 | 
				
			||||||
	enableDragDrop();
 | 
						enableDragDrop();
 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(window).load(function() {
 | 
						gSocket = new WebSocket("ws://"
 | 
				
			||||||
	setTimeout(function() {
 | 
							+ window.location.hostname
 | 
				
			||||||
		receive();
 | 
							+ (window.location.port.length ? ":" + window.location.port : "")
 | 
				
			||||||
	}, 0);
 | 
							+ "/terminal/socket");
 | 
				
			||||||
 | 
						gSocket.onopen = function() {
 | 
				
			||||||
 | 
							gSocket.send(JSON.stringify({
 | 
				
			||||||
 | 
								action: "hello",
 | 
				
			||||||
 | 
								path: window.location.pathname,
 | 
				
			||||||
 | 
							}));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						gSocket.onmessage = function(event) {
 | 
				
			||||||
 | 
							receive(JSON.parse(event.data));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										27
									
								
								core/core.js
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								core/core.js
									
									
									
									
									
								
							@@ -146,27 +146,6 @@ function getUsers(packageOwner, packageName) {
 | 
				
			|||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function ping() {
 | 
					 | 
				
			||||||
	var process = this;
 | 
					 | 
				
			||||||
	var now = Date.now();
 | 
					 | 
				
			||||||
	var again = true;
 | 
					 | 
				
			||||||
	if (now - process.lastActive < process.timeout) {
 | 
					 | 
				
			||||||
		// Active.
 | 
					 | 
				
			||||||
	} else if (process.lastPing > process.lastActive) {
 | 
					 | 
				
			||||||
		// We lost them.
 | 
					 | 
				
			||||||
		process.task.kill();
 | 
					 | 
				
			||||||
		again = false;
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		// Idle.  Ping them.
 | 
					 | 
				
			||||||
		process.terminal.ping();
 | 
					 | 
				
			||||||
		process.lastPing = now;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (again) {
 | 
					 | 
				
			||||||
		setTimeout(ping.bind(process), process.timeout);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function postMessageInternal(from, to, message) {
 | 
					function postMessageInternal(from, to, message) {
 | 
				
			||||||
	return invoke(to.eventHandlers['onMessage'], [getUser(from, from), message]);
 | 
						return invoke(to.eventHandlers['onMessage'], [getUser(from, from), message]);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -273,9 +252,6 @@ function getProcess(packageOwner, packageName, key, options) {
 | 
				
			|||||||
				process.connections.length = 0;
 | 
									process.connections.length = 0;
 | 
				
			||||||
				delete gProcesses[key];
 | 
									delete gProcesses[key];
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
			if (process.timeout > 0) {
 | 
					 | 
				
			||||||
				setTimeout(ping.bind(process), process.timeout);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			var imports = {
 | 
								var imports = {
 | 
				
			||||||
				'core': {
 | 
									'core': {
 | 
				
			||||||
					'broadcast': broadcast.bind(process),
 | 
										'broadcast': broadcast.bind(process),
 | 
				
			||||||
@@ -352,8 +328,6 @@ function getProcess(packageOwner, packageName, key, options) {
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if (manifest && manifest.require) {
 | 
								if (manifest && manifest.require) {
 | 
				
			||||||
				print("manifest.require = ", manifest.require);
 | 
					 | 
				
			||||||
				print(manifest.require.map(packageNameToPath.bind(process)));
 | 
					 | 
				
			||||||
				process.task.addPath(manifest.require.map(packageNameToPath.bind(process)));
 | 
									process.task.addPath(manifest.require.map(packageNameToPath.bind(process)));
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			process.task.setImports(imports);
 | 
								process.task.setImports(imports);
 | 
				
			||||||
@@ -439,3 +413,4 @@ httpd.all("", function(request, response) {
 | 
				
			|||||||
		return response.end(data);
 | 
							return response.end(data);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					httpd.registerSocketHandler("/terminal/socket", terminal.socket);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										161
									
								
								core/httpd.js
									
									
									
									
									
								
							
							
						
						
									
										161
									
								
								core/httpd.js
									
									
									
									
									
								
							@@ -1,4 +1,7 @@
 | 
				
			|||||||
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var gHandlers = [];
 | 
					var gHandlers = [];
 | 
				
			||||||
 | 
					var gSocketHandlers = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function logError(error) {
 | 
					function logError(error) {
 | 
				
			||||||
	print("ERROR " + error);
 | 
						print("ERROR " + error);
 | 
				
			||||||
@@ -27,6 +30,14 @@ function all(prefix, handler) {
 | 
				
			|||||||
	});
 | 
						});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function registerSocketHandler(prefix, handler) {
 | 
				
			||||||
 | 
						gSocketHandlers.push({
 | 
				
			||||||
 | 
							owner: this,
 | 
				
			||||||
 | 
							path: prefix,
 | 
				
			||||||
 | 
							invoke: handler,
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Request(method, uri, version, headers, body, client) {
 | 
					function Request(method, uri, version, headers, body, client) {
 | 
				
			||||||
	this.method = method;
 | 
						this.method = method;
 | 
				
			||||||
	var index = uri.indexOf("?");
 | 
						var index = uri.indexOf("?");
 | 
				
			||||||
@@ -56,8 +67,21 @@ function findHandler(request) {
 | 
				
			|||||||
	return matchedHandler;
 | 
						return matchedHandler;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function findSocketHandler(request) {
 | 
				
			||||||
 | 
						var matchedHandler = null;
 | 
				
			||||||
 | 
						for (var name in gSocketHandlers) {
 | 
				
			||||||
 | 
							var handler = gSocketHandlers[name];
 | 
				
			||||||
 | 
							if (request.uri == handler.path || request.uri.slice(0, handler.path.length + 1) == handler.path + '/') {
 | 
				
			||||||
 | 
								matchedHandler = handler;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return matchedHandler;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Response(request, client) {
 | 
					function Response(request, client) {
 | 
				
			||||||
	var kStatusText = {
 | 
						var kStatusText = {
 | 
				
			||||||
 | 
							101: "Switching Protocols",
 | 
				
			||||||
		200: 'OK',
 | 
							200: 'OK',
 | 
				
			||||||
		303: 'See other',
 | 
							303: 'See other',
 | 
				
			||||||
		403: 'Forbidden',
 | 
							403: 'Forbidden',
 | 
				
			||||||
@@ -160,6 +184,136 @@ function handleRequest(request, response) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function handleWebSocketRequest(request, response, client) {
 | 
				
			||||||
 | 
						var buffer = "";
 | 
				
			||||||
 | 
						var frame = "";
 | 
				
			||||||
 | 
						var frameOpCode = 0x0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var handler = findSocketHandler(request);
 | 
				
			||||||
 | 
						if (!handler) {
 | 
				
			||||||
 | 
							client.close();
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						response.send = function(message, opCode) {
 | 
				
			||||||
 | 
							if (opCode === undefined) {
 | 
				
			||||||
 | 
								opCode = 0x2;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							var fin = true;
 | 
				
			||||||
 | 
							var packet = String.fromCharCode((fin ? (1 << 7) : 0) | (opCode & 0xf));
 | 
				
			||||||
 | 
							var mask = false;
 | 
				
			||||||
 | 
							if (message.length < 126) {
 | 
				
			||||||
 | 
								packet += String.fromCharCode((mask ? (1 << 7) : 0) | message.length);
 | 
				
			||||||
 | 
							} else if (message.length < (1 << 16)) {
 | 
				
			||||||
 | 
								packet += String.fromCharCode((mask ? (1 << 7) : 0) | 126);
 | 
				
			||||||
 | 
								packet += String.fromCharCode((message.length >> 8) & 0xff);
 | 
				
			||||||
 | 
								packet += String.fromCharCode(message.length & 0xff);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								packet += String.fromCharCode((mask ? (1 << 7) : 0) | 127);
 | 
				
			||||||
 | 
								packet += String.fromCharCode((message.length >> 24) & 0xff);
 | 
				
			||||||
 | 
								packet += String.fromCharCode((message.length >> 16) & 0xff);
 | 
				
			||||||
 | 
								packet += String.fromCharCode((message.length >> 8) & 0xff);
 | 
				
			||||||
 | 
								packet += String.fromCharCode(message.length & 0xff);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							packet += message;
 | 
				
			||||||
 | 
							return client.write(packet);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						response.onMessage = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						handler.invoke(request, response);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client.read(function(data) {
 | 
				
			||||||
 | 
							if (data) {
 | 
				
			||||||
 | 
								buffer += data;
 | 
				
			||||||
 | 
								if (buffer.length >= 2) {
 | 
				
			||||||
 | 
									var bits0 = buffer.charCodeAt(0);
 | 
				
			||||||
 | 
									var bits1 = buffer.charCodeAt(1);
 | 
				
			||||||
 | 
									if (bits1 & (1 << 7) == 0) {
 | 
				
			||||||
 | 
										// Unmasked message.
 | 
				
			||||||
 | 
										client.close();
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									var opCode = bits0 & 0xf;
 | 
				
			||||||
 | 
									var fin = bits0 & (1 << 7);
 | 
				
			||||||
 | 
									var payloadLength = bits1 & 0x7f;
 | 
				
			||||||
 | 
									var maskStart = 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (payloadLength == 126) {
 | 
				
			||||||
 | 
										payloadLength = 0;
 | 
				
			||||||
 | 
										for (var i = 0; i < 2; i++) {
 | 
				
			||||||
 | 
											payloadLength <<= 8;
 | 
				
			||||||
 | 
											payloadLength |= buffer.charCodeAt(2 + i);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										maskStart = 4;
 | 
				
			||||||
 | 
									} else if (payloadLength == 127) {
 | 
				
			||||||
 | 
										payloadLength = 0;
 | 
				
			||||||
 | 
										for (var i = 0; i < 8; i++) {
 | 
				
			||||||
 | 
											payloadLength <<= 8;
 | 
				
			||||||
 | 
											payloadLength |= buffer.charCodeAt(2 + i);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										maskStart = 10;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									var havePayload = buffer.length >= payloadLength + 2 + 4;
 | 
				
			||||||
 | 
									if (havePayload) {
 | 
				
			||||||
 | 
										var mask = buffer.substring(maskStart, maskStart + 4);
 | 
				
			||||||
 | 
										var dataStart = maskStart + 4;
 | 
				
			||||||
 | 
										var decoded = "";
 | 
				
			||||||
 | 
										var payload = buffer.substring(dataStart, dataStart + payloadLength);
 | 
				
			||||||
 | 
										buffer = buffer.substring(dataStart + payloadLength);
 | 
				
			||||||
 | 
										for (var i = 0; i < payloadLength; i++) {
 | 
				
			||||||
 | 
											decoded += String.fromCharCode(payload.charCodeAt(i) ^ mask.charCodeAt(i % 4));
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										frame += decoded;
 | 
				
			||||||
 | 
										if (opCode) {
 | 
				
			||||||
 | 
											frameOpCode = opCode;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if (fin) {
 | 
				
			||||||
 | 
											if (response.onMessage) {
 | 
				
			||||||
 | 
												response.onMessage({
 | 
				
			||||||
 | 
													data: frame,
 | 
				
			||||||
 | 
													opCode: frameOpCode,
 | 
				
			||||||
 | 
												});
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											frame = "";
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						client.onError(function(error) {
 | 
				
			||||||
 | 
							logError(client.peerName + " - - [" + new Date() + "] " + error);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						response.writeHead(101, {
 | 
				
			||||||
 | 
							"Upgrade": "websocket",
 | 
				
			||||||
 | 
							"Connection": "Upgrade",
 | 
				
			||||||
 | 
							"Sec-WebSocket-Accept": webSocketAcceptResponse(request.headers["sec-websocket-key"]),
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function webSocketAcceptResponse(key) {
 | 
				
			||||||
 | 
						var kMagic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
 | 
				
			||||||
 | 
						var kAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
 | 
				
			||||||
 | 
						var hex = require("sha1").hash(key + kMagic)
 | 
				
			||||||
 | 
						var binary = "";
 | 
				
			||||||
 | 
						for (var i = 0; i < hex.length; i += 6) {
 | 
				
			||||||
 | 
							var characters = hex.substring(i, i + 6);
 | 
				
			||||||
 | 
							if (characters.length < 6) {
 | 
				
			||||||
 | 
								characters += "0".repeat(6 - characters.length);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							var value = parseInt(characters, 16);
 | 
				
			||||||
 | 
							for (var bit = 0; bit < 8 * 3; bit += 6) {
 | 
				
			||||||
 | 
								if (i * 8 / 2 + bit >= 8 * hex.length / 2) {
 | 
				
			||||||
 | 
									binary += kAlphabet.charAt(64);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									binary += kAlphabet.charAt((value >> (18 - bit)) & 63);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return binary;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function handleConnection(client) {
 | 
					function handleConnection(client) {
 | 
				
			||||||
	var inputBuffer = "";
 | 
						var inputBuffer = "";
 | 
				
			||||||
	var request;
 | 
						var request;
 | 
				
			||||||
@@ -207,6 +361,12 @@ function handleConnection(client) {
 | 
				
			|||||||
					lineByLine = false;
 | 
										lineByLine = false;
 | 
				
			||||||
					body = "";
 | 
										body = "";
 | 
				
			||||||
					return true;
 | 
										return true;
 | 
				
			||||||
 | 
									} else if (headers["connection"].toLowerCase().split(",").map(x => x.trim()).indexOf("upgrade") != -1
 | 
				
			||||||
 | 
										&& headers["upgrade"].toLowerCase() == "websocket") {
 | 
				
			||||||
 | 
										var requestObject = new Request(request[0], request[1], request[2], headers, body, client);
 | 
				
			||||||
 | 
										var response = new Response(requestObject, client);
 | 
				
			||||||
 | 
										handleWebSocketRequest(requestObject, response, client);
 | 
				
			||||||
 | 
										return false;
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					finish();
 | 
										finish();
 | 
				
			||||||
					return false;
 | 
										return false;
 | 
				
			||||||
@@ -291,3 +451,4 @@ if (privateKey && certificate) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exports.all = all;
 | 
					exports.all = all;
 | 
				
			||||||
 | 
					exports.registerSocketHandler = registerSocketHandler;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										160
									
								
								core/sha1.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								core/sha1.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,160 @@
 | 
				
			|||||||
 | 
					/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
 | 
				
			||||||
 | 
					/*  SHA-1 implementation in JavaScript                  (c) Chris Veness 2002-2014 / MIT Licence  */
 | 
				
			||||||
 | 
					/*                                                                                                */
 | 
				
			||||||
 | 
					/*  - see http://csrc.nist.gov/groups/ST/toolkit/secure_hashing.html                              */
 | 
				
			||||||
 | 
					/*        http://csrc.nist.gov/groups/ST/toolkit/examples.html                                    */
 | 
				
			||||||
 | 
					/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* jshint node:true *//* global define, escape, unescape */
 | 
				
			||||||
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * SHA-1 hash function reference implementation.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @namespace
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					var Sha1 = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Generates SHA-1 hash of string.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param   {string} msg - (Unicode) string to be hashed.
 | 
				
			||||||
 | 
					 * @returns {string} Hash of msg as hex character string.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					Sha1.hash = function(msg) {
 | 
				
			||||||
 | 
					    // convert string to UTF-8, as SHA only deals with byte-streams
 | 
				
			||||||
 | 
					    msg = msg.utf8Encode();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // constants [§4.2.1]
 | 
				
			||||||
 | 
					    var K = [ 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // PREPROCESSING
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    msg += String.fromCharCode(0x80);  // add trailing '1' bit (+ 0's padding) to string [§5.1.1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // convert string msg into 512-bit/16-integer blocks arrays of ints [§5.2.1]
 | 
				
			||||||
 | 
					    var l = msg.length/4 + 2; // length (in 32-bit integers) of msg + ‘1’ + appended length
 | 
				
			||||||
 | 
					    var N = Math.ceil(l/16);  // number of 16-integer-blocks required to hold 'l' ints
 | 
				
			||||||
 | 
					    var M = new Array(N);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (var i=0; i<N; i++) {
 | 
				
			||||||
 | 
					        M[i] = new Array(16);
 | 
				
			||||||
 | 
					        for (var j=0; j<16; j++) {  // encode 4 chars per integer, big-endian encoding
 | 
				
			||||||
 | 
					            M[i][j] = (msg.charCodeAt(i*64+j*4)<<24) | (msg.charCodeAt(i*64+j*4+1)<<16) |
 | 
				
			||||||
 | 
					                (msg.charCodeAt(i*64+j*4+2)<<8) | (msg.charCodeAt(i*64+j*4+3));
 | 
				
			||||||
 | 
					        } // note running off the end of msg is ok 'cos bitwise ops on NaN return 0
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // add length (in bits) into final pair of 32-bit integers (big-endian) [§5.1.1]
 | 
				
			||||||
 | 
					    // note: most significant word would be (len-1)*8 >>> 32, but since JS converts
 | 
				
			||||||
 | 
					    // bitwise-op args to 32 bits, we need to simulate this by arithmetic operators
 | 
				
			||||||
 | 
					    M[N-1][14] = ((msg.length-1)*8) / Math.pow(2, 32); M[N-1][14] = Math.floor(M[N-1][14]);
 | 
				
			||||||
 | 
					    M[N-1][15] = ((msg.length-1)*8) & 0xffffffff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // set initial hash value [§5.3.1]
 | 
				
			||||||
 | 
					    var H0 = 0x67452301;
 | 
				
			||||||
 | 
					    var H1 = 0xefcdab89;
 | 
				
			||||||
 | 
					    var H2 = 0x98badcfe;
 | 
				
			||||||
 | 
					    var H3 = 0x10325476;
 | 
				
			||||||
 | 
					    var H4 = 0xc3d2e1f0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // HASH COMPUTATION [§6.1.2]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var W = new Array(80); var a, b, c, d, e;
 | 
				
			||||||
 | 
					    for (var i=0; i<N; i++) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 1 - prepare message schedule 'W'
 | 
				
			||||||
 | 
					        for (var t=0;  t<16; t++) W[t] = M[i][t];
 | 
				
			||||||
 | 
					        for (var t=16; t<80; t++) W[t] = Sha1.ROTL(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 2 - initialise five working variables a, b, c, d, e with previous hash value
 | 
				
			||||||
 | 
					        a = H0; b = H1; c = H2; d = H3; e = H4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 3 - main loop
 | 
				
			||||||
 | 
					        for (var t=0; t<80; t++) {
 | 
				
			||||||
 | 
					            var s = Math.floor(t/20); // seq for blocks of 'f' functions and 'K' constants
 | 
				
			||||||
 | 
					            var T = (Sha1.ROTL(a,5) + Sha1.f(s,b,c,d) + e + K[s] + W[t]) & 0xffffffff;
 | 
				
			||||||
 | 
					            e = d;
 | 
				
			||||||
 | 
					            d = c;
 | 
				
			||||||
 | 
					            c = Sha1.ROTL(b, 30);
 | 
				
			||||||
 | 
					            b = a;
 | 
				
			||||||
 | 
					            a = T;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 4 - compute the new intermediate hash value (note 'addition modulo 2^32')
 | 
				
			||||||
 | 
					        H0 = (H0+a) & 0xffffffff;
 | 
				
			||||||
 | 
					        H1 = (H1+b) & 0xffffffff;
 | 
				
			||||||
 | 
					        H2 = (H2+c) & 0xffffffff;
 | 
				
			||||||
 | 
					        H3 = (H3+d) & 0xffffffff;
 | 
				
			||||||
 | 
					        H4 = (H4+e) & 0xffffffff;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return Sha1.toHexStr(H0) + Sha1.toHexStr(H1) + Sha1.toHexStr(H2) +
 | 
				
			||||||
 | 
					           Sha1.toHexStr(H3) + Sha1.toHexStr(H4);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Function 'f' [§4.1.1].
 | 
				
			||||||
 | 
					 * @private
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					Sha1.f = function(s, x, y, z)  {
 | 
				
			||||||
 | 
					    switch (s) {
 | 
				
			||||||
 | 
					        case 0: return (x & y) ^ (~x & z);           // Ch()
 | 
				
			||||||
 | 
					        case 1: return  x ^ y  ^  z;                 // Parity()
 | 
				
			||||||
 | 
					        case 2: return (x & y) ^ (x & z) ^ (y & z);  // Maj()
 | 
				
			||||||
 | 
					        case 3: return  x ^ y  ^  z;                 // Parity()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Rotates left (circular left shift) value x by n positions [§3.2.5].
 | 
				
			||||||
 | 
					 * @private
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					Sha1.ROTL = function(x, n) {
 | 
				
			||||||
 | 
					    return (x<<n) | (x>>>(32-n));
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Hexadecimal representation of a number.
 | 
				
			||||||
 | 
					 * @private
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					Sha1.toHexStr = function(n) {
 | 
				
			||||||
 | 
					    // note can't use toString(16) as it is implementation-dependant,
 | 
				
			||||||
 | 
					    // and in IE returns signed numbers when used on full words
 | 
				
			||||||
 | 
					    var s="", v;
 | 
				
			||||||
 | 
					    for (var i=7; i>=0; i--) { v = (n>>>(i*4)) & 0xf; s += v.toString(16); }
 | 
				
			||||||
 | 
					    return s;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Extend String object with method to encode multi-byte string to utf8
 | 
				
			||||||
 | 
					 *  - monsur.hossa.in/2012/07/20/utf-8-in-javascript.html */
 | 
				
			||||||
 | 
					if (typeof String.prototype.utf8Encode == 'undefined') {
 | 
				
			||||||
 | 
					    String.prototype.utf8Encode = function() {
 | 
				
			||||||
 | 
					        return unescape( encodeURIComponent( this ) );
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Extend String object with method to decode utf8 string to multi-byte */
 | 
				
			||||||
 | 
					if (typeof String.prototype.utf8Decode == 'undefined') {
 | 
				
			||||||
 | 
					    String.prototype.utf8Decode = function() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            return decodeURIComponent( escape( this ) );
 | 
				
			||||||
 | 
					        } catch (e) {
 | 
				
			||||||
 | 
					            return this; // invalid UTF-8? return as-is
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
 | 
				
			||||||
 | 
					if (typeof module != 'undefined' && module.exports) module.exports = Sha1; // CommonJs export
 | 
				
			||||||
 | 
					if (typeof define == 'function' && define.amd) define([], function() { return Sha1; }); // AMD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports.hash = Sha1.hash;
 | 
				
			||||||
							
								
								
									
										256
									
								
								core/terminal.js
									
									
									
									
									
								
							
							
						
						
									
										256
									
								
								core/terminal.js
									
									
									
									
									
								
							@@ -14,9 +14,9 @@ var auth = require('auth');
 | 
				
			|||||||
var form = require('form');
 | 
					var form = require('form');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Terminal() {
 | 
					function Terminal() {
 | 
				
			||||||
	this._waiting = [];
 | 
					 | 
				
			||||||
	this._index = 0;
 | 
						this._index = 0;
 | 
				
			||||||
	this._firstLine = 0;
 | 
						this._firstLine = 0;
 | 
				
			||||||
 | 
						this._sentIndex = -1;
 | 
				
			||||||
	this._lines = [];
 | 
						this._lines = [];
 | 
				
			||||||
	this._lastRead = null;
 | 
						this._lastRead = null;
 | 
				
			||||||
	this._lastWrite = null;
 | 
						this._lastWrite = null;
 | 
				
			||||||
@@ -24,29 +24,31 @@ function Terminal() {
 | 
				
			|||||||
	this._readLine = null;
 | 
						this._readLine = null;
 | 
				
			||||||
	this._selected = null;
 | 
						this._selected = null;
 | 
				
			||||||
	this._corked = 0;
 | 
						this._corked = 0;
 | 
				
			||||||
 | 
						this._onOutput = null;
 | 
				
			||||||
	return this;
 | 
						return this;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Terminal.kBacklog = 64;
 | 
					Terminal.kBacklog = 64;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Terminal.prototype.dispatch = function(data) {
 | 
					Terminal.prototype.readOutput = function(callback) {
 | 
				
			||||||
	for (var i in this._waiting) {
 | 
						this._onOutput = callback;
 | 
				
			||||||
		this.feedWaiting(this._waiting[i], data);
 | 
						this.dispatch();
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	this._waiting.length = 0;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Terminal.prototype.feedWaiting = function(waiting, data) {
 | 
					Terminal.prototype.dispatch = function(data) {
 | 
				
			||||||
	var terminal = this;
 | 
						var payload = this._lines.slice(Math.max(0, this._sentIndex + 1 - this._firstLine));
 | 
				
			||||||
	var payload = terminal._lines.slice(Math.max(0, waiting.haveIndex + 1 - terminal._firstLine));
 | 
					 | 
				
			||||||
	if (data) {
 | 
						if (data) {
 | 
				
			||||||
		payload.push(data);
 | 
							payload.push(data);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (waiting.haveIndex < terminal._index - 1 || data) {
 | 
						if (this._onOutput && (this._sentIndex < this._index - 1 || data)) {
 | 
				
			||||||
		waiting.resolve({index: terminal._index - 1, lines: payload});
 | 
							this._sentIndex = this._index - 1;
 | 
				
			||||||
 | 
							this._onOutput({lines: payload});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Terminal.prototype.feedWaiting = function(waiting, data) {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Terminal.prototype.print = function() {
 | 
					Terminal.prototype.print = function() {
 | 
				
			||||||
	var data = arguments;
 | 
						var data = arguments;
 | 
				
			||||||
	if (this._selected) {
 | 
						if (this._selected) {
 | 
				
			||||||
@@ -104,8 +106,6 @@ Terminal.prototype.postMessageToIframe = function(name, message) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Terminal.prototype.clear = function() {
 | 
					Terminal.prototype.clear = function() {
 | 
				
			||||||
	//this._lines.length = 0;
 | 
					 | 
				
			||||||
	//this._firstLine = this._index;
 | 
					 | 
				
			||||||
	this.print({action: "clear"});
 | 
						this.print({action: "clear"});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -113,18 +113,6 @@ Terminal.prototype.ping = function() {
 | 
				
			|||||||
	this.dispatch({action: "ping"});
 | 
						this.dispatch({action: "ping"});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Terminal.prototype.getOutput = function(haveIndex) {
 | 
					 | 
				
			||||||
	var terminal = this;
 | 
					 | 
				
			||||||
	terminal._lastRead = new Date();
 | 
					 | 
				
			||||||
	return new Promise(function(resolve) {
 | 
					 | 
				
			||||||
		if (haveIndex < terminal._index - 1) {
 | 
					 | 
				
			||||||
			resolve({index: terminal._index - 1, lines: terminal._lines.slice(Math.max(0, haveIndex + 1 - terminal._firstLine))});
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			terminal._waiting.push({haveIndex: haveIndex, resolve: resolve});
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	});
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Terminal.prototype.setEcho = function(echo) {
 | 
					Terminal.prototype.setEcho = function(echo) {
 | 
				
			||||||
	this._echo = echo;
 | 
						this._echo = echo;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -145,10 +133,7 @@ Terminal.prototype.cork = function() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Terminal.prototype.uncork = function() {
 | 
					Terminal.prototype.uncork = function() {
 | 
				
			||||||
	if (--this._corked == 0) {
 | 
						if (--this._corked == 0) {
 | 
				
			||||||
		for (var i = 0; i < this._waiting.length; i++) {
 | 
							this.dispatch();
 | 
				
			||||||
			this.feedWaiting(this._waiting[i]);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		this._waiting.length = 0;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -162,6 +147,97 @@ function invoke(handlers, argv) {
 | 
				
			|||||||
	return Promise.all(promises);
 | 
						return Promise.all(promises);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function socket(request, response, client) {
 | 
				
			||||||
 | 
						var process;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var options = {};
 | 
				
			||||||
 | 
						var credentials = auth.query(request.headers);
 | 
				
			||||||
 | 
						if (credentials && credentials.session) {
 | 
				
			||||||
 | 
							options.userName = credentials.session.name;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						options.credentials = credentials;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						response.onMessage = function(event) {
 | 
				
			||||||
 | 
							if (event.opCode == 0x1 || event.opCode == 0x2) {
 | 
				
			||||||
 | 
								var message;
 | 
				
			||||||
 | 
								try {
 | 
				
			||||||
 | 
									message = JSON.parse(event.data);
 | 
				
			||||||
 | 
								} catch (error) {
 | 
				
			||||||
 | 
									print("ERROR", error, event.data, event.data.length, event.opCode);
 | 
				
			||||||
 | 
									return;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (message.action == "hello") {
 | 
				
			||||||
 | 
									var packageOwner;
 | 
				
			||||||
 | 
									var packageName;
 | 
				
			||||||
 | 
									var match;
 | 
				
			||||||
 | 
									if (match = /^\/\~([^\/]+)\/([^\/]+)(.*)/.exec(message.path)) {
 | 
				
			||||||
 | 
										packageOwner = match[1];
 | 
				
			||||||
 | 
										packageName = match[2];
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									response.send(JSON.stringify({action: "hello"}), 0x1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									process = getSessionProcess(packageOwner, packageName, makeSessionId(), options);
 | 
				
			||||||
 | 
									process.terminal.readOutput(function(message) {
 | 
				
			||||||
 | 
										response.send(JSON.stringify(message), 0x1);
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									var ping = function() {
 | 
				
			||||||
 | 
										var now = Date.now();
 | 
				
			||||||
 | 
										var again = true;
 | 
				
			||||||
 | 
										if (now - process.lastActive < process.timeout) {
 | 
				
			||||||
 | 
											// Active.
 | 
				
			||||||
 | 
										} else if (process.lastPing > process.lastActive) {
 | 
				
			||||||
 | 
											// We lost them.
 | 
				
			||||||
 | 
											process.task.kill();
 | 
				
			||||||
 | 
											again = false;
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											// Idle.  Ping them.
 | 
				
			||||||
 | 
											response.send("", 0x9);
 | 
				
			||||||
 | 
											process.lastPing = now;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if (again) {
 | 
				
			||||||
 | 
											setTimeout(ping, process.timeout);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (process.timeout > 0) {
 | 
				
			||||||
 | 
										setTimeout(ping, process.timeout);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else if (message.action == "command") {
 | 
				
			||||||
 | 
									var command = message.command;
 | 
				
			||||||
 | 
									var eventName = 'unknown';
 | 
				
			||||||
 | 
									if (typeof command == "string") {
 | 
				
			||||||
 | 
										if (process.terminal._echo) {
 | 
				
			||||||
 | 
											process.terminal.print("> " + command);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										if (process.terminal._readLine) {
 | 
				
			||||||
 | 
											let promise = process.terminal._readLine;
 | 
				
			||||||
 | 
											process.terminal._readLine = null;
 | 
				
			||||||
 | 
											promise[0](command);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										eventName = 'onInput';
 | 
				
			||||||
 | 
									} else if (command.event) {
 | 
				
			||||||
 | 
										eventName = command.event;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return invoke(process.eventHandlers[eventName], [command]).catch(function(error) {
 | 
				
			||||||
 | 
										process.terminal.print(error);
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else if (event.opCode == 0x8) {
 | 
				
			||||||
 | 
								// Close.
 | 
				
			||||||
 | 
								process.task.kill();
 | 
				
			||||||
 | 
								response.send(event.data, 0x8);
 | 
				
			||||||
 | 
							} else if (event.opCode == 0xa) {
 | 
				
			||||||
 | 
								// PONG
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (process) {
 | 
				
			||||||
 | 
								process.lastActive = Date.now();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function handler(request, response, packageOwner, packageName, uri) {
 | 
					function handler(request, response, packageOwner, packageName, uri) {
 | 
				
			||||||
	var found = false;
 | 
						var found = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -226,62 +302,9 @@ function handler(request, response, packageOwner, packageName, uri) {
 | 
				
			|||||||
					response.end("Problem saving: " + packageName);
 | 
										response.end("Problem saving: " + packageName);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else if (uri === "/submit") {
 | 
				
			||||||
			var options = {};
 | 
									var process = getServiceProcess(packageOwner, packageName, "submit");
 | 
				
			||||||
			var credentials = auth.query(request.headers);
 | 
									process.lastActive = Date.now();
 | 
				
			||||||
			if (credentials && credentials.session) {
 | 
					 | 
				
			||||||
				options.userName = credentials.session.name;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			options.credentials = credentials;
 | 
					 | 
				
			||||||
			if (uri == "/submit") {
 | 
					 | 
				
			||||||
				process = getServiceProcess(packageOwner, packageName, "submit");
 | 
					 | 
				
			||||||
			} else if (uri == "/atom") {
 | 
					 | 
				
			||||||
				process = getServiceProcess(packageOwner, packageName, "atom");
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				var sessionId = form.decodeForm(request.query).sessionId;
 | 
					 | 
				
			||||||
				var isNewSession = false;
 | 
					 | 
				
			||||||
				if (!getSessionProcess(packageOwner, packageName, sessionId, {create: false})) {
 | 
					 | 
				
			||||||
					sessionId = makeSessionId();
 | 
					 | 
				
			||||||
					isNewSession = true;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				process = getSessionProcess(packageOwner, packageName, sessionId, options);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			process.lastActive = Date.now();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (uri === "/send") {
 | 
					 | 
				
			||||||
				if (isNewSession) {
 | 
					 | 
				
			||||||
					response.writeHead(403, {"Content-Type": "text/plain; charset=utf-8"});
 | 
					 | 
				
			||||||
					response.end("Too soon.");
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					var command = JSON.parse(request.body);
 | 
					 | 
				
			||||||
					var eventName = 'unknown';
 | 
					 | 
				
			||||||
					if (typeof command == "string") {
 | 
					 | 
				
			||||||
						if (process.terminal._echo) {
 | 
					 | 
				
			||||||
							process.terminal.print("> " + command);
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
						if (process.terminal._readLine) {
 | 
					 | 
				
			||||||
							let promise = process.terminal._readLine;
 | 
					 | 
				
			||||||
							process.terminal._readLine = null;
 | 
					 | 
				
			||||||
							promise[0](command);
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
						eventName = 'onInput';
 | 
					 | 
				
			||||||
					} else if (command.event) {
 | 
					 | 
				
			||||||
						eventName = command.event;
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					return invoke(process.eventHandlers[eventName], [command]).then(function() {
 | 
					 | 
				
			||||||
						response.writeHead(200, {
 | 
					 | 
				
			||||||
							"Content-Type": "text/plain; charset=utf-8",
 | 
					 | 
				
			||||||
							"Content-Length": "0",
 | 
					 | 
				
			||||||
							"Cache-Control": "no-cache, no-store, must-revalidate",
 | 
					 | 
				
			||||||
							"Pragma": "no-cache",
 | 
					 | 
				
			||||||
							"Expires": "0",
 | 
					 | 
				
			||||||
						});
 | 
					 | 
				
			||||||
						response.end("");
 | 
					 | 
				
			||||||
					}).catch(function(error) {
 | 
					 | 
				
			||||||
						process.terminal.print(error);
 | 
					 | 
				
			||||||
					});
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			} else if (uri === "/submit") {
 | 
					 | 
				
			||||||
				return process.ready.then(function() {
 | 
									return process.ready.then(function() {
 | 
				
			||||||
					var payload = form.decodeForm(request.body, form.decodeForm(request.query));
 | 
										var payload = form.decodeForm(request.body, form.decodeForm(request.query));
 | 
				
			||||||
					return invoke(process.eventHandlers['onSubmit'], [payload]).then(function() {
 | 
										return invoke(process.eventHandlers['onSubmit'], [payload]).then(function() {
 | 
				
			||||||
@@ -295,63 +318,26 @@ function handler(request, response, packageOwner, packageName, uri) {
 | 
				
			|||||||
						return response.end("");
 | 
											return response.end("");
 | 
				
			||||||
					});
 | 
										});
 | 
				
			||||||
				});
 | 
									});
 | 
				
			||||||
			} else if (uri === "/atom") {
 | 
							} else if (uri === "/atom") {
 | 
				
			||||||
				return process.ready.then(function() {
 | 
								var process = getServiceProcess(packageOwner, packageName, "atom");
 | 
				
			||||||
					var payload = form.decodeForm(request.body, form.decodeForm(request.query));
 | 
								process.lastActive = Date.now();
 | 
				
			||||||
					return invoke(process.eventHandlers['onAtom'], [payload]).then(function(content) {
 | 
								return process.ready.then(function() {
 | 
				
			||||||
						var atomContent = content.join();
 | 
									var payload = form.decodeForm(request.body, form.decodeForm(request.query));
 | 
				
			||||||
						response.writeHead(200, {
 | 
									return invoke(process.eventHandlers['onAtom'], [payload]).then(function(content) {
 | 
				
			||||||
							"Content-Type": "application/atom+xml; charset=utf-8",
 | 
										var atomContent = content.join();
 | 
				
			||||||
							"Content-Length": atomContent.length.toString(),
 | 
					 | 
				
			||||||
							"Cache-Control": "no-cache, no-store, must-revalidate",
 | 
					 | 
				
			||||||
							"Pragma": "no-cache",
 | 
					 | 
				
			||||||
							"Expires": "0",
 | 
					 | 
				
			||||||
						});
 | 
					 | 
				
			||||||
						return response.end(atomContent);
 | 
					 | 
				
			||||||
					});
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
			} else if (uri === "/receive") {
 | 
					 | 
				
			||||||
				if (isNewSession) {
 | 
					 | 
				
			||||||
					var data = JSON.stringify({
 | 
					 | 
				
			||||||
						lines: [
 | 
					 | 
				
			||||||
							{
 | 
					 | 
				
			||||||
								action: "session",
 | 
					 | 
				
			||||||
								session: {
 | 
					 | 
				
			||||||
									sessionId: sessionId,
 | 
					 | 
				
			||||||
									credentials: credentials,
 | 
					 | 
				
			||||||
								}
 | 
					 | 
				
			||||||
							},
 | 
					 | 
				
			||||||
						]
 | 
					 | 
				
			||||||
					});
 | 
					 | 
				
			||||||
					response.writeHead(200, {
 | 
										response.writeHead(200, {
 | 
				
			||||||
						"Content-Type": "text/plain; charset=utf-8",
 | 
											"Content-Type": "application/atom+xml; charset=utf-8",
 | 
				
			||||||
						"Content-Length": data.length.toString(),
 | 
											"Content-Length": atomContent.length.toString(),
 | 
				
			||||||
						"Cache-Control": "no-cache, no-store, must-revalidate",
 | 
											"Cache-Control": "no-cache, no-store, must-revalidate",
 | 
				
			||||||
						"Pragma": "no-cache",
 | 
											"Pragma": "no-cache",
 | 
				
			||||||
						"Expires": "0",
 | 
											"Expires": "0",
 | 
				
			||||||
					});
 | 
										});
 | 
				
			||||||
					process.ready.then(function() {
 | 
										return response.end(atomContent);
 | 
				
			||||||
						process.terminal.print({action: "ready", ready: true});
 | 
									});
 | 
				
			||||||
					}).catch(function(error) {
 | 
								});
 | 
				
			||||||
						process.terminal.print({action: "ready", error: error});
 | 
					 | 
				
			||||||
					});
 | 
					 | 
				
			||||||
					response.end(data);
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					return process.terminal.getOutput(parseInt(request.body)).then(function(output) {
 | 
					 | 
				
			||||||
						var data = JSON.stringify(output);
 | 
					 | 
				
			||||||
						response.writeHead(200, {
 | 
					 | 
				
			||||||
							"Content-Type": "text/plain; charset=utf-8",
 | 
					 | 
				
			||||||
							"Content-Length": data.length.toString(),
 | 
					 | 
				
			||||||
							"Cache-Control": "no-cache, no-store, must-revalidate",
 | 
					 | 
				
			||||||
							"Pragma": "no-cache",
 | 
					 | 
				
			||||||
							"Expires": "0",
 | 
					 | 
				
			||||||
						});
 | 
					 | 
				
			||||||
						response.end(data);
 | 
					 | 
				
			||||||
					});
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exports.handler = handler;
 | 
					exports.handler = handler;
 | 
				
			||||||
 | 
					exports.socket = socket;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user