diff options
| author | 2012-10-11 20:55:48 +0200 | |
|---|---|---|
| committer | 2012-10-11 20:55:48 +0200 | |
| commit | 582b8eb777a86a157f4825e3d9d68e8655e8f092 (patch) | |
| tree | 2c96b04e167135101c57d2ef2d387380f60c1370 /module/remote | |
| parent | last test commit (diff) | |
| download | pyload-582b8eb777a86a157f4825e3d9d68e8655e8f092.tar.xz | |
refactored ws handler, generate debug information for ttypes
Diffstat (limited to 'module/remote')
| -rw-r--r-- | module/remote/create_ttypes.py | 72 | ||||
| -rw-r--r-- | module/remote/ttypes_debug.py | 277 | ||||
| -rw-r--r-- | module/remote/wsbackend/AbstractHandler.py | 79 | ||||
| -rw-r--r-- | module/remote/wsbackend/ApiHandler.py | 50 | ||||
| -rw-r--r-- | module/remote/wsbackend/EventHandler.py | 32 | 
5 files changed, 451 insertions, 59 deletions
diff --git a/module/remote/create_ttypes.py b/module/remote/create_ttypes.py index a9a93bde7..023db60bb 100644 --- a/module/remote/create_ttypes.py +++ b/module/remote/create_ttypes.py @@ -11,9 +11,44 @@ module = join(path, "..", "..")  sys.path.append(join(module, "lib"))  sys.path.append(join(module, "remote")) +from thrift.Thrift import TType  from thriftgen.pyload import ttypes -from thriftgen.pyload.Pyload import Iface +from thriftgen.pyload import Pyload + +type_map = { +    TType.BOOL: 'bool', +    TType.DOUBLE: 'float', +    TType.I16: 'int', +    TType.I32: 'int', +    TType.I64: 'int', +    TType.STRING: 'basestring', +    TType.MAP: 'dict', +    TType.LIST: 'list', +    TType.SET: 'set', +    TType.VOID: 'None', +    TType.STRUCT: 'BaseObject', +    TType.UTF8: 'unicode', +} + +def write_spec(attr, spec, f): +    """ analyze the generated spec file and writes information into file """ +    if spec[1] == TType.STRUCT: +        f.write("\t'%s': %s,\n" % (attr, spec[3][0].__name__)) +    elif spec[1]  == TType.LIST: +        if spec[3][0] == TType.STRUCT: +            ttype = spec[3][1][0].__name__ +        else: +            ttype = type_map[spec[3][0]] +        f.write("\t'%s': (list, %s),\n" % (attr, ttype)) +    elif spec[1] == TType.MAP: +        if spec[3][2] == TType.STRUCT: +            ttype = spec[3][3][0].__name__ +        else: +            ttype = type_map[spec[3][2]] +        f.write("\t'%s': (dict, %s, %s),\n" % (attr, type_map[spec[3][0]], ttype)) +    else: +        f.write("\t'%s': %s,\n" % (attr, type_map[spec[1]]))  def main(): @@ -35,7 +70,6 @@ def main():      f = open(join(path, "ttypes.py"), "wb") -      f.write(          """#!/usr/bin/env python  # -*- coding: utf-8 -*- @@ -47,6 +81,14 @@ class BaseObject(object):  """) +    dev = open(join(path, "ttypes_debug.py"), "wb") +    dev.write("""#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Autogenerated by pyload +# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n +from ttypes import *\n +""") +      ## generate enums      for enum in enums:          name = enum.__name__ @@ -59,33 +101,53 @@ class BaseObject(object):          f.write("\n") +    dev.write("classes = {\n\n") +      for klass in classes:          name = klass.__name__          base = "Exception" if issubclass(klass, ttypes.TExceptionBase) else "BaseObject"          f.write("class %s(%s):\n" % (name,  base))          f.write("\t__slots__ = %s\n\n" % klass.__slots__) +        dev.write("'%s' : {\n" % name)          #create init          args = ["self"] + ["%s=None" % x for x in klass.__slots__]          f.write("\tdef __init__(%s):\n" % ", ".join(args)) -        for attr in klass.__slots__: +        for i, attr in enumerate(klass.__slots__):              f.write("\t\tself.%s = %s\n" % (attr, attr)) +            spec = klass.thrift_spec[i+1] +            assert spec[2] == attr +            write_spec(attr, spec, dev) +          f.write("\n") +        dev.write("},\n") + +    dev.write("}\n\n")      f.write("class Iface(object):\n") +    dev.write("methods = {\n") -    for name in dir(Iface): +    for name in dir(Pyload.Iface):          if name.startswith("_"): continue -        func = inspect.getargspec(getattr(Iface, name)) +        func = inspect.getargspec(getattr(Pyload.Iface, name))          f.write("\tdef %s(%s):\n\t\tpass\n" % (name, ", ".join(func.args))) +        spec = getattr(Pyload, "%s_result" % name).thrift_spec +        if not spec or not spec[0]: +            dev.write("\t'%s': None,\n" % name) +        else: +            spec = spec[0] +            write_spec(name, spec, dev) +      f.write("\n") +    dev.write("}\n")      f.close() +    dev.close()  if __name__ == "__main__":      main()
\ No newline at end of file diff --git a/module/remote/ttypes_debug.py b/module/remote/ttypes_debug.py new file mode 100644 index 000000000..dca54a56a --- /dev/null +++ b/module/remote/ttypes_debug.py @@ -0,0 +1,277 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Autogenerated by pyload +# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +from ttypes import * + +classes = { + +'AccountInfo' : { +	'plugin': basestring, +	'loginname': basestring, +	'owner': int, +	'valid': bool, +	'validuntil': int, +	'trafficleft': int, +	'maxtraffic': int, +	'premium': bool, +	'activated': bool, +	'shared': bool, +	'options': (dict, basestring, basestring), +}, +'AddonInfo' : { +	'func_name': basestring, +	'description': basestring, +	'value': basestring, +}, +'AddonService' : { +	'func_name': basestring, +	'description': basestring, +	'arguments': (list, basestring), +	'media': int, +}, +'ConfigHolder' : { +	'name': basestring, +	'label': basestring, +	'description': basestring, +	'long_description': basestring, +	'items': (list, ConfigItem), +	'info': (list, AddonInfo), +	'handler': (list, InteractionTask), +}, +'ConfigInfo' : { +	'name': basestring, +	'label': basestring, +	'description': basestring, +	'saved': bool, +	'activated': bool, +}, +'ConfigItem' : { +	'name': basestring, +	'label': basestring, +	'description': basestring, +	'type': basestring, +	'default_value': basestring, +	'value': basestring, +}, +'DownloadInfo' : { +	'url': basestring, +	'plugin': basestring, +	'hash': basestring, +	'status': int, +	'statusmsg': basestring, +	'error': basestring, +}, +'DownloadProgress' : { +	'fid': int, +	'pid': int, +	'speed': int, +	'status': int, +}, +'EventInfo' : { +	'eventname': basestring, +	'event_args': (list, basestring), +}, +'FileDoesNotExists' : { +	'fid': int, +}, +'FileInfo' : { +	'fid': int, +	'name': basestring, +	'package': int, +	'owner': int, +	'size': int, +	'status': int, +	'media': int, +	'added': int, +	'fileorder': int, +	'download': DownloadInfo, +}, +'InteractionTask' : { +	'iid': int, +	'input': int, +	'data': (list, basestring), +	'output': int, +	'default_value': basestring, +	'title': basestring, +	'description': basestring, +	'plugin': basestring, +}, +'LinkStatus' : { +	'url': basestring, +	'name': basestring, +	'plugin': basestring, +	'size': int, +	'status': int, +	'packagename': basestring, +}, +'OnlineCheck' : { +	'rid': int, +	'data': (dict, basestring, LinkStatus), +}, +'PackageDoesNotExists' : { +	'pid': int, +}, +'PackageInfo' : { +	'pid': int, +	'name': basestring, +	'folder': basestring, +	'root': int, +	'owner': int, +	'site': basestring, +	'comment': basestring, +	'password': basestring, +	'added': int, +	'tags': (list, basestring), +	'status': int, +	'packageorder': int, +	'stats': PackageStats, +	'fids': (list, int), +	'pids': (list, int), +}, +'PackageStats' : { +	'linkstotal': int, +	'linksdone': int, +	'sizetotal': int, +	'sizedone': int, +}, +'ProgressInfo' : { +	'plugin': basestring, +	'name': basestring, +	'statusmsg': basestring, +	'eta': int, +	'format_eta': basestring, +	'done': int, +	'total': int, +	'download': DownloadProgress, +}, +'ServerStatus' : { +	'pause': bool, +	'active': int, +	'queue': int, +	'total': int, +	'speed': int, +	'download': bool, +	'reconnect': bool, +}, +'ServiceDoesNotExists' : { +	'plugin': basestring, +	'func': basestring, +}, +'ServiceException' : { +	'msg': basestring, +}, +'TreeCollection' : { +	'root': PackageInfo, +	'files': (dict, int, FileInfo), +	'packages': (dict, int, PackageInfo), +}, +'UserData' : { +	'uid': int, +	'name': basestring, +	'email': basestring, +	'role': int, +	'permission': int, +	'folder': basestring, +	'traffic': int, +	'dllimit': int, +	'dlquota': basestring, +	'hddquota': int, +	'user': int, +	'templateName': basestring, +}, +'UserDoesNotExists' : { +	'user': basestring, +}, +} + +methods = { +	'addFromCollector': int, +	'addLinks': None, +	'addLocalFile': None, +	'addPackage': int, +	'addPackageChild': int, +	'addPackageP': int, +	'addToCollector': None, +	'addUser': UserData, +	'callAddon': None, +	'callAddonHandler': None, +	'checkOnlineStatus': OnlineCheck, +	'checkOnlineStatusContainer': OnlineCheck, +	'checkURLs': (dict, basestring, list), +	'configurePlugin': ConfigHolder, +	'createPackage': int, +	'deleteCollLink': None, +	'deleteCollPack': None, +	'deleteConfig': None, +	'deleteFiles': None, +	'deletePackages': None, +	'findFiles': TreeCollection, +	'freeSpace': int, +	'generateAndAddPackages': (list, int), +	'generateDownloadLink': basestring, +	'generatePackages': (dict, basestring, list), +	'getAccountTypes': (list, basestring), +	'getAccounts': (list, AccountInfo), +	'getAddonHandler': (dict, basestring, list), +	'getAllFiles': TreeCollection, +	'getAllInfo': (dict, basestring, list), +	'getAllUserData': (dict, int, UserData), +	'getCollector': (list, LinkStatus), +	'getConfig': (dict, basestring, ConfigHolder), +	'getEvents': (list, EventInfo), +	'getFileInfo': FileInfo, +	'getFileTree': TreeCollection, +	'getFilteredFileTree': TreeCollection, +	'getFilteredFiles': TreeCollection, +	'getGlobalPlugins': (list, ConfigInfo), +	'getInfoByPlugin': (list, AddonInfo), +	'getInteractionTask': InteractionTask, +	'getLog': (list, basestring), +	'getNotifications': (list, InteractionTask), +	'getPackageContent': TreeCollection, +	'getPackageInfo': PackageInfo, +	'getProgressInfo': (list, ProgressInfo), +	'getServerVersion': basestring, +	'getUserData': UserData, +	'getUserPlugins': (list, ConfigInfo), +	'hasAddonHandler': bool, +	'isInteractionWaiting': bool, +	'isTimeDownload': bool, +	'isTimeReconnect': bool, +	'kill': None, +	'login': bool, +	'moveFiles': bool, +	'movePackage': bool, +	'orderFiles': None, +	'orderPackage': None, +	'parseURLs': (dict, basestring, list), +	'pauseServer': None, +	'pollResults': OnlineCheck, +	'recheckPackage': None, +	'removeAccount': None, +	'removeUser': None, +	'renameCollPack': None, +	'restart': None, +	'restartFailed': None, +	'restartFile': None, +	'restartPackage': None, +	'saveConfig': None, +	'setConfigHandler': None, +	'setInteractionResult': None, +	'setPackageData': None, +	'setPackageFolder': bool, +	'setPackagePaused': None, +	'setPassword': bool, +	'statusServer': ServerStatus, +	'stopAllDownloads': None, +	'stopDownloads': None, +	'togglePause': bool, +	'toggleReconnect': bool, +	'unpauseServer': None, +	'updateAccount': None, +	'updateAccountInfo': None, +	'updateUserData': None, +	'uploadContainer': int, +} diff --git a/module/remote/wsbackend/AbstractHandler.py b/module/remote/wsbackend/AbstractHandler.py new file mode 100644 index 000000000..291dbf100 --- /dev/null +++ b/module/remote/wsbackend/AbstractHandler.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +############################################################################### +#   Copyright(c) 2008-2012 pyLoad Team +#   http://www.pyload.org +# +#   This file is part of pyLoad. +#   pyLoad is free software: you can redistribute it and/or modify +#   it under the terms of the GNU Affero General Public License as +#   published by the Free Software Foundation, either version 3 of the +#   License, or (at your option) any later version. +# +#   Subjected to the terms and conditions in LICENSE +# +#   @author: RaNaN +############################################################################### + +from mod_pywebsocket.msgutil import send_message +from mod_pywebsocket.util import get_class_logger +from module.remote.json_converter import loads, dumps + + +class AbstractHandler: +    """ +        Abstract Handler providing common methods shared across WebSocket handlers +    """ + +    def __init__(self, api): +        self.log = get_class_logger() +        self.api = api + +    def do_extra_handshake(self, req): +        self.log.debug("WS Connected: %s" % req) +        self.on_open(req) + +    def on_open(self, req): +        pass + +    def passive_closing_handshake(self, req): +        self.log.debug("WS Closed: %s" % req) +        self.on_close(req) + +    def on_close(self, req): +        pass + +    def transfer_data(self, req): +        raise NotImplemented + +    def handle_call(self, msg, req): +        """ Parses the msg for an argument call. If func is null an response was already sent. + +        :param msg: +        :param req: +        :return: func, args, kwargs +        """ +        try: +            o = loads(msg) +        except ValueError, e: #invalid json object +            self.log.debug("Invalid Request: %s" % e) +            return None, None, None + +        if type(o) != list and len(o) > 2: +            self.log.debug("Invalid Api call: %s" % o) +            self.send_result(req, 500, "Invalid Api call") +            return None, None, None +        if len(o) == 1: # arguments omitted +            o.append([]) + +        func, args = o +        if type(args) == list: +            kwargs = {} +        else: +            args, kwargs = [], args + +        return func, args, kwargs + +    def send_result(self, req, code, result): +        return send_message(req, dumps([code, result]))
\ No newline at end of file diff --git a/module/remote/wsbackend/ApiHandler.py b/module/remote/wsbackend/ApiHandler.py index 478ea6de0..57d9ecd5b 100644 --- a/module/remote/wsbackend/ApiHandler.py +++ b/module/remote/wsbackend/ApiHandler.py @@ -16,11 +16,11 @@  #   @author: RaNaN  ############################################################################### -from mod_pywebsocket.util import get_class_logger -from mod_pywebsocket.msgutil import receive_message, send_message -from module.remote.json_converter import loads, dumps +from mod_pywebsocket.msgutil import receive_message -class ApiHandler: +from AbstractHandler import AbstractHandler + +class ApiHandler(AbstractHandler):      """Provides access to the API.      Send your request as json encoded string in the following manner: @@ -34,44 +34,22 @@ class ApiHandler:      Non json request will be ignored.      """ -    def __init__(self, api): -        self.log = get_class_logger() -        self.api = api - -    def do_extra_handshake(self, req): -        self.log.debug("WS Connected: %s" % req) -      def transfer_data(self, req): -          while True:              try:                  line = receive_message(req) -                self.handle_message(line, req)              except TypeError, e: # connection closed -                print e +                self.log.debug("WS Error: %s" % e) +                self.passive_closing_handshake(req)                  return -    def passive_closing_handshake(self, req): -        self.log.debug("WS Closed: %s" % req) +            self.handle_message(line, req)      def handle_message(self, msg, req): -        try: -            o = loads(msg) -        except ValueError, e: #invalid json object -            self.log.debug("Invalid Request: %s" % e) -            return - -        if type(o) != list and len(o) > 2: -            self.log.debug("Invalid Api call: %s" % o) -            return self.send_result(req, 500, "Invalid Api call") -        if len(o) == 1: # arguments omitted -            o.append([]) - -        func, args = o -        if type(args) == list: -            kwargs = {} -        else: -            args, kwargs = [], args + +        func, args, kwargs = self.handle_call(msg, req) +        if not func: +            return # Result was already sent          if func == 'login':              user =  self.api.checkAuth(*args, **kwargs) @@ -103,8 +81,4 @@ class ApiHandler:              # None is invalid json type              if result is None: result = True -            return self.send_result(req, 200, result) - - -    def send_result(self, req, code, result): -        return send_message(req, dumps([code, result]))
\ No newline at end of file +            return self.send_result(req, 200, result)
\ No newline at end of file diff --git a/module/remote/wsbackend/EventHandler.py b/module/remote/wsbackend/EventHandler.py index deab25a6c..2550ff2eb 100644 --- a/module/remote/wsbackend/EventHandler.py +++ b/module/remote/wsbackend/EventHandler.py @@ -16,26 +16,26 @@  #   @author: RaNaN  ############################################################################### -from mod_pywebsocket.msgutil import receive_message, send_message +from threading import Lock -class EventHandler: -    def __init__(self, api): -        self.api = api +from module.utils import lock +from AbstractHandler import AbstractHandler -    def do_extra_handshake(self, req): -        pass +class EventHandler(AbstractHandler): -    def transfer_data(self, req): +    def __init__(self, api): +        AbstractHandler.__init__(self, api) +        self.clients = [] +        self.lock = Lock() -        while True: -            try: -                line = receive_message(req) -            except TypeError: # connection closed -                return +    @lock +    def on_open(self, req): +        self.clients.append(req) -            print "Got", line -            send_message(req, "You send: %s" % line) +    @lock +    def on_close(self, req): +        self.clients.remove(req) -    def passive_closing_handshake(self, req): -        print "Closed", req
\ No newline at end of file +    def handle_message(self, line, req): +        pass
\ No newline at end of file  | 
