summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin <martin@leo.fri1.uni-lj.si>2015-09-15 12:14:30 +0200
committerMartin <martin@leo.fri1.uni-lj.si>2015-09-15 12:14:30 +0200
commit9df933ebc3ade50628a26af691693254ed1daa22 (patch)
tree26fa4689f581eda562fcfd881ea3c9cca7b4ecc5
parentb2b4e40e5333b3182d0a57052640e8a7ecae8619 (diff)
parentcc3a807157d6d2d2c4830afcec90d6614e486b70 (diff)
Merge branch 'master' of ssh://212.235.189.51:22122/codeq-server
-rw-r--r--client/__init__.py37
-rw-r--r--readme.md34
-rw-r--r--server/__init__.py41
-rw-r--r--server/socket.py6
-rw-r--r--web/main.js2
-rw-r--r--wsgi_server.py252
6 files changed, 32 insertions, 340 deletions
diff --git a/client/__init__.py b/client/__init__.py
deleted file mode 100644
index 1c5d693..0000000
--- a/client/__init__.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# coding=utf-8
-
-# TODO: this module is deprecated, remove it
-
-import multiprocessing.managers
-
-__all__ = ['get_session_by_id', 'get_or_create_session']
-
-class CodeqManager(multiprocessing.managers.BaseManager):
- pass
-
-CodeqManager.register('PrologSession')
-CodeqManager.register('PythonSession')
-CodeqManager.register('UserSession')
-#CodeqManager.register('get_session_by_id')
-#CodeqManager.register('get_or_create_session')
-#CodeqManager.register('authenticate_and_create_session')
-CodeqManager.register('Codeq')
-
-m = CodeqManager(address=('localhost', 16231), authkey=b'c0d3q3y')
-m.connect()
-codeq = m.Codeq()
-
-def get_session_by_id(sid):
-# return m.get_session_by_id(sid)
- return codeq.get_session_by_id(sid)
-
-def get_or_create_session(uid, username, sid=None):
-# return m.get_or_create_session(uid, username, sid)
- return codeq.get_or_create_session(uid, username, sid)
-
-def authenticate_and_create_session(username, password):
-# return m.authenticate_and_create_session(username, password)
- return codeq.authenticate_and_create_session(username, password)
-
-def list_problems():
- return codeq.list_problems()
diff --git a/readme.md b/readme.md
index f52a663..a7e11ca 100644
--- a/readme.md
+++ b/readme.md
@@ -1,21 +1,41 @@
Deployment
==========
+To use the correct SWI prolog package (>= 7.2) on Debian, add a custom
+repository by creating the file /etc/apt/sources.list.d/prolog.list
+containing the following 2 lines:
+
+deb http://ppa.launchpad.net/swi-prolog/stable/ubuntu trusty main
+deb-src http://ppa.launchpad.net/swi-prolog/stable/ubuntu trusty main
+
+After that run the following sequence of shell statements to update
+the package cache, register the new repository's key, and again refresh
+the package cache using the additional key:
+
+apt-get update
+apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys EF8406856DBFCA18
+apt-get update
+
Install the following packages:
- apache2
- - python3-falcon
+ - python3 (>= 3.4)
- python3-ply
- python3-psycopg2
- python3-termcolor
- - python3-waitress
- - swi-prolog-nox
+ - swi-prolog-nox (>= 7.2)
- nodejs (>= 10.0.22)
Settings:
- point webroot to codeq-web
- set up reverse proxy for /ws/ to the node server:
+ - using the command a2enmod enable apache modules: proxy, proxy_http, proxy_wstunnel, rewrite
+ - add to the apache configuration the following directives:
+ RewriteEngine on
+ RewriteCond %{REQUEST_URI} ^/ws/ [NC]
+ RewriteCond %{QUERY_STRING} transport=websocket [NC]
+ RewriteRule /(.*) ws://localhost:8083/$1 [P,L]
ProxyPass /ws/ http://localhost:8083/ws/
ProxyPassReverse /ws/ http://localhost:8083/ws/
- set _path_prefix in server.problems
@@ -25,10 +45,10 @@ Settings:
Running:
- - run prolog/runner/main.pl
- - run python/runner/main.py
- - run daemon.py
- - start the node process (node web/main.js)
+ - run the prolog interpreter server: swipl prolog/runner/main.pl
+ - run the python interpreter server: python3 python/runner/main.py
+ - run the session daemon: python3 daemon.py
+ - run the web server for client communication: node web/main.js
Misc.
=====
diff --git a/server/__init__.py b/server/__init__.py
index 861a7df..0007e0a 100644
--- a/server/__init__.py
+++ b/server/__init__.py
@@ -1,6 +1,5 @@
# coding=utf-8
-import multiprocessing.managers
from . import user_session
from . import prolog_session
from . import python_session
@@ -8,43 +7,3 @@ from . import socket
import server.problems
__all__ = ['socket', 'handlers', 'user_session', 'prolog_session', 'python_session', 'problems', 'start']
-
-
-# TODO: everything below is deprecated, remove it
-
-class Codeq(object):
- _method_to_typeid_ = {
- 'get_session_by_id': 'UserSession',
- 'get_or_create_session': 'UserSession',
- 'authenticate_and_create_session': 'UserSession'
- }
-
- def get_session_by_id(self, sid):
- return user_session.get_session_by_id(sid)
-
- def get_or_create_session(self, uid, username, sid=None):
- return user_session.get_or_create_session(uid, username, sid)
-
- def authenticate_and_create_session(self, username, password):
- return user_session.authenticate_and_create_session(username, password)
-
- def list_problems(self):
- return server.problems.list_problems()
-
-class CodeqManager(multiprocessing.managers.BaseManager):
- pass
-
-class UserSessionProxy(multiprocessing.managers.BaseProxy):
- _method_to_typeid_ = {'get_prolog': 'PrologSession', 'get_python': 'PythonSession'}
-
-CodeqManager.register('PrologSession')
-CodeqManager.register('PythonSession')
-CodeqManager.register('UserSession', proxytype=UserSessionProxy)
-#CodeqManager.register('get_session_by_id', callable=user_session.get_session_by_id, proxytype=UserSessionProxy)
-#CodeqManager.register('get_or_create_session', callable=user_session.get_or_create_session, proxytype=UserSessionProxy)
-#CodeqManager.register('authenticate_and_create_session', callable=user_session.authenticate_and_create_session, proxytype=UserSessionProxy)
-CodeqManager.register('Codeq', callable=Codeq, method_to_typeid=Codeq._method_to_typeid_)
-
-def start():
- m = CodeqManager(address=('localhost', 16231), authkey=b'c0d3q3y')
- m.get_server().serve_forever()
diff --git a/server/socket.py b/server/socket.py
index c248056..b184d4b 100644
--- a/server/socket.py
+++ b/server/socket.py
@@ -20,8 +20,10 @@ _transactions_to_socket = {} # keyed by tid, used only when there is no sid ava
def processIncomingPacket(receiving_socket, packet):
print('Decocoding JSON: {}'.format(packet))
obj = json.loads(packet)
- if obj.get('type') == 'connect':
+ req_type = obj.get('type') # private (meta) requests have the 'type'
+ if req_type == 'connect':
return # TODO: first packet is 'connect', providing the list of connected sessions to the peer
+
tid = obj.get('tid') # transaction ID
if tid is None:
raise Exception('Transaction ID is missing from the request')
@@ -270,7 +272,7 @@ class Communication(SocketHandler):
lock.release()
for handler in handlers:
handler.destroy() # destroy them all, even the server socket
- # this is where everything is destroyed but us, so what follows is self-destruct
+ # this is where everything has been destroyed but us, so what follows is self-destruct
lock.acquire()
try:
self._destroying = True
diff --git a/web/main.js b/web/main.js
index d2b84af..7f1b2e4 100644
--- a/web/main.js
+++ b/web/main.js
@@ -195,7 +195,7 @@ server.on('connection', function (socket) {
};
socket.on('close', function (reason, description) { // description is optional
- logger.debug('GUI socket closed');
+ logger.debug('GUI socket closed: ' + reason);
if (session.sid !== null) {
if (sessions[session.sid] === session) delete sessions[session.sid];
sendDataToPython({'type': 'unregister', 'sid': session.sid});
diff --git a/wsgi_server.py b/wsgi_server.py
deleted file mode 100644
index f754b14..0000000
--- a/wsgi_server.py
+++ /dev/null
@@ -1,252 +0,0 @@
-#!/usr/bin/python3
-# coding=utf-8
-
-# TODO: this module is deprecated, remove it
-# TODO: all new development should occur in server/handlers.py instead
-
-import falcon
-import json
-import client
-from errors.session import NoSuchSession, AuthenticationFailed
-
-MAX_REQUEST_LENGTH = 1024 * 1024 # 1 MB
-
-api = application = falcon.API()
-
-
-def get_json_payload(req, session_is_optional=False):
- content_type_parts = req.content_type.split(';')
- content_type = content_type_parts[0].strip()
- encoding = 'utf-8'
- if len(content_type_parts) > 1:
- for part in content_type_parts[1:]:
- subparts = part.strip().split('=')
- if (len(subparts) == 2) and subparts[0] == 'charset':
- encoding = subparts[1]
- if not ((content_type == 'application/json') or (content_type == 'text/json')):
- raise falcon.HTTPUnsupportedMediaType('Unsupported content-type: {0}'.format(req.content_type))
- length = req.get_header('Content-Length')
- if length is None:
- raise falcon.HTTPLengthRequired('Requests without content-length are not accepted')
- try:
- l = int(length)
- except:
- raise falcon.HTTPBadRequest('Invalid Content-Length', 'The given Content-Length is not a number: {0}'.format(length))
- if l > MAX_REQUEST_LENGTH:
- raise falcon.HTTPError(falcon.HTTP_413, 'Request Entity Too Large', 'Cannot accept the request of length {0}: maximum allowed content-length is {1}'.format(length, MAX_REQUEST_LENGTH))
- try:
- txt = req.stream.read()
- except Exception as e:
- raise falcon.HTTPBadRequest('Error reading request', 'Error while reading the request body: {0}'.format(e.message))
- try:
- js = json.loads(txt.decode(encoding=encoding, errors='replace'))
- except ValueError as e:
- raise falcon.HTTPBadRequest('Error parsing JSON payload', 'Error while parsing the request as JSON: {0}'.format(e.message))
- sid = js.get('sid')
- if sid is None:
- if session_is_optional:
- return js, None
- raise falcon.HTTPBadRequest('No Session', 'Request is missing a session-ID')
- del js['sid']
- try:
- session = client.get_session_by_id(sid)
- except NoSuchSession:
- if session_is_optional:
- return js, None
- raise falcon.HTTPBadRequest('Session Expired', 'This user session has expired. Please log-in again.')
- return js, session
-
-
-class CodeqService(object):
- """Base class for all CodeQ services.
- It only support the POST method and JSON data.
- Handles JSON decoding and encoding, and session retrieval which can be optional.
- """
- session_is_optional = False
-
- def on_post(self, req, resp):
- js, session = get_json_payload(req, self.session_is_optional)
- resp.body = json.dumps(self.process(js, session))
- resp.cache_control = 'private, no-cache, no-store'
- resp.content_type = 'application/json'
- resp.status = falcon.HTTP_200
-
- def process(self, js, session):
- raise falcon.HTTPNotFound()
-
-class ProblemList(CodeqService):
- session_is_optional = True
-
- def process(self, js, session):
- return {'code': 0, 'message': 'ok', 'problems': client.list_problems()}
-
-class Login(CodeqService):
- session_is_optional = True
-
- def process(self, js, old_session):
- username = js.get('username')
- password = js.get('password')
- if username is None:
- return {'code': 1, 'message': 'Username was not provided'}
- elif password is None:
- return {'code': 2, 'message': 'Password was not provided'}
- else:
- try:
- session = client.authenticate_and_create_session(username, password)
- except AuthenticationFailed:
- return {'code': 3, 'message': 'Username or password do not match'}
- else:
- if old_session:
- old_session.destroy()
- return {'code': 0, 'message': 'OK', 'sid':session.get_sid()}
-
-class Activity(CodeqService):
- def process(self, js, session):
- trace = js.get('trace')
- solution = js.get('solution')
- problem_id = js.get('problem_id')
- if (trace is not None) or (solution is not None):
- # we have something to do
- if problem_id is None:
- return {'code': 1, 'message': 'Problem ID is missing'}
- else:
- session.update_solution(problem_id, trace, solution)
-
-class Query(CodeqService):
- def process(self, js, session):
- step = js.get('step')
- if step is None:
- return {'code': 1, 'message': '"step" is not set'}
- else:
- problem_id = js.get('problem_id')
- if problem_id is None:
- return {'code': 4, 'message': 'Problem ID not given'}
- trace = js.get('trace')
- prolog = session.get_prolog()
- program = None
- if step == 'run':
- program = js.get('program')
- query = js.get('query')
- if program is None:
- result = {'code': 2, 'message': 'No program specified'}
- elif query is None:
- result = {'code': 3, 'message': 'No query specified'}
- else:
- messages, status, have_more = prolog.run_for_user(session.get_uid(), problem_id, program, query)
- result = {'code': 0, 'message': 'ok', 'terminal': {'messages': messages, 'status': status, 'have_more': have_more}}
- elif step == 'next':
- messages, status, have_more = prolog.step()
- result = {'code': 0, 'message': 'ok', 'terminal': {'messages': messages, 'status': status, 'have_more': have_more}}
- elif step == 'end':
- messages, status, have_more = prolog.end()
- result = {'code': 0, 'message': 'ok', 'terminal': {'messages': messages, 'status': status, 'have_more': have_more}}
- else:
- result = {'code': 5, 'message': 'Unknown prolog step: {0}'.format(step)}
- if program or trace:
- session.update_solution(problem_id, trace, program)
- return result
-
-# Pull stdout/stderr from the session's Python interpreter.
-class PythonPull(CodeqService):
- def process(self, js, session):
- python = session.get_python()
- output = python.pull()
- return {'code': 0, 'message': 'ok', 'terminal': {'text': output if output else ''}}
-
-# Push stdin to the session's Python interpreter.
-class PythonPush(CodeqService):
- def process(self, js, session):
- text = js.get('text')
- if text is None:
- return {'code': 1, 'message': 'No input specified'}
-
- python = session.get_python()
- python.push(text)
- return {'code': 0, 'message': 'ok'}
-
-class Hint(CodeqService):
- def process(self, js, session):
- language = js.get('language')
- problem_id = js.get('problem_id')
- program = js.get('program')
-
- if problem_id is None:
- return {'code': 1, 'message': 'No problem ID specified'}
- if program is None:
- return {'code': 2, 'message': 'No program specified'}
-
- if language == 'prolog':
- lang_session = session.get_prolog()
- elif language == 'python':
- lang_session = session.get_python()
- else:
- return {'code': 3, 'message': 'Unknown language specified'}
-
- hints = lang_session.hint(session.get_sid(), problem_id, program)
- return {'code': 0, 'message': 'ok', 'hints': hints}
-
-class Test(CodeqService):
- def process(self, js, session):
- language = js.get('language')
- problem_id = js.get('problem_id')
- program = js.get('program')
-
- if problem_id is None:
- return {'code': 1, 'message': 'No problem ID specified'}
- if program is None:
- return {'code': 2, 'message': 'No program specified'}
-
- if language == 'prolog':
- lang_session = session.get_prolog()
- elif language == 'python':
- lang_session = session.get_python()
- else:
- return {'code': 3, 'message': 'Unknown language specified'}
-
- hints = lang_session.test(session.get_sid(), problem_id, program)
- return {'code': 0, 'message': 'ok', 'hints': hints}
-
-class GetProblem(CodeqService):
- def process(self, js, session):
- language = js.get('language')
- problem_group = js.get('problem_group')
- problem = js.get('problem')
- if language is None:
- return {'code': 1, 'message': 'Language identifier not given'}
- elif problem_group is None:
- return {'code': 2, 'message': 'Problem group identifier not given'}
- elif problem is None:
- return {'code': 3, 'message': 'Problem identifier not given'}
- else:
- return {'code': 0, 'message': 'ok', 'data': session.get_problem_data(language, problem_group, problem)}
-
-problem_list = ProblemList()
-api.add_route('/list_problems', problem_list)
-
-login = Login()
-api.add_route('/login', login)
-
-activity = Activity()
-api.add_route('/activity', activity)
-
-query = Query()
-api.add_route('/query', query)
-
-api.add_route('/python_pull', PythonPull())
-api.add_route('/python_push', PythonPush())
-
-hint = Hint()
-api.add_route('/hint', hint)
-
-test = Test()
-api.add_route('/test', test)
-
-get_problem = GetProblem()
-api.add_route('/get_problem', get_problem)
-
-if __name__ == '__main__':
- import logging
- from waitress import serve
- logger = logging.getLogger('waitress')
- logger.setLevel(logging.DEBUG)
- serve(application, host='0.0.0.0', port=8082)