diff --git a/core/client.js b/core/client.js index 48adb918..5e2f66e7 100644 --- a/core/client.js +++ b/core/client.js @@ -108,83 +108,58 @@ function split(container, children) { } } -function receive() { - $.ajax({ - url: url() + "/receive?sessionId=" + gSessionId, - method: "POST", - data: gHaveIndex.toString(), - dataType: "json", - }).then(function(data) { - for (var i in data.lines) { - var line = data.lines[i]; +function receive(data) { + for (var i in data.lines) { + var line = data.lines[i]; - var target = document.getElementsByClassName("terminal")[0].id; - if (line && line.terminal) { - if (document.getElementById("terminal_" + line.terminal)) { - target = "terminal_" + line.terminal; - } - line = line.value; + var target = document.getElementsByClassName("terminal")[0].id; + if (line && line.terminal) { + if (document.getElementById("terminal_" + line.terminal)) { + target = "terminal_" + line.terminal; } - if (line && line.action == "ping") { - // PONG - } else if (line && line.action == "session") { - gSessionId = line.session.sessionId; - gCredentials = line.session.credentials; - updateLogin(); - } else if (line && line[0] && line[0].action == "ready") { - if (window.location.hash) { - send({event: "hashChange", hash: window.location.hash}); - } - } else if (line && line[0] && line[0].action == "notify") { - new Notification(line[0].title, line[0].options); - } else if (line && line[0] && line[0].action == "title") { - window.document.title = line[0].value; - } else if (line && line[0] && line[0].action == "prompt") { - var prompt = document.getElementById("prompt"); - while (prompt.firstChild) { - prompt.removeChild(prompt.firstChild); - } - prompt.appendChild(document.createTextNode(line[0].value)); - } else if (line && line[0] && line[0].action == "password") { - var prompt = document.getElementById("input"); - prompt.setAttribute("type", line[0].value ? "password" : "text"); - } else if (line && line[0] && line[0].action == "hash") { - window.location.hash = line[0].value; - } else if (line && line[0] && line[0].action == "update") { - document.getElementById("update").setAttribute("Style", "display: inline"); - } else if (line && line[0] && line[0].action == "split") { - split(document.getElementById("terminals"), line[0].options); - } else if (line && line[0] && line[0].action == "postMessageToIframe") { - var iframe = document.getElementById("iframe_" + line[0].name); - if (iframe) { - iframe.contentWindow.postMessage(line[0].message, "*"); - } - } else { - print(document.getElementById(target), line); + line = line.value; + } + if (line && line.action == "ping") { + gSocket.send(JSON.stringify({action: "pong"})); + } else if (line && line.action == "session") { + gSessionId = line.session.sessionId; + gCredentials = line.session.credentials; + updateLogin(); + } else if (line && line[0] && line[0].action == "ready") { + if (window.location.hash) { + send({event: "hashChange", hash: window.location.hash}); + } + } else if (line && line[0] && line[0].action == "notify") { + new Notification(line[0].title, line[0].options); + } else if (line && line[0] && line[0].action == "title") { + window.document.title = line[0].value; + } else if (line && line[0] && line[0].action == "prompt") { + var prompt = document.getElementById("prompt"); + while (prompt.firstChild) { + prompt.removeChild(prompt.firstChild); + } + prompt.appendChild(document.createTextNode(line[0].value)); + } else if (line && line[0] && line[0].action == "password") { + var prompt = document.getElementById("input"); + prompt.setAttribute("type", line[0].value ? "password" : "text"); + } else if (line && line[0] && line[0].action == "hash") { + window.location.hash = line[0].value; + } else if (line && line[0] && line[0].action == "update") { + document.getElementById("update").setAttribute("Style", "display: inline"); + } else if (line && line[0] && line[0].action == "split") { + split(document.getElementById("terminals"), line[0].options); + } else if (line && line[0] && line[0].action == "postMessageToIframe") { + var iframe = document.getElementById("iframe_" + line[0].name); + if (iframe) { + iframe.contentWindow.postMessage(line[0].message, "*"); } - } - 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 { - setTimeout(receive, 60 * 1000); + print(document.getElementById(target), line); } - gErrorCount++; - }); + } + if ("index" in data) { + gHaveIndex = data.index; + } } function autoNewLine(terminal) { @@ -282,19 +257,16 @@ function send(command) { value = prefix + $("#input").val(); $("#input").val(""); } - $.ajax({ - url: url() + "/send?sessionId=" + gSessionId, - method: "POST", - data: JSON.stringify(value), - dataType: "text", - }).fail(function(xhr, status, error) { + try { + gSocket.send(JSON.stringify({action: "command", command: value})); + } catch (error) { var node = document.getElementById("status"); while (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"); - }); + } } function updateLogin() { @@ -437,6 +409,8 @@ function onMessage(event) { send({event: "onWindowMessage", message: event.data}); } +var gSocket; + $(document).ready(function() { if (Notification) { Notification.requestPermission(); @@ -448,10 +422,18 @@ $(document).ready(function() { window.addEventListener("blur", blur); window.addEventListener("message", onMessage, false); enableDragDrop(); -}); -$(window).load(function() { - setTimeout(function() { - receive(); - }, 0); + gSocket = new WebSocket("ws://" + + window.location.hostname + + (window.location.port.length ? ":" + window.location.port : "") + + "/terminal/socket"); + gSocket.onopen = function() { + gSocket.send(JSON.stringify({ + action: "hello", + path: window.location.pathname, + })); + } + gSocket.onmessage = function(event) { + receive(JSON.parse(event.data)); + } }); diff --git a/core/core.js b/core/core.js index d42486f7..ffc0426b 100644 --- a/core/core.js +++ b/core/core.js @@ -146,27 +146,6 @@ function getUsers(packageOwner, packageName) { 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) { return invoke(to.eventHandlers['onMessage'], [getUser(from, from), message]); } @@ -273,9 +252,6 @@ function getProcess(packageOwner, packageName, key, options) { process.connections.length = 0; delete gProcesses[key]; }; - if (process.timeout > 0) { - setTimeout(ping.bind(process), process.timeout); - } var imports = { 'core': { 'broadcast': broadcast.bind(process), @@ -352,8 +328,6 @@ function getProcess(packageOwner, packageName, key, options) { } } 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.setImports(imports); @@ -439,3 +413,4 @@ httpd.all("", function(request, response) { return response.end(data); } }); +httpd.registerSocketHandler("/terminal/socket", terminal.socket); diff --git a/core/httpd.js b/core/httpd.js index e141e18b..b9c9c825 100644 --- a/core/httpd.js +++ b/core/httpd.js @@ -1,4 +1,7 @@ +"use strict"; + var gHandlers = []; +var gSocketHandlers = []; function logError(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) { this.method = method; var index = uri.indexOf("?"); @@ -56,8 +67,21 @@ function findHandler(request) { 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) { var kStatusText = { + 101: "Switching Protocols", 200: 'OK', 303: 'See other', 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) { var inputBuffer = ""; var request; @@ -207,6 +361,12 @@ function handleConnection(client) { lineByLine = false; body = ""; 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 { finish(); return false; @@ -291,3 +451,4 @@ if (privateKey && certificate) { } exports.all = all; +exports.registerSocketHandler = registerSocketHandler; diff --git a/core/sha1.js b/core/sha1.js new file mode 100644 index 00000000..995b218b --- /dev/null +++ b/core/sha1.js @@ -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>> 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>>(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; diff --git a/core/terminal.js b/core/terminal.js index 7e0c2a65..7a86c735 100644 --- a/core/terminal.js +++ b/core/terminal.js @@ -14,9 +14,9 @@ var auth = require('auth'); var form = require('form'); function Terminal() { - this._waiting = []; this._index = 0; this._firstLine = 0; + this._sentIndex = -1; this._lines = []; this._lastRead = null; this._lastWrite = null; @@ -24,29 +24,31 @@ function Terminal() { this._readLine = null; this._selected = null; this._corked = 0; + this._onOutput = null; return this; } Terminal.kBacklog = 64; -Terminal.prototype.dispatch = function(data) { - for (var i in this._waiting) { - this.feedWaiting(this._waiting[i], data); - } - this._waiting.length = 0; +Terminal.prototype.readOutput = function(callback) { + this._onOutput = callback; + this.dispatch(); } -Terminal.prototype.feedWaiting = function(waiting, data) { - var terminal = this; - var payload = terminal._lines.slice(Math.max(0, waiting.haveIndex + 1 - terminal._firstLine)); +Terminal.prototype.dispatch = function(data) { + var payload = this._lines.slice(Math.max(0, this._sentIndex + 1 - this._firstLine)); if (data) { payload.push(data); } - if (waiting.haveIndex < terminal._index - 1 || data) { - waiting.resolve({index: terminal._index - 1, lines: payload}); + if (this._onOutput && (this._sentIndex < this._index - 1 || data)) { + this._sentIndex = this._index - 1; + this._onOutput({lines: payload}); } } +Terminal.prototype.feedWaiting = function(waiting, data) { +} + Terminal.prototype.print = function() { var data = arguments; if (this._selected) { @@ -104,8 +106,6 @@ Terminal.prototype.postMessageToIframe = function(name, message) { } Terminal.prototype.clear = function() { - //this._lines.length = 0; - //this._firstLine = this._index; this.print({action: "clear"}); } @@ -113,18 +113,6 @@ Terminal.prototype.ping = function() { 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) { this._echo = echo; } @@ -145,10 +133,7 @@ Terminal.prototype.cork = function() { Terminal.prototype.uncork = function() { if (--this._corked == 0) { - for (var i = 0; i < this._waiting.length; i++) { - this.feedWaiting(this._waiting[i]); - } - this._waiting.length = 0; + this.dispatch(); } } @@ -162,6 +147,97 @@ function invoke(handlers, argv) { 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) { var found = false; @@ -226,62 +302,9 @@ function handler(request, response, packageOwner, packageName, uri) { response.end("Problem saving: " + packageName); } } - } else { - var options = {}; - var credentials = auth.query(request.headers); - 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") { + } else if (uri === "/submit") { + var process = getServiceProcess(packageOwner, packageName, "submit"); + process.lastActive = Date.now(); return process.ready.then(function() { var payload = form.decodeForm(request.body, form.decodeForm(request.query)); return invoke(process.eventHandlers['onSubmit'], [payload]).then(function() { @@ -295,63 +318,26 @@ function handler(request, response, packageOwner, packageName, uri) { return response.end(""); }); }); - } else if (uri === "/atom") { - return process.ready.then(function() { - var payload = form.decodeForm(request.body, form.decodeForm(request.query)); - return invoke(process.eventHandlers['onAtom'], [payload]).then(function(content) { - var atomContent = content.join(); - response.writeHead(200, { - "Content-Type": "application/atom+xml; charset=utf-8", - "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, - } - }, - ] - }); + } else if (uri === "/atom") { + var process = getServiceProcess(packageOwner, packageName, "atom"); + process.lastActive = Date.now(); + return process.ready.then(function() { + var payload = form.decodeForm(request.body, form.decodeForm(request.query)); + return invoke(process.eventHandlers['onAtom'], [payload]).then(function(content) { + var atomContent = content.join(); response.writeHead(200, { - "Content-Type": "text/plain; charset=utf-8", - "Content-Length": data.length.toString(), + "Content-Type": "application/atom+xml; charset=utf-8", + "Content-Length": atomContent.length.toString(), "Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": "0", }); - process.ready.then(function() { - 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); - }); - } - } + return response.end(atomContent); + }); + }); } } } exports.handler = handler; +exports.socket = socket;