Use ws directly to handle new exec ws route
This commit is contained in:
+22
-18
@@ -461,7 +461,7 @@ function exec(req, res, next) {
|
||||
});
|
||||
}
|
||||
|
||||
function execWebSocket(ws, req, next) {
|
||||
function execWebSocket(req, res, next) {
|
||||
assert.strictEqual(typeof req.params.id, 'string');
|
||||
|
||||
debug('Execing websocket into app id:%s and cmd:%s', req.params.id, req.query.cmd);
|
||||
@@ -487,24 +487,28 @@ function execWebSocket(ws, req, next) {
|
||||
|
||||
console.log('Connected to terminal');
|
||||
|
||||
duplexStream.on('end', function () { ws.close(); });
|
||||
duplexStream.on('close', function () { ws.close(); });
|
||||
duplexStream.on('error', function (error) {
|
||||
console.error('duplexStream error:', error);
|
||||
});
|
||||
duplexStream.on('data', function (data) {
|
||||
if (ws.readyState !== WebSocket.OPEN) return;
|
||||
ws.send(data.toString());
|
||||
});
|
||||
req.clearTimeout();
|
||||
|
||||
ws.on('error', function (error) {
|
||||
console.error('websocket error:', error);
|
||||
});
|
||||
ws.on('message', function (msg) {
|
||||
duplexStream.write(msg);
|
||||
});
|
||||
ws.on('close', function () {
|
||||
// Clean things up, if any?
|
||||
res.handleUpgrade(function (ws) {
|
||||
duplexStream.on('end', function () { ws.close(); });
|
||||
duplexStream.on('close', function () { ws.close(); });
|
||||
duplexStream.on('error', function (error) {
|
||||
console.error('duplexStream error:', error);
|
||||
});
|
||||
duplexStream.on('data', function (data) {
|
||||
if (ws.readyState !== WebSocket.OPEN) return;
|
||||
ws.send(data.toString());
|
||||
});
|
||||
|
||||
ws.on('error', function (error) {
|
||||
console.error('websocket error:', error);
|
||||
});
|
||||
ws.on('message', function (msg) {
|
||||
duplexStream.write(msg);
|
||||
});
|
||||
ws.on('close', function () {
|
||||
// Clean things up, if any?
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -534,7 +534,9 @@ function scope(requestedScope) {
|
||||
];
|
||||
}
|
||||
|
||||
function websocketAuth(ws, req, next) {
|
||||
function websocketAuth(requestedScopes, req, res, next) {
|
||||
assert(Array.isArray(requestedScopes));
|
||||
|
||||
if (typeof req.query.access_token !== 'string') return next(new HttpError(401, 'Unauthorized'));
|
||||
|
||||
auth.accessTokenAuth(req.query.access_token, function (error, user, info) {
|
||||
@@ -544,6 +546,9 @@ function websocketAuth(ws, req, next) {
|
||||
req.user = user;
|
||||
req.authInfo = info;
|
||||
|
||||
var error = validateRequestedScopes(req, requestedScopes);
|
||||
if (error) return next(new HttpError(401, error.message));
|
||||
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
+16
-13
@@ -19,7 +19,8 @@ var assert = require('assert'),
|
||||
middleware = require('./middleware'),
|
||||
passport = require('passport'),
|
||||
path = require('path'),
|
||||
routes = require('./routes/index.js');
|
||||
routes = require('./routes/index.js'),
|
||||
ws = require('ws');
|
||||
|
||||
var gHttpServer = null;
|
||||
var gSysadminHttpServer = null;
|
||||
@@ -28,8 +29,7 @@ function initializeExpressSync() {
|
||||
var app = express();
|
||||
var httpServer = http.createServer(app);
|
||||
|
||||
// enabled websocket handling
|
||||
require('express-ws')(app, httpServer);
|
||||
const wsServer = new ws.Server({ noServer: true }); // in noServer mode, we have to handle 'upgrade' and call handleUpgrade
|
||||
|
||||
var QUERY_LIMIT = '1mb', // max size for json and urlencoded queries (see also client_max_body_size in nginx)
|
||||
FIELD_LIMIT = 2 * 1024 * 1024; // max fields that can appear in multipart
|
||||
@@ -192,7 +192,8 @@ function initializeExpressSync() {
|
||||
router.get ('/api/v1/apps/:id/logstream', appsScope, routes.user.requireAdmin, routes.apps.getLogStream);
|
||||
router.get ('/api/v1/apps/:id/logs', appsScope, routes.user.requireAdmin, routes.apps.getLogs);
|
||||
router.get ('/api/v1/apps/:id/exec', routes.developer.enabled, appsScope, routes.user.requireAdmin, routes.apps.exec);
|
||||
router.ws ('/api/v1/apps/:id/execws', routes.oauth2.websocketAuth, routes.apps.execWebSocket);
|
||||
// websocket cannot do bearer authentication
|
||||
router.get ('/api/v1/apps/:id/execws', routes.oauth2.websocketAuth.bind(null, [ clients.SCOPE_APPS ]), routes.user.requireAdmin, routes.apps.execWebSocket);
|
||||
router.post('/api/v1/apps/:id/clone', appsScope, routes.user.requireAdmin, routes.apps.cloneApp);
|
||||
|
||||
// settings routes (these are for the settings tab - avatar & name have public routes for normal users. see above)
|
||||
@@ -238,13 +239,15 @@ function initializeExpressSync() {
|
||||
|
||||
// upgrade handler
|
||||
httpServer.on('upgrade', function (req, socket, head) {
|
||||
// create a node response object for express
|
||||
var res = new http.ServerResponse({});
|
||||
res.assignSocket(socket);
|
||||
|
||||
if (req.headers.upgrade === 'websocket') {
|
||||
// websocket connections should never time out
|
||||
req.clearTimeout();
|
||||
res.handleUpgrade = function (callback) {
|
||||
wsServer.handleUpgrade(req, socket, head, callback);
|
||||
};
|
||||
} else {
|
||||
// create a node response object for express
|
||||
var res = new http.ServerResponse({});
|
||||
res.assignSocket(socket);
|
||||
res.sendUpgradeHandshake = function () { // could extend express.response as well
|
||||
console.log('----- now send the upgrade handshake')
|
||||
socket.write('HTTP/1.1 101 TCP Handshake\r\n' +
|
||||
@@ -252,11 +255,11 @@ function initializeExpressSync() {
|
||||
'Connection: Upgrade\r\n' +
|
||||
'\r\n');
|
||||
};
|
||||
|
||||
// route through express middleware. if we provide no callback, express will provide a 'finalhandler'
|
||||
// TODO: it's not clear if socket needs to be destroyed
|
||||
app(req, res);
|
||||
}
|
||||
|
||||
// route through express middleware. if we provide no callback, express will provide a 'finalhandler'
|
||||
// TODO: it's not clear if socket needs to be destroyed
|
||||
app(req, res);
|
||||
});
|
||||
|
||||
return httpServer;
|
||||
|
||||
Reference in New Issue
Block a user