diff options
Diffstat (limited to 'paste/webkit/FakeWebware/MiscUtils/PickleRPC.py')
-rw-r--r-- | paste/webkit/FakeWebware/MiscUtils/PickleRPC.py | 428 |
1 files changed, 0 insertions, 428 deletions
diff --git a/paste/webkit/FakeWebware/MiscUtils/PickleRPC.py b/paste/webkit/FakeWebware/MiscUtils/PickleRPC.py deleted file mode 100644 index c74f651..0000000 --- a/paste/webkit/FakeWebware/MiscUtils/PickleRPC.py +++ /dev/null @@ -1,428 +0,0 @@ -""" -PickleRPC provides a Server object for connection to Pickle-RPC servers -for the purpose of making requests and receiving the responses. - - >>> from MiscUtils.PickleRPC import Server - >>> server = Server('http://localhost/cgi-bin/WebKit.cgi/Examples/PickleRPCExample') - >>> server.multiply(10,20) - 200 - >>> server.add(10,20) - 30 - - -See also: Server, Webkit.PickleRPCServlet, WebKit.Examples.PickleRPCExample - - -UNDER THE HOOD - -Requests look like this: - { - 'version': 1, # default - 'action': 'call', # default - 'methodName': 'NAME', - 'args': (A, B, ...), # default = (,) - 'keywords': {'A': A, 'B': B, ...} # default = {} - } - -Only 'methodName' is required since that is the only key without a -default value. - -Responses look like this: - { - 'timeReceived': N, - 'timeReponded': M, - 'value': V, - 'exception': E, - 'requestError': E, - } - -TimeReceived is the time the initial request was received. -TimeResponded is the time at which the response was finished, as -close to transmission as possible. The times are expressed as -number of seconds since the Epoch, e.g., time.time(). - -Value is whatever the method happened to return. - -Exception may be 'occurred' to indicate that an exception -occurred, the specific exception, such as "KeyError: foo" or the -entire traceback (as a string), at the discretion of the server. -It will always be a non-empty string if it is present. - -RequestError is an exception such as "Missing method -in request." (with no traceback) that indicates a problem with the -actual request received by the Pickle-RPC server. - -Value, exception and requestError are all exclusive to each other. - - -SECURITY - -Pickle RPC uses the SafeUnpickler class (in this module) to -prevent unpickling of unauthorized classes. By default, it -doesn't allow _any_ classes to be unpickled. You can override -allowedGlobals() or findGlobal() in a subclass as needed to -allow specific class instances to be unpickled. - -Note that both Transport in this module and PickleRPCServlet in -WebKit are derived from SafeUnpickler. - - -CREDIT - -The implementation of this module was taken directly from Python 2.2's -xmlrpclib and then transformed from XML-orientation to Pickle-orientation. - -The zlib compression was adapted from code by Skip Montanaro that I found -here: http://manatee.mojam.com/~skip/python/ -""" - - -__version__ = 1 # version of PickleRPC protocol - -import types - -try: - from cPickle import dumps, Unpickler, UnpicklingError -except ImportError: - from pickle import dumps, Unpickler, UnpicklingError - -try: - import zlib -except ImportError: - zlib = None - -try: - from cStringIO import StringIO -except ImportError: - from StringIO import StringIO - -class Error(Exception): - """ - The abstract exception/error class for all PickleRPC errors. - """ - pass - - -class ResponseError(Error): - """ - These are unhandled exceptions raised when the server was computing - a response. These will indicate errors such as: - * exception in the actual target method on the server - * malformed responses - * non "200 OK" status code responses - """ - pass - - -# Sometimes xmlrpclib is installed as a package, sometimes not. So we'll -# make sure it works either way. -try: - from xmlrpclib.xmlrpclib import ProtocolError as _PE -except ImportError: - from xmlrpclib import ProtocolError as _PE -# @@ 2002-01-31 ce: should this be caught somewhere for special handling? Perhaps in XMLRPCServlet? - -class ProtocolError(ResponseError, _PE): - pass - - -class RequestError(Error): - """ - These are errors originally raised by the server complaining about - malformed requests. - """ - pass - - -class InvalidContentTypeError(ResponseError): - - def __init__(self, headers, content): - Exception.__init__(self) #, headers, content) - self.headers = headers - self.content = content - - def __repr__(self): - content = self.content - return '%s: Content type is not text/x-python-pickled-dict\nheaders = %s\ncontent =\n%s' % ( - self.__class__.__name__, self.headers, content) - - __str__ = __repr__ - - -class SafeUnpickler: - """ - For security reasons, we don't want to allow just anyone to unpickle - anything. That can cause arbitrary code to be executed. - So this SafeUnpickler base class is used to control - what can be unpickled. By default it doesn't let you unpickle - any class instances at all, but you can create subclass that - overrides allowedGlobals(). - - Note that the PickleRPCServlet class in WebKit is derived from this class - and uses its load() and loads() methods to do all unpickling. - """ - def allowedGlobals(self): - """ - Must return a list of (moduleName, klassName) tuples for all - classes that you want to allow to be unpickled. - - Example: - return [('mx.DateTime', '_DT')] - allows mx.DateTime instances to be unpickled. - """ - return [] - - def findGlobal(self, module, klass): - if (module, klass) not in self.allowedGlobals(): - raise UnpicklingError, 'For security reasons, you can\'t unpickle objects from module %s with type %s' % (module, klass) - globals = {} - exec 'from %s import %s as theClass' % (module, klass) in globals - return globals['theClass'] - - def load(self, file): - safeUnpickler = Unpickler(file) - safeUnpickler.find_global = self.findGlobal - return safeUnpickler.load() - - def loads(self, str): - return self.load(StringIO(str)) - - -# @@ 2002-01-31 ce: Could we reduce code duplication and automatically -# inherit future improvements by actually importing and using the -# xmlrpclib classes below either as base classes or mix-ins? - - -class Server: - """uri [,options] -> a logical connection to an XML-RPC server - - uri is the connection point on the server, given as - scheme://host/target. - - The standard implementation always supports the "http" scheme. If - SSL socket support is available (Python 2.0), it also supports - "https". - - If the target part and the slash preceding it are both omitted, - "/PickleRPC" is assumed. - - See the module doc string for more information. - """ - - def __init__(self, uri, transport=None, verbose=0, binary=1, compressRequest=1, acceptCompressedResponse=1): - # establish a "logical" server connection - - # get the url - import urllib - type, uri = urllib.splittype(uri) - if type not in ("http", "https"): - raise IOError, "unsupported Pickle-RPC protocol" - self.__host, self.__handler = urllib.splithost(uri) - if not self.__handler: - self.__handler = "/PickleRPC" - - if transport is None: - if type == "https": - transport = SafeTransport() - else: - transport = Transport() - self.__transport = transport - - self.__verbose = verbose - self.__binary = binary - self.__compressRequest = compressRequest - self.__acceptCompressedResponse = acceptCompressedResponse - - def _request(self, methodName, args, keywords): - """ - Call a method on the remote server. - """ - request = { - 'version': 1, - 'action': 'call', - 'methodName': methodName, - 'args': args, - 'keywords': keywords, - } - if self.__binary: - request = dumps(request, 1) - else: - request = dumps(request) - if zlib is not None and self.__compressRequest and len(request) > 1000: - request = zlib.compress(request, 1) - compressed = 1 - else: - compressed = 0 - - response = self.__transport.request( - self.__host, - self.__handler, - request, - verbose=self.__verbose, - binary=self.__binary, - compressed=compressed, - acceptCompressedResponse=self.__acceptCompressedResponse - ) - - return response - - def __requestValue(self, methodName, args, keywords): - dict = self._request(methodName, args, keywords) - if dict.has_key('value'): - return dict['value'] - elif dict.has_key('exception'): - raise ResponseError, dict['exception'] - elif dict.has_key('requestError'): - raise RequestError, dict['requestError'] - else: - raise RequestError, 'Response does not have a value, expection or requestError.' - - def __repr__(self): - return "<%s for %s%s>" % (self.__class__.__name__, self.__host, self.__handler) - - __str__ = __repr__ - - def __getattr__(self, name): - # magic method dispatcher - return _Method(self.__requestValue, name) - - ## note: to call a remote object with an non-standard name, use - ## result getattr(server, "strange-python-name")(args) - - -ServerProxy = Server # be like xmlrpclib for those who might guess or expect it - - - -class _Method: - """ - Some magic to bind a Pickle-RPC method to an RPC server. - Supports "nested" methods (e.g. examples.getStateName). - """ - - def __init__(self, send, name): - self.__send = send - self.__name = name - - def __getattr__(self, name): - return _Method(self.__send, "%s.%s" % (self.__name, name)) - - def __call__(self, *args, **keywords): # note that keywords are supported - return self.__send(self.__name, args, keywords) - - -class Transport(SafeUnpickler): - """ - Handles an HTTP transaction to a Pickle-RPC server. - """ - - # client identifier (may be overridden) - user_agent = "PickleRPC/%s (by http://webware.sf.net/)" % __version__ - - def request(self, host, handler, request_body, verbose=0, binary=0, compressed=0, - acceptCompressedResponse=0): - # issue Pickle-RPC request - - h = self.make_connection(host) - if verbose: - h.set_debuglevel(1) - - self.send_request(h, handler, request_body) - self.send_host(h, host) - self.send_user_agent(h) - self.send_content(h, request_body, binary, compressed, acceptCompressedResponse) - - errcode, errmsg, headers = h.getreply() - - if errcode != 200: - raise ProtocolError( - host + handler, - errcode, errmsg, - headers - ) - - self.verbose = verbose - - if h.headers['content-type'] not in ['text/x-python-pickled-dict', 'application/x-python-binary-pickled-dict']: - headers = h.headers.headers - content = h.getfile().read() - raise InvalidContentTypeError(headers, content) - - try: - content_encoding = headers["content-encoding"] - if content_encoding and content_encoding == "x-gzip": - return self.parse_response_gzip(h.getfile()) - elif content_encoding: - raise ProtocolError(host + handler, - 500, - "Unknown encoding type: %s" % - content_encoding, - headers) - else: - return self.parse_response(h.getfile()) - except KeyError: - return self.parse_response(h.getfile()) - - def make_connection(self, host): - # create a HTTP connection object from a host descriptor - import httplib - return httplib.HTTP(host) - - def send_request(self, connection, handler, request_body): - connection.putrequest("POST", handler) - - def send_host(self, connection, host): - connection.putheader("Host", host) - - def send_user_agent(self, connection): - connection.putheader("User-Agent", self.user_agent) - - def send_content(self, connection, request_body, binary=0, compressed=0, - acceptCompressedResponse=0): - if binary: - connection.putheader("Content-Type", "application/x-python-binary-pickled-dict") - else: - connection.putheader("Content-Type", "text/x-python-pickled-dict") - connection.putheader("Content-Length", str(len(request_body))) - if compressed: - connection.putheader("Content-Encoding", "x-gzip") - if zlib is not None and acceptCompressedResponse: - connection.putheader("Accept-Encoding", "gzip") - connection.endheaders() - if request_body: - connection.send(request_body) - - def parse_response(self, f): - return self.load(f) - - def parse_response_gzip(self, f): - # read response from input file, decompress it, and parse it - # @@ gat: could this be made more memory-efficient? - return self.loads(zlib.decompress(f.read())) - -class SafeTransport(Transport): - """ - Handles an HTTPS transaction to a Pickle-RPC server. - """ - - def make_connection(self, host): - # create a HTTPS connection object from a host descriptor - # host may be a string, or a (host, x509-dict) tuple - import httplib - if isinstance(host, types.TupleType): - host, x509 = host - else: - x509 = {} - try: - HTTPS = httplib.HTTPS - except AttributeError: - raise NotImplementedError,\ - "your version of httplib doesn't support HTTPS" - else: - return apply(HTTPS, (host, None), x509) - - def send_host(self, connection, host): - if isinstance(host, types.TupleType): - host, x509 = host - connection.putheader("Host", host) - |