
Previously, the filename was URL-encoded, thus each / was replaced with %2F. This caused issues with some Apache2 configuration, smartly mingling with the URL to either encode it a second time (resulting in %252F) or decoding it (generating a real /). However, for authenticated file downloads with the JWT, we hardly require a byte-by-byte matching. With these changes, the URL parameter is no longer URL-encoded, so that Apache2 won't break our implementation any longer. Further, we use this opportunity to get rid of the unnecessary .json extension for those filename routes, simplifying the routes generated and doing some further cleanup.
87 lines
3.7 KiB
JavaScript
87 lines
3.7 KiB
JavaScript
CodeOceanEditorWebsocket = {
|
|
websocket: null,
|
|
// Replace `http` with `ws` for the WebSocket connection. This also works with `https` and `wss`.
|
|
webSocketProtocol: window.location.protocol.replace(/^http/, 'ws').split(':')[0],
|
|
|
|
initializeSocket: function(urlHelper, params, closeCallback) {
|
|
// 1. Specify the protocol for all URLs to generate
|
|
params.protocol = this.webSocketProtocol;
|
|
params._options = true;
|
|
|
|
// 2. Create a new Sentry transaction.
|
|
// Since we want to group similar URLs, we use the URL without the ID and filename as the description.
|
|
const cleanedUrl = urlHelper({
|
|
...params,
|
|
...(params.id && {id: '*'}), // Overwrite the ID with a wildcard only if it is present.
|
|
...(params.filename && {filename: '*'}), // Overwrite the filename with a wildcard only if it is present.
|
|
});
|
|
const sentryDescription = `WebSocket ${cleanedUrl}`;
|
|
const span = this.sentryTransaction?.startChild({op: 'websocket.client', description: sentryDescription, data: {...params}})
|
|
|
|
// 3. Create the actual WebSocket URL.
|
|
// This URL might contain Sentry Tracing headers to propagate the Sentry transaction.
|
|
if (span) {
|
|
const dynamicContext = this.sentryTransaction.getDynamicSamplingContext();
|
|
const baggage = SentryUtils.dynamicSamplingContextToSentryBaggageHeader(dynamicContext);
|
|
if (baggage) {
|
|
params.HTTP_SENTRY_TRACE = span.toTraceparent();
|
|
params.HTTP_BAGGAGE = baggage;
|
|
}
|
|
}
|
|
const url = urlHelper({...params});
|
|
|
|
// 4. Connect to the given URL.
|
|
this.websocket = new CommandSocket(url,
|
|
function (evt) {
|
|
this.resetOutputTab();
|
|
}.bind(this)
|
|
);
|
|
CodeOceanEditorWebsocket.websocket = this.websocket;
|
|
this.websocket.onError(this.showWebsocketError.bind(this));
|
|
this.websocket.onClose(function(span, callback){
|
|
span?.finish();
|
|
if(callback != null){
|
|
callback();
|
|
}
|
|
}.bind(this, span, closeCallback));
|
|
},
|
|
|
|
initializeSocketForTesting: function(submissionID, filename) {
|
|
this.initializeSocket(Routes.test_submission_url, {id: submissionID, filename: filename});
|
|
this.websocket.on('default',this.handleTestResponse.bind(this));
|
|
this.websocket.on('exit', this.handleExitCommand.bind(this));
|
|
},
|
|
|
|
initializeSocketForScoring: function(submissionID) {
|
|
this.initializeSocket(Routes.score_submission_url, {id: submissionID}, function() {
|
|
$('#assess').one('click', this.scoreCode.bind(this))
|
|
}.bind(this));
|
|
this.websocket.on('default',this.handleScoringResponse.bind(this));
|
|
this.websocket.on('hint', this.showHint.bind(this));
|
|
this.websocket.on('exit', this.handleExitCommand.bind(this));
|
|
this.websocket.on('status', this.showStatus.bind(this));
|
|
},
|
|
|
|
initializeSocketForRunning: function(submissionID, filename) {
|
|
this.initializeSocket(Routes.run_submission_url, {id: submissionID, filename: filename});
|
|
this.websocket.on('input',this.showPrompt.bind(this));
|
|
this.websocket.on('write', this.printWebsocketOutput.bind(this));
|
|
this.websocket.on('clear', this.clearOutput.bind(this));
|
|
this.websocket.on('turtle', this.handleTurtleCommand.bind(this));
|
|
this.websocket.on('turtlebatch', this.handleTurtlebatchCommand.bind(this));
|
|
this.websocket.on('render', this.printWebsocketOutput.bind(this));
|
|
this.websocket.on('exit', this.handleExitCommand.bind(this));
|
|
this.websocket.on('status', this.showStatus.bind(this));
|
|
this.websocket.on('hint', this.showHint.bind(this));
|
|
this.websocket.on('files', this.prepareFileDownloads.bind(this));
|
|
},
|
|
|
|
handleExitCommand: function() {
|
|
this.killWebsocket();
|
|
this.handleStderrOutputForFlowr();
|
|
this.augmentStacktraceInOutput();
|
|
this.cleanUpTurtle();
|
|
this.cleanUpUI();
|
|
}
|
|
};
|