client.py 3.92 KB
Newer Older
Guido van Rossum's avatar
Guido van Rossum committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
"""RPC Client module."""

import sys
import socket
import pickle
import __builtin__
import os


# Default verbosity (0 = silent, 1 = print connections, 2 = print requests too)
VERBOSE = 1


class Client:
	
	"""RPC Client class.  No need to derive a class -- it's fully generic."""
	
	def __init__(self, address, verbose = VERBOSE):
Guido van Rossum's avatar
Guido van Rossum committed
19 20 21 22
		self._pre_init(address, verbose)
		self._post_init()
	
	def _pre_init(self, address, verbose = VERBOSE):
Guido van Rossum's avatar
Guido van Rossum committed
23 24 25 26 27 28 29 30 31 32 33 34 35
		if type(address) == type(0):
			address = ('', address)
		self._address = address
		self._verbose = verbose
		if self._verbose: print "Connecting to %s ..." % repr(address)
		self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		self._socket.connect(address)
		if self._verbose: print "Connected."
		self._lastid = 0 # Last id for which a reply has been received
		self._nextid = 1 # Id of next request
		self._replies = {} # Unprocessed replies
		self._rf = self._socket.makefile('r')
		self._wf = self._socket.makefile('w')
Guido van Rossum's avatar
Guido van Rossum committed
36 37
	
	def _post_init(self):
Guido van Rossum's avatar
Guido van Rossum committed
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
		self._methods = self._call('.methods')
	
	def __del__(self):
		self._close()
	
	def _close(self):
		if self._rf: self._rf.close()
		self._rf = None
		if self._wf: self._wf.close()
		self._wf = None
		if self._socket: self._socket.close()
		self._socket = None
	
	def __getattr__(self, name):
		if name in self._methods:
			method = _stub(self, name)
			setattr(self, name, method) # XXX circular reference
			return method
		raise AttributeError, name
	
	def _setverbose(self, verbose):
		self._verbose = verbose
	
	def _call(self, name, *args):
		return self._vcall(name, args)
	
	def _vcall(self, name, args):
		return self._recv(self._vsend(name, args))
	
	def _send(self, name, *args):
		return self._vsend(name, args)
	
	def _send_noreply(self, name, *args):
		return self._vsend(name, args, 0)
	
	def _vsend_noreply(self, name, args):
		return self._vsend(name, args, 0)
	
	def _vsend(self, name, args, wantreply = 1):
		id = self._nextid
		self._nextid = id+1
		if not wantreply: id = -id
		request = (name, args, id)
		if self._verbose > 1: print "sending request: %s" % repr(request)
		wp = pickle.Pickler(self._wf)
		wp.dump(request)
		return id
	
	def _recv(self, id):
		exception, value, rid = self._vrecv(id)
		if rid != id:
			raise RuntimeError, "request/reply id mismatch: %d/%d" % (id, rid)
		if exception is None:
			return value
		x = exception
		if hasattr(__builtin__, exception):
			x = getattr(__builtin__, exception)
		elif exception in ('posix.error', 'mac.error'):
			x = os.error
		if x == exception:
			exception = x
		raise exception, value		
	
	def _vrecv(self, id):
		self._flush()
		if self._replies.has_key(id):
			if self._verbose > 1: print "retrieving previous reply, id = %d" % id
			reply = self._replies[id]
			del self._replies[id]
			return reply
		aid = abs(id)
		while 1:
			if self._verbose > 1: print "waiting for reply, id = %d" % id
			rp = pickle.Unpickler(self._rf)
			reply = rp.load()
			del rp
			if self._verbose > 1: print "got reply: %s" % repr(reply)
			rid = reply[2]
			arid = abs(rid)
			if arid == aid:
				if self._verbose > 1: print "got it"
				return reply
			self._replies[rid] = reply
			if arid > aid:
				if self._verbose > 1: print "got higher id, assume all ok"
				return (None, None, id)
	
	def _flush(self):
		self._wf.flush()


Guido van Rossum's avatar
Guido van Rossum committed
129 130 131 132 133 134 135
from security import Security


class SecureClient(Client, Security):

	def __init__(self, *args):
		import string
Guido van Rossum's avatar
Guido van Rossum committed
136
		apply(self._pre_init, args)
Guido van Rossum's avatar
Guido van Rossum committed
137
		Security.__init__(self)
138
		self._wf.flush()
Guido van Rossum's avatar
Guido van Rossum committed
139
		line = self._rf.readline()
Guido van Rossum's avatar
Guido van Rossum committed
140
		challenge = string.atoi(string.strip(line))
Guido van Rossum's avatar
Guido van Rossum committed
141 142 143 144 145
		response = self._encode_challenge(challenge)
		line = `long(response)`
		if line[-1] in 'Ll': line = line[:-1]
		self._wf.write(line + '\n')
		self._wf.flush()
Guido van Rossum's avatar
Guido van Rossum committed
146
		self._post_init()
Guido van Rossum's avatar
Guido van Rossum committed
147

Guido van Rossum's avatar
Guido van Rossum committed
148 149 150 151 152 153 154 155 156 157 158
class _stub:
	
	"""Helper class for Client -- each instance serves as a method of the client."""
	
	def __init__(self, client, name):
		self._client = client
		self._name = name
	
	def __call__(self, *args):
		return self._client._vcall(self._name, args)