1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 import thread
32 from threading import Lock
33 __thread_data__ = {}
34
36 """ Returns the spyro's request dictionary that
37 rules the current thread or spyro request. If this thread is not
38 ruled by an spyro request, it will raise a KeyError exception"""
39 return __thread_data__[thread.get_ident()]
40
43
46
47 import warn
48 warn = warn.Warn("SPyRO.SPyRO")
49 import os
50 import sys
51 import socket, SocketServer
52 import gzip
53 try:
54 from cStringIO import StringIO
55 except ImportError:
56 from StringIO import StringIO
57
58 from SocketServer import ThreadingTCPServer
59 import Queue
60 import httplib
61 import time
62
63 import shttp
64 import shttp.http as http
65
66 from shttp import uri
67 from shttp.uri import URI
68 import policies
69
70 from policies import AuthError
71
72 from codepools import CodePool, ServerObjectPool, ModulePool
73
74 try:
75 import OpenSSL
76 import OpenSSL.SSL as SSL
77 except ImportError, err:
78 warn.debug("Unable to use HTTPS as transport layer ", err)
79 SSL = None
80
81 import args as Args
82
83 sleep_min_sec = 0.05
84 sleep_max_sec = 100.0
85
86 SendByReference = 'REF'
87 SendByValue = 'VAL'
88
91
92 import types
93 from types import DictType
94
96 BooleanType = types.IntType
97 __builtins__['True'] = 1
98 __builtins__['False'] = 0
99
100 try:
101 BooleanType = types.BooleanType
102 True
103 except:
104 warn.warn("An old version of python or jython was detected")
105 _set_booleans()
106
107 _HIBRID_SEND_VALUE_tuple = (types.StringType,
108 types.NoneType,
109 types.IntType,
110 BooleanType,
111 types.LongType, types.FloatType,
112 types.UnicodeType,
113
114 )
116 """ By default pickle is disabled, to enable you must overload 'overture' or
117 set to None
118
119 Any other prelude action must be done in this method. The overture is provided
120 to control the execution before start it. The authorities (from policies) are
121 checked when a protocol is up and loading. This method must be set to control
122 access and tune settings before any protocol and authority.
123 """
124 q = environment['QUERY_STRING'].lower()
125 if 'pickle' in q:
126 raise "The pickle marshaler is not allowed on this SPyRO server"
127
128 __doc__ = """
129 Simple Python Remote Objects
130 Implements Simple Remote Objects (attributes and methods).
131
132 Provides the complete suite: server and client services, and RemoteObject
133 wrappers to objects.
134
135 Implement new formats are quite simple. Just create a class or module with
136 the methods or functions loads and dumps and register your interface
137 with the |registerFormat| function. Thread safe operation is responsibility
138 of the new add-on.
139
140
141 Important Note:
142 Since SPyRO |Server| is multithreaded, but |CommunicationCliente| had synchronous
143 protocol, it will not be able to manage many request (every action over a
144 object is a request) on the same connection. It is necessary use a |Client|
145 object peer concurrent thread, in the best case, may be one or two are
146 necessary or use |TClient| (uses a pool of |Client|'s transparentely).
147
148 """
149
150 import formats
151 ORBFormats = formats.ORBFormats()
152 ORBFormats.init()
153
154
156 """ Common methods in the ProtocolHTTP* server and clients"""
164
166
167
168
169
170
171 return x
172
176
177
179 """ Protocol used by clients, it runs under HTTP transmition layer """
180 - def __init__(self, address, location, compress, usingssl=False):
181 """ Initialize a client protocol manager.
182
183 Using |address| as a tuple (host,port) and |location|
184 Set the remote object location to perform the remote operations.
185 In |location| the format of the broker is attached concatenating
186 a '?' and a name of the format to the normal location.
187 |compress| Use gzip compression in messages
188 |usingssl| If True, the connection will be done using https
189
190 For example, to request a remote server that is listening on
191 /path/SPyRO to use the registered XMLRPC format /path/SPyRO?XMLRPC"""
192 self.address = address
193 self.usingssl = usingssl
194 if not usingssl:
195 self.conn = httplib.HTTPConnection(address[0], address[1])
196 else:
197 self.conn = httplib.HTTPSConnection(address[0], address[1])
198 self.location = location
199 self.__closed = False
200 self.format = None
201 self._choose_format(uri.query_string(location), None)
202 self.compress = compress
203
207
209
210 self.conn.close()
211
212 - def __request(self, msg, contenttype = "application/xml"):
213 """ Send the message |msg|"""
214
215
216 conn = self.conn
217 conn.putrequest('POST',self.location)
218 conn.putheader('Host', "%s:%s"%(self.address[0],self.address[1]))
219 conn.putheader('Content-Type', contenttype)
220 if self.compress:
221 conn.putheader('Accept-Encoding', 'x-gzip')
222
223
224
225
226
227
228
229 conn.putheader('Content-Length', len(msg))
230 conn.endheaders()
231 conn.send(msg)
232
234 """ Read a message and returns. It uses a specific format """
235 res = self.conn.getresponse()
236 gzipheader = res.getheader("content-encoding")
237 if gzipheader and gzipheader.find("gzip") > 0:
238 f = gzip.GzipFile(fileobj=StringIO(res.read()))
239 msg = f.read()
240 f.close()
241 else:
242 msg = res.read()
243 x = self.broker.load_result(msg)
244 return self._load_result(x)
245
247 try:
248 msg['auth'] = msg['auth'].__dict__
249 except:
250 pass
251
255
260
265
270
272 """ An HTTP Server to manage SPyRO requests, attached to another HTTP server """
273
274 - def __init__(self, rfile, wfile, env, default_format = "MARSHAL"):
275 """ Perform connection using the opened socket |reqsocket|"""
276 self.format = None
277 self.default_format = default_format
278 self.broker = None
279 self.__closed = False
280 self.rfile = rfile
281 self.wfile = wfile
282 self.env = env
283
285 """ Read the request and return it as a Request object instance"""
286 self._choose_format(format, self.default_format)
287 try:
288 x = self.rfile.read(int(self.env['CONTENT_LENGTH']))
289 except:
290 if self.env['REQUEST_METHOD'] == 'POST':
291 x = self.rfile.read()
292 else:
293 x = ''
294 x = self.broker.load_request(x)
295 return self._load_request(x)
296
300
302 """ Encode and send the message (msg is a Response object) """
303 x = self.broker.dump_result(msg)
304 self.send_data(x)
305
306
307
308
309
310
311
313 """ Close the server """
314 if not self.__closed:
315 self.__closed = True
316
318 if not self.__closed: self.close()
319
320
321
322
323
324
325
327 """ Object to request to the remote object server
328
329 Values of the variables:
330 # Say bye
331 ReqBye = 'BYE'
332 # Request to Get Remote Attr
333 ReqGetAttr = 'GET'
334 # Set an attribute
335 ReqSetAttr = 'SET'
336 # Del an object
337 ReqDelObj = 'DEL'
338 # Request to Call a remote method
339 ReqCallMethod = 'CALL'
340 """
341
342 ReqBye = 'BYE'
343
344 ReqGetAttr = 'GET'
345
346 ReqSetAttr = 'SET'
347
348 ReqDelObj = 'DEL'
349
350 ReqCallMethod = 'CALL'
351
353 return {'objname' : objname, 'attrname' : attrname, 'reqtype' : reqtype, 'auth' : auth}
354
356 """ Creates a new Request for a |setattr| operation, to set the
357 attrname to value"""
358 req = newRequest(objname, attrname, Request.ReqSetAttr, auth)
359 req['value'] = value
360 req['local_server'] = local_server
361 return req
362
364 """ Creates a new Request for a |getattr| operation """
365 req = newRequest(objname, attrname, Request.ReqGetAttr, auth)
366 req['rettype'] = rettype
367 return req
368
372
374 """ Creates a new Request for a BYE object operation """
375 return newRequest(None, None, Request.ReqBye, None)
376
377 -def newRequestCall(objname, attrname, args, kwargs, rettype, local_server,
378 auth, extra):
379 """ A SPyRO request, call mode
380 |objname| Object name
381 |attrname| The methodname
382 |args| Positional arguments, tuple
383 |kwargs| Keyword arguments, dictionary
384 |rettype| The return type, SendByReference or SendByValue
385 |local_server| If Request to call a remote method and exists port we call
386 the method with the pass the arguments by reference the server receives the
387 name or id in the remote peer (the caller) and a port to connect. The IP
388 address is obtained by the socket
389 |auth| The authentication object
390 |extra| Additional info to append to message, for example routed calls
391 """
392 req = newRequest(objname, attrname, Request.ReqCallMethod, auth)
393 req['args'] = args
394 req['kwargs'] = kwargs
395 req['rettype'] = rettype
396 req['local_server'] = local_server
397 if extra:
398 req['extra'] = extra
399 return req
400
401
402
403
404
405
406
414
416 """ Wrap objects to send objects to Reference """
417 xobjname = '!' + str(id(xobject))
418 xserver.setsend(xobjname, xobject, xauthority)
419 return (xobjname, SendByReference)
420
422 """ Wrap objects to send objects to Reference """
423 return (xobject, SendByValue)
424
427
430
433
435 """ Create and returns a server side response from the arguments """
436 return {'errcode': errcode, 'retvalue' : retvalue, 'rettype': rettype}
437
438
440 """ Objects to send as response at requests.
441 The request codes are the following:
442 Successful = 0
443 # Unknown Object, no object registered to share in server, retvalue is None
444 ErrorUnknownObject = 1
445 # Object doesn't have specified attribute, retvalue = None
446 ErrorUnknownAttribute = 2 # NO USAR
447 # An Exception Throw, exception is returned in retvalue
448 ErrorExceptionThrowed = 3
449 # An error on protocol
450 ErrorUnknownRequestType = 4
451 # The object is callable, we don't wanna send it, we want to call it
452 ObjectIsCallable = 5
453 # The object or the method is not accesible with the authentication or is not valid
454 ErrorInTheAuthentication = 6
455 """
456
457 Successful = 0
458
459 ErrorUnknownObject = 1
460
461 ErrorUnknownAttribute = 2
462
463 ErrorExceptionThrowed = 3
464
465 ErrorUnknownRequestType = 4
466
467 ObjectIsCallable = 5
468
469 ErrorInTheAuthentication = 6
470
471 ErrorNames = ['Successful', 'ErrorUnknownObject','ErrorUnknownAttribute',
472 'ErrorExceptionThrowed', 'ErrorUnknownRequestType','ObjectIsCallable','ErrorInTheAuthentication']
473
474 - def __init__(self, errcode = None, retvalue = None, rettype = None):
475 """ Initialize the response with an error code |errcode| and the
476 return value |retvalue|"""
477 self.errcode = errcode
478 self.retvalue = retvalue
479 self.rettype = rettype
480
484
486 """ Returns the returned value of calling |methodname| with arguments
487 |args|. Can raise remote exceptions or locals due to remote errors """
488
489
490 self.attrname = self.retvalue[1]
491
492 self.comm,self.objname,self.objauth = remoteobj.__xxxconnxxx__
493
494 remoteobj.__dict__[self.attrname] = self.__call__
495
497 """ Internal function, this is the real __call__ method """
498
499
500
501
502 return self.comm.callMethodGuess(self.objname, self.attrname,
503 args, kwargs, SendByValue,
504 self.comm.local_server,
505 auth = self.objauth)
506
507
509 """ The base of spyro's communication servers """
512 """ |address| The address where the Server is listening
513 |location| Location or path in the web server
514 |priorities| Priorities of the protocols
515 |defaultFormat| The default message format where none is
516 specified
517 |transportProtocol| The class to manage the transport layer
518 |objectpool| The object pool manager , if None then
519 a new SeverObjectPool instance is used
520 """
521 self.server_address = address
522 self.transportProtocol = transportProtocol
523 self.priorities = priorities
524 if defaultFormat is None: defaultFormat = ''
525 self.defaultFormat = defaultFormat
526 self.location = location
527
528
529 self.commclients = {}
530
531 self.lockconn = Lock()
532 if not objectpool:
533 objectpool = ServerObjectPool()
534 self.setobj = objectpool.setobj
535 self.getobj = objectpool.getobj
536 self.delobj = objectpool.delobj
537 self.setsend = objectpool.setsend
538
540 """ To pickle functions """
541 return {}
542
544 """ Register a client connection """
545
546
547
548 pass
549
551 """ Removes a client connection """
552 pass
553
554
555
556
558 """Register an object. """
559 self.setobj(k,v)
560
562 """ Register an object |v| with the ID |k|. Use |setobj| instead"""
563 warn.stack("This method is deprecated")
564 raise "This method is deprecated"
565
567 """ Close server """
568 pass
569
570
571
572
574 """ Returns the CommunicationClient associated to the address that
575 made the |request| through the |serv| """
576
577 addr=(local_server[2],local_server[0])
578 try:
579 comm = self.commclients[addr]
580 except KeyError:
581 comm = CommunicationClientThreaded(addr, local_server[1], local_server=self,poolSize=5)
582 self.commclients[addr] = comm
583 return comm
584
585
586
588 """ Get the argument asociated to |arg| using the |comm|
589 It manages references and values """
590 argobj, argtype = ObjectWrapper_Unpack(arg)
591 if argtype == SendByReference:
592 return comm[argobj]
593 else:
594 return argobj
595
596 - def handlecall(self, obj, atr, objname, methodname, args, kwargs, local_server):
597 """ handles the execution of methods. called by handle.
598 If the object has the attribute __spyro__ it must be an object
599 with the |__contains__| method and every element is a method
600 that must be handled by pass (call methods with the request
601 method as the first argument, other arguments are passed as normal)
602
603 If the object has the attribute '__spyro_formats__' it must be a
604 dictionary (object with the __getitem__ method) of { methodname: argumentformats }
605
606 Where |methodname| must be the name of methods where the arguments need to be checked
607 |argumentformats| is the format as listed in the SPyRO.args module
608 """
609
610 try:
611 hasspyro = methodname in getattr(obj,'__spyro__')
612 except AttributeError:
613 hasspyro = False
614 try:
615 fmt_args = getattr(obj,'__spyro_formats__')
616 fmtcomp = None
617 try:
618 comp = fmt_args['!compiled!']
619
620 try:
621 fmtcomp = comp[methodname]
622 except (KeyError, AttributeError):
623 fmtcomp = None
624 except KeyError:
625 fmt_args['!compiled!'] = {}
626 if fmtcomp is None:
627 try:
628 fmtcomp = Args.Arguments(fmt_args[methodname])
629 fmt_args['!compiled!'][methodname] = fmtcomp
630 except KeyError:
631 fmtcomp = None
632 if not(fmtcomp is None):
633 fmtcomp.check(args, kwargs)
634 except AttributeError:
635 pass
636 if hasspyro:
637 warn.debug("A new api to __spyro__ was developed... check your implementation please")
638 return atr(self, objname, attrname, args, kwargs)
639 if local_server:
640 comm = self.getcomm(local_server)
641 args = list(args)
642 for index, arg in enumerate(args):
643 args[index] = self.getargument(comm, arg)
644
645 for k,v in kwargs.items():
646 kwargs[k] = self.getargument(comm, v)
647 if not (type(kwargs) is DictType): kwargs = {}
648 return atr(*args, **kwargs)
649
651 if authobj == None: return True
652 if authobj.can_del(req): return True
653 raise AuthError, "Can't del object %(objname)s permission denied"%req
654
656 if authobj == None: return True
657 if authobj.can_set(req): return True
658 raise AuthError, "Can't set attr '%(attrname)s' in the object '%(objname)s'"%req
659
661 if authobj == None: return True
662 if authobj.can_follow_objname(req): return True
663 raise AuthError, "Can't follow using dot names in the object '%(objname)s'"%req
664
666 if authobj == None: return True
667 if authobj.can_get(req): return True
668 raise AuthError, "Can't get attr '%(attrname)s' in the object '%(objname)s'"%req
669
671 if authobj == None: return True
672 if authobj.can_call(req): return True
673 raise AuthError, "Can't call in the object '%(objname)s'"%req
674
676 if authobj == None: return True
677 if authobj.can_register(req): return True
678 raise AuthError, "Can't register objects, permission denied"
679
680 - def handle(self, rfile, wfile, environment, httprequest, overture=None, request_rewrite=None):
681 """ Handles an spyro request reading the request from rfile, using the broker specified in environment['QUERY_STRING']
682 It writes data using wfile and the broker.
683 |rfile| Read file
684 |wfile| Write file
685 |environment| Environment of the Request
686 |httprequest| http request object
687 Optional arguments:
688 |overture| Overture to be executed before anything. If overture throws an exception
689 solve_request will not be executed. Prototype
690 overture(httprequest,environment)
691 |request_rewrite| If it's different of None it will be executed after the read of the
692 request and the generated dictionary will be sent and perhaps modified.
693 Prototype
694 request_rewrite(requestdict,httprequest,environment)
695 """
696 if overture: overture(httprequest, environment)
697
698
699
700 broker = self.transportProtocol(rfile, wfile, environment)
701 self.regconn(self)
702 req = broker.get_request(environment['QUERY_STRING'])
703 try:
704 local_server = req['local_server']
705 if local_server is None: raise KeyError
706 req['local_server'] = (local_server[0], local_server[1], httprequest.request.getpeername()[0])
707 except KeyError:
708 pass
709 if request_rewrite: request_rewrite(req, self, environment)
710 res = self.handle_request(**req)
711 broker.send_result( res )
712 broker.close()
713
715 try:
716 res = self._handle_request(*args, **kwargs)
717 except AuthError, error:
718 res = newResponse(Response.ErrorInTheAuthentication,str(error),SendByValue)
719 except (Exception, types.StringType, types.UnicodeType, types.NoneType, BooleanType,
720 types.LongType, types.FloatType, types.TupleType, types.ListType, types.DictType):
721 etype, evalue, etraceback = sys.exc_info()
722 error = "%s %s" % (etype, evalue)
723 warn.warn(error)
724 res = newResponse(Response.ErrorExceptionThrowed,str(error),SendByValue)
725 except:
726 _delrequest()
727 raise
728 _delrequest()
729 return res
730
731 - def _handle_request(self, objname, attrname, args = (), kwargs = {}, auth = None, extra = None, local_server = None,
732 reqtype=Request.ReqCallMethod, rettype=SendByValue, value=None, req=None):
733 if req is None: req = {'objname' : objname, 'attrname' : attrname, 'args' : args, 'kwargs' : kwargs,
734 'auth' : auth, 'local_server' : local_server, 'reqtype' : reqtype, 'rettype' : rettype,
735 'value' : value, 'req' : None
736 }
737 _setrequest(req)
738
739
740
741 if Request.ReqBye == reqtype:
742 return newResponse(Response.Successful, None, SendByValue)
743
744
745
746 try:
747 obj, authobj = self.getobj(objname)
748 except KeyError:
749 try:
750
751 objpath = objname.split('.')
752 obj, authobj = self.getobj(objpath.pop(0))
753 if not self.can_follow_objname(authobj, req):
754 raise AttributeError, "UnknownObject because: Forbidden access to object's attribute"
755 for oname in objpath:
756 obj = getattr(obj, oname)
757 except AttributeError:
758 return newResponse(Response.ErrorUnknownObject, 'Unknown Object: %s'%repr(objname), SendByValue)
759 req['obj'] = obj
760
761
762
763 if Request.ReqDelObj == reqtype and self.can_del(authobj, req):
764 if objname == '!':
765 warn.debug("-DEL-OBJ-", objname)
766
767 return newResponse(Response.Successful, '', SendByValue)
768
769
770
771 if Request.ReqSetAttr == reqtype and self.can_set(authobj, req):
772 if local_server != None:
773 value=self.getargument(self.getcomm(local_server), value)
774 setattr(obj, attrname, value)
775 return newResponse(Response.Successful, None, SendByValue)
776 val=None
777
778
779
780 atr=getattr(obj, attrname)
781 if Request.ReqGetAttr == reqtype and self.can_get(authobj, req):
782 if callable(atr):
783 return newResponse(Response.ObjectIsCallable,(objname,attrname), SendByValue)
784
785 val = atr
786
787
788
789 elif Request.ReqCallMethod == reqtype and self.can_call(authobj, req):
790 val = self.handlecall(obj, atr, objname, attrname, args, kwargs, local_server)
791 else:
792 return newResponse(Response.ErrorUnknownRequestType, 'Unknown Request Type: %s'%repr(reqtype))
793 if rettype == SendByReference and self.can_register(authobj, req):
794 res = newResponse(Response.Successful, self.setsend("?%s"%id(val),val), SendByReference)
795 else:
796 res = newResponse(Response.Successful, val, SendByValue)
797 return res
798
800 - def __init__(self, address,
801 location = "/SPyRO?XMLRPC",
802 priorities = None,
803 defaultFormat = None,
804 overture = defaultoverture,
805 objectpool = None,
806 protocolServer = ProtocolHTTPShared):
807 """ |address| The address where the Server is listening
808 |location| Location or path in the web server
809 |priorities| Priorities of the protocols
810 |defaultFormat| The default message format where none is
811 specified
812 |overture| A function to be called before anything in the connection process
813 See PeerConnection.solve_request (the overture argument) to more information
814 If it's None, the overture will be skipped.
815 |objectpool| The object pool that dispatches the connections
816 |protocolServer| The class of the Procotol used in the Server side, by default
817 ProtocolHTTPShared is Used
818 """
819 BasicServer.__init__(self, address, location, priorities,
820 defaultFormat, protocolServer,
821 objectpool = objectpool, overture = overture)
822
824
825 self.handle(httpreq.rfile, httpreq.wfile, environment, httpreq, overture = self.overture)
826
828 """ Starts a standalone SPyRO Server """
829 - def __init__(self, address, location = "/SPyRO?XMLRPC",
830 priorities = None, defaultFormat = None,
831 objectpool = None,
832 protocolServer = ProtocolHTTPShared,
833 overture = defaultoverture,
834 **httpoptions):
835 """ |address| The address where the Server is listening
836 |location| Location or path in the web server
837 |priorities| Priorities of the protocols
838 |defaultFormat| The default message format when None is
839 specified
840 |overture| A function to be called before anything in the connection process
841 See PeerConnection.solve_request (the overture argument) to more information
842 If it's None, the overture will be skipped.
843 |protocolServer| The class of the Procotol used in the Server side, by default
844 ProtocolHTTPShared is Used
845 """
846
847 BasicServer.__init__(self, address, location, priorities,
848 defaultFormat, protocolServer,
849 objectpool = objectpool,
850 overture = overture)
851 self.httpserver = http.HTTPServer(address, **httpoptions)
852 self.httpserver.register_urihandler(location.split('?')[0], self.request_handler)
853
856
858 """ SPyRO Handler """
859 self.handle(httpreq.rfile, httpreq.wfile, environment, httpreq)
860
861
862
863
865 self.httpserver.close()
866
867 ServerShare = CommunicationServerShared
868 Server = CommunicationServer
869
870
871
872
873
874
875
877 """ The default, fool, error handler in Client connections """
878 errno, errstr = error
879 if errno == 111:
880 seconds = sleep_min_sec
881 connected = False
882 while True:
883 try:
884 _socket.connect(addr)
885 break
886 except socket.error, _error:
887 if _error[0] != 111:
888 default_socket_error_handler(comm, _socket, addr, _error)
889 break
890
891
892 warn.debug("SPyRO Client to %s: %s"%(addr, error),
893 "trying in %f seconds"%(seconds))
894 time.sleep(seconds)
895 if seconds > sleep_max_sec:
896 seconds = sleep_min_sec
897 else:
898 seconds = seconds + seconds
899 else:
900 warn.warn(error)
901 raise error
902 warn.debug("Done")
903
905 """ The mixin to CommunicationClient{,Threaded}"""
906 - def get(self, name, auth = None):
907 """ Get a local representation of the remote object with ID |name|. Deprecated. Use |getobj| instead"""
908 warn.stack("This is not an error, but this method is deprecated")
909 return RemoteObject(self, name, auth)
910
912 """ Get a local representation of the remote object with ID |name|."""
913 return RemoteObject(self, name, None)
914
915 - def getobj(self, name, auth = None):
916 """ Get a local representation of the remote object with ID |name|"""
917 return RemoteObject(self, name, auth)
918
920 """ Get a communication object """
921 return self
922
923
925 """ Return a communication Object """
926 pass
927
928
962
965 """ Get an attribute |attrname| of the object |objname| with the method
966 to send the object |rettype|, the default is SendByValue
967 |auth| The authorizing information to access the object
968 """
969 request=newRequestGet(objname, attrname, rettype, auth)
970 conn = self.getconn()
971 return self.handleRequest(conn, request, conn.broker.send_getAttr).retvalue
972
973 - def setAttr(self, objname, attrname, value, sendtype=SendByValue,
974 local_server = None, auth = None):
992
997
999 """ Get an attribute |attrname| of the object |objname| with the method
1000 to send the object |rettype|, the default is SendByValue.
1001 |auth| The authorizing information to access the object
1002 """
1003 request=newRequestDel(objname, auth)
1004 conn = self.getconn()
1005 return self.handleRequest(conn, request, conn.broker.send_control).retvalue
1006
1007 - def callMethod(self, objname, attrname, args = (), kwargs = {},
1008 rettype = SendByValue, local_server = None, auth = None, extra = None):
1009 """ Call a method. Every argument must be a ObjectWrapper
1010 with all the necessary information.
1011 |objname| The object's name that has the method
1012 |attrname| The name of the object
1013 |args| The positional arguments of the call
1014 |kwargs| The keyword arguments of the call
1015 |rettype| The return type of the result
1016 |local_server| the local server to send arguments by reference
1017 |auth| The authorizing information to access the object
1018 |extra| Extra general information to be passed in the message
1019 """
1020 request = newRequestCall(objname, attrname, args, kwargs,
1021 rettype, local_server, auth, extra)
1022 conn = self.getconn()
1023 return self.handleRequest(conn, request, conn.broker.send_callMethod).retvalue
1024
1026 """ Return a local_server tuple to make the request """
1027 return (local_server.server_address[1],local_server.location)
1028
1029 - def callMethodGuess(self, objname, attrname, args = (), kwargs = {},
1030 rettype = SendByValue, local_server = None,
1031 auth = None, extra = None):
1032 """ Call a method, Guessing the best type to send arguments
1033 |objname| The object's name that has the method
1034 |attrname| The name of the object
1035 |args| The positional arguments of the call
1036 |kwargs| The keyword arguments of the call
1037 |rettype| The return type of the result
1038 |local_server| the local server to send arguments by reference
1039 |auth| The authorizing information to access the object
1040 |extra| Additional information to be added to the message
1041 """
1042 if local_server:
1043 warn.debug("Objects references as arguments, GC problem")
1044 xargs = []
1045 for arg in args:
1046 xargs.append(ObjectWrapper_GuessType(arg, local_server))
1047 args = tuple(xargs)
1048 for k,v in kwargs.items():
1049 kwargs[k] = ObjectWrapper_GuessType(v, local_server)
1050 local_server=self.get_request_local_server(local_server)
1051 return self.callMethod(objname, attrname, args, kwargs, rettype,
1052 local_server, auth = auth, extra = extra)
1053
1054
1056 """ A base class to any CommunicationServer client. Represents a
1057 communication channel
1058 with the server """
1062 """ Initialize client.
1063 |serv_addr| The remote server address (host,port)
1064 |path| Location of the server (to apache or another general purpose
1065 http servers). It describes the encoder protocol using the
1066 query_string '?' notation.
1067 |local_server| If is set to a CommunicationServer, when a method is
1068 called the arguments are passed by reference and the remote
1069 server
1070 will try to connect to this server (port and location).
1071 The IP address is the used to contact the remote server.
1072 |socket_error_handler| The error handler, called when an exception
1073 is arised in the connection, parsing, or interpreting messages.
1074 |compress| Compress the messages between peers
1075 |usingssl| If True, every connection is performed with the https protocol
1076 """
1077 self.socket_error_handler = socket_error_handler
1078 self.path = path
1079 self.local_server = local_server
1080 self.__closed = False
1081 self.broker = ProtocolHTTPReader(serv_addr, path, compress, usingssl = usingssl)
1082 self._do_bye = True
1083
1085 """ To pickle functions """
1086 return {'path': self.path }
1087
1089 """ Close the client """
1090 self.__closed = True
1091 if self._do_bye:
1092 self.byeRequest()
1093 self.broker.close()
1094
1096 """ called for the garbage collection system. Close the object
1097 if it is not closed yet. """
1098 if not self.__closed: self.close()
1099
1101 """ A Client to CommunicationServer. Represents a communication channel
1102 with the server """
1107 CommunicationClientBase.__init__(self, serv_addr, path, local_server,
1108 socket_error_handler,
1109 compress = compress,
1110 usingssl = usingssl)
1111 self.connlock = Lock()
1112
1116
1118 self.connlock.acquire()
1119 return self
1120
1122 self.connlock.release()
1123
1124 Client = CommunicationClient
1125
1127 """ The thread safe CommunicationClient. It uses several
1128 CommunicationClient to manage threading """
1133 """ Create the threaded client.
1134 |serv_addr| The server address (host,port).
1135 |path| The path where is listening the remote host, it has the
1136 protocol using query_string '?' notation.
1137 |poolSize| The maximum size of the pool of CommunicationClient objects
1138 |local_server| If is set to a CommunicationServer, when a method is
1139 called the arguments are passed by reference and the remote server
1140 will try to connect to this server (port and location).
1141 The IP address is the used to contact the remote server.
1142 |socket_error_handler| The socket error handler.
1143 |compress| If True it uses gzip compression to content
1144 |usingssl| If True it uses the https protocol to perform connections
1145 """
1146 self.queue = Queue.Queue(poolSize)
1147 self.path = path
1148 self.local_server = local_server
1149 self.ehandler = socket_error_handler
1150 self.address = serv_addr
1151 self.__closed = False
1152 self.compress = compress
1153 self.usingssl = usingssl
1154
1156 """ To pickle functions """
1157 return {'path': self.path, 'address': self.address}
1158
1160 try:
1161 comm=self.queue.get_nowait()
1162 except Queue.Empty:
1163 comm=CommunicationClientBase(self.address, self.path,
1164 self.local_server,
1165 socket_error_handler = self.ehandler,
1166 compress = self.compress,
1167 usingssl = self.usingssl)
1168 return comm
1169
1171 try:
1172 self.queue.put(conn)
1173 except Queue.Full:
1174 pass
1175
1177 if not self.__closed: self.close()
1178
1180 while not self.__closed:
1181 try:
1182 comm = self.queue.get_nowait()
1183 comm.close()
1184 except Queue.Empty:
1185 break
1186 self.__closed = True
1187
1188 TClient = CommunicationClientThreaded
1189
1191 """ Special Client thay always call methods, no attribute operations are allowed"""
1194
1195
1197 """ Special TClient to always call methods, no attribute operations"""
1200
1201
1202
1203
1204
1205
1206
1208 """ FakeIterator """
1210 self.obj = obj
1211 self.pos = -1
1212 self.len = len(obj) - 1
1213
1215 if self.pos < self.len:
1216 self.pos = self.pos + 1
1217 return self.obj[self.pos]
1218 else:
1219 raise StopIteration
1220
1223
1224
1226 """ Local representation of a RemoteObject. The remote object needs to be
1227 registered in the server. """
1228 - def __init__(self, connection, objectname, auth):
1229 self.__dict__['__xxxconnxxx__'] = (connection, objectname, auth)
1230
1233
1235 """ Returns the remote attribute named |attribute| as local object.
1236 Can raise remote exceptions or locals due remote errors """
1237 if attrname == '__iter__':
1238
1239 return FakeIterator(self)
1240 conn,objname,auth = self.__xxxconnxxx__
1241 try:
1242 return conn.getAttr(objname, attrname, SendByValue,
1243 auth = auth)
1244 except AttributeIsCallable, response:
1245 r = response.args[0]
1246 r.prepareToCall(self)
1247 return r
1248
1250 conn, objname, auth = self.__xxxconnxxx__
1251 if objname[0] == '!':
1252 warn.debug("-USR-DEL-OBJ-",objname)
1253 conn.delObject(objname, auth)
1254
1256 """ __setattr__('name', value) <==> x.name = value. Where |x| is
1257 a remote object """
1258 conn,objname, auth = self.__xxxconnxxx__
1259 return conn.setAttr(objname, attrname, value, SendByValue, None,
1260 auth = auth)
1261
1263 """ Internal representation """
1264 return "<SPyRO.RemoteObject name: %s, local id: %s>"%(getobjname(self),
1265 hex(id(self)))
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277 -def getobject(address, location, name, local_server = None, poolSize=0):
1278 """ Can be used as a shortcut when only we need one object in address """
1279 comm=CommunicationClientThreaded(address, location, local_server, poolSize)
1280 return comm.get(name)
1281
1283 """ Return the connection of the remote object"""
1284 return remoteobject.__xxxconnxxx__[0]
1285
1287 """ Returns the name of the object """
1288 return remoteobject.__xxxconnxxx__[1]
1289