From 5b81841f420eb948de7f184b588bc75a3227da15 Mon Sep 17 00:00:00 2001 From: emkael Date: Fri, 31 May 2019 17:18:46 +0200 Subject: Web (mod_python) API --- cache/.gitignore | 1 + http/api/.htaccess | 6 ++ http/api/api.py | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 cache/.gitignore create mode 100644 http/api/.htaccess create mode 100644 http/api/api.py diff --git a/cache/.gitignore b/cache/.gitignore new file mode 100644 index 0000000..72e8ffc --- /dev/null +++ b/cache/.gitignore @@ -0,0 +1 @@ +* diff --git a/http/api/.htaccess b/http/api/.htaccess new file mode 100644 index 0000000..998bfca --- /dev/null +++ b/http/api/.htaccess @@ -0,0 +1,6 @@ +RewriteEngine On +RewriteRule ^.*$ api.py [QSA,L] + +AddHandler mod_python .py +PythonHandler api +PythonDebug On diff --git a/http/api/api.py b/http/api/api.py new file mode 100644 index 0000000..1c1cb0f --- /dev/null +++ b/http/api/api.py @@ -0,0 +1,165 @@ +# coding=utf-8 + +import base64, copy, json, os, random, sys, warnings +from StringIO import StringIO + +from mod_python import apache, Session + +OLDPATH = copy.copy(sys.path) +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) +from dealconvert import DealConverter +sys.path = OLDPATH + +CACHEPATH = os.path.abspath( + os.path.join( + os.path.dirname(__file__), + '../../cache')) + +def _get_rand_string(length=30): + return ('%0' + str(length) + 'x') % (random.randrange(16**length)) + +def _get_file_id(): + while True: + output_id = _get_rand_string() + output_path = os.path.join(CACHEPATH, output_id) + if not os.path.exists(output_path): + return output_id, output_path + +def _print_response(response, obj): + response.write(json.dumps(obj)) + +def handle_upload(response, request): + if request.method != 'POST': + response.status = apache.HTTP_METHOD_NOT_ALLOWED + return + try: + params = json.load(request) + except ValueError as e: + response.write(str(e)) + response.status = apache.HTTP_BAD_REQUEST + return + + session = Session.Session(response) + if 'tokens' not in session: + session['tokens'] = {} + + response.content_type = 'application/json' + return_obj = { + 'name': None, + 'warnings': [], + 'error': None, + 'files': [] + } + warnings.simplefilter('always') + warnings.showwarning = lambda msg, *args: return_obj['warnings'].append( + unicode(msg)) + + try: + return_obj['name'] = params['name'] + converter = DealConverter() + parser = converter.detect_format(params['name']) + input_file = StringIO(base64.b64decode(params['content'])) + dealset = parser.parse_content(input_file) + input_file.close() + if not len(dealset): + raise RuntimeError('Dealset is empty') + except RuntimeError as e: + return_obj['error'] = unicode(e) + return _print_response(response, return_obj) + + for output_type in params['output']: + output_return = { + 'name': None, + 'link': None, + 'warnings': [], + 'error': None + } + warnings.showwarning = lambda msg, *args: output_return['warnings'].append( + unicode(msg)) + try: + output_name = '.'.join(params['name'].split('.')[:-1] + [output_type]) + output_return['name'] = output_name + output = converter.detect_format(output_name) + output_id, output_path = _get_file_id() + token = _get_rand_string(16) + output_buffer = StringIO() + output.output_content(output_buffer, dealset) + with file(output_path, 'w') as output_file: + json.dump({ + 'token': token, + 'name': output_name, + 'content': base64.b64encode(output_buffer.getvalue()) + }, output_file) + output_buffer.close() + session['tokens'][output_id] = token + output_return['link'] = 'download/%s' % (output_id) + except RuntimeError as e: + output_return['error'] = unicode(e) + return_obj['files'].append(output_return) + session.save() + _print_response(response, return_obj) + + +def handle_download(response, request, uri_parts=[]): + if not len(uri_parts): + response.status = apache.HTTP_BAD_REQUEST + return + if request.method != 'GET': + response.status = apache.HTTP_METHOD_NOT_ALLOWED + return + + session = Session.Session(response) + + if 'tokens' not in session: + response.status = apache.HTTP_NOT_FOUND + return + + output_id = uri_parts[0] + output_path = os.path.join(CACHEPATH, output_id) + if not os.path.exists(output_path): + response.status = apache.HTTP_NOT_FOUND + return + if output_id not in session['tokens']: + response.status = apache.HTTP_NOT_FOUND + return + with file(output_path) as output_file: + output = json.load(output_file) + if output['token'] != session['tokens'][output_id]: + response.status = apache.HTTP_NOT_FOUND + return + content = base64.b64decode(output['content']) + response.content_type = 'application/octet-stream' + response.headers_out.add( + 'Content-Disposition', 'attachment; filename=%s' % (output['name'])) + response.write(content) + +def handler(req): + # MIME type fix for error messages + req.content_type = 'text/plain' + + # we need to recover original request path, from before rewrite + orig_req = req + while True: + if orig_req.prev: + orig_req = orig_req.prev + else: + break + + uri_parts = [part for part in orig_req.uri.split('/') if part.strip()] + uri_parts = uri_parts[uri_parts.index('api')+1:] + + if not len(uri_parts): + req.status = apache.HTTP_BAD_REQUEST + else: + try: + if uri_parts[0] == 'upload': + handle_upload(req, orig_req) + elif uri_parts[0] == 'download': + handle_download(req, orig_req, uri_parts[1:]) + else: + req.status = apache.HTTP_BAD_REQUEST + except Exception as e: + req.status = apache.HTTP_INTERNAL_SERVER_ERROR + req.write(str(e)) + + return apache.OK -- cgit v1.2.3