mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	Added server classes, and various robustness hacks
This commit is contained in:
		
							parent
							
								
									2eed1e796e
								
							
						
					
					
						commit
						424c673d2f
					
				
					 1 changed files with 244 additions and 0 deletions
				
			
		
							
								
								
									
										244
									
								
								Demo/rpc/rpc.py
									
										
									
									
									
								
							
							
						
						
									
										244
									
								
								Demo/rpc/rpc.py
									
										
									
									
									
								
							|  | @ -75,6 +75,11 @@ def pack_replyheader(self, xid, verf): | ||||||
| 		# Caller must add procedure-specific part of reply | 		# Caller must add procedure-specific part of reply | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | # Exceptions | ||||||
|  | BadRPCFormat = 'rpc.BadRPCFormat' | ||||||
|  | BadRPCVersion = 'rpc.BadRPCVersion' | ||||||
|  | GarbageArgs = 'rpc.GarbageArgs' | ||||||
|  | 
 | ||||||
| class Unpacker(xdr.Unpacker): | class Unpacker(xdr.Unpacker): | ||||||
| 
 | 
 | ||||||
| 	def unpack_auth(self): | 	def unpack_auth(self): | ||||||
|  | @ -82,6 +87,22 @@ def unpack_auth(self): | ||||||
| 		stuff = self.unpack_opaque() | 		stuff = self.unpack_opaque() | ||||||
| 		return (flavor, stuff) | 		return (flavor, stuff) | ||||||
| 
 | 
 | ||||||
|  | 	def unpack_callheader(self): | ||||||
|  | 		xid = self.unpack_uint(xid) | ||||||
|  | 		temp = self.unpack_enum() | ||||||
|  | 		if temp <> CALL: | ||||||
|  | 			raise BadRPCFormat, 'no CALL but ' + `temp` | ||||||
|  | 		temp = self.unpack_uint() | ||||||
|  | 		if temp <> RPCVERSION: | ||||||
|  | 			raise BadRPCVerspion, 'bad RPC version ' + `temp` | ||||||
|  | 		prog = self.unpack_uint() | ||||||
|  | 		vers = self.unpack_uint() | ||||||
|  | 		proc = self.unpack_uint() | ||||||
|  | 		cred = self.unpack_auth() | ||||||
|  | 		verf = self.unpack_auth() | ||||||
|  | 		return xid, prog, vers, proc, cred, verf | ||||||
|  | 		# Caller must add procedure-specific part of call | ||||||
|  | 
 | ||||||
| 	def unpack_replyheader(self): | 	def unpack_replyheader(self): | ||||||
| 		xid = self.unpack_uint() | 		xid = self.unpack_uint() | ||||||
| 		mtype = self.unpack_enum() | 		mtype = self.unpack_enum() | ||||||
|  | @ -105,11 +126,17 @@ def unpack_replyheader(self): | ||||||
| 			  'Neither MSG_DENIED nor MSG_ACCEPTED: ' + `stat` | 			  'Neither MSG_DENIED nor MSG_ACCEPTED: ' + `stat` | ||||||
| 		verf = self.unpack_auth() | 		verf = self.unpack_auth() | ||||||
| 		stat = self.unpack_enum() | 		stat = self.unpack_enum() | ||||||
|  | 		if stat == PROG_UNAVAIL: | ||||||
|  | 			raise RuntimeError, 'call failed: PROG_UNAVAIL' | ||||||
| 		if stat == PROG_MISMATCH: | 		if stat == PROG_MISMATCH: | ||||||
| 			low = self.unpack_uint() | 			low = self.unpack_uint() | ||||||
| 			high = self.unpack_uint() | 			high = self.unpack_uint() | ||||||
| 			raise RuntimeError, \ | 			raise RuntimeError, \ | ||||||
| 				'call failed: PROG_MISMATCH: ' + `low, high` | 				'call failed: PROG_MISMATCH: ' + `low, high` | ||||||
|  | 		if stat == PROC_UNAVAIL: | ||||||
|  | 			raise RuntimeError, 'call failed: PROC_UNAVAIL' | ||||||
|  | 		if stat == GARBAGE_ARGS: | ||||||
|  | 			raise RuntimeError, 'call failed: GARBAGE_ARGS' | ||||||
| 		if stat <> SUCCESS: | 		if stat <> SUCCESS: | ||||||
| 			raise RuntimeError, 'call failed: ' + `stat` | 			raise RuntimeError, 'call failed: ' + `stat` | ||||||
| 		return xid, verf | 		return xid, verf | ||||||
|  | @ -193,6 +220,8 @@ def sendrecord(sock, record): | ||||||
| 
 | 
 | ||||||
| def recvfrag(sock): | def recvfrag(sock): | ||||||
| 	header = sock.recv(4) | 	header = sock.recv(4) | ||||||
|  | 	if len(header) < 4: | ||||||
|  | 		raise EOFError | ||||||
| 	x = long(ord(header[0]))<<24 | ord(header[1])<<16 | \ | 	x = long(ord(header[0]))<<24 | ord(header[1])<<16 | \ | ||||||
| 	    ord(header[2])<<8 | ord(header[3]) | 	    ord(header[2])<<8 | ord(header[3]) | ||||||
| 	last = ((x & 0x80000000) != 0) | 	last = ((x & 0x80000000) != 0) | ||||||
|  | @ -359,6 +388,22 @@ def addpackers(self): | ||||||
| 		self.packer = PortMapperPacker().init() | 		self.packer = PortMapperPacker().init() | ||||||
| 		self.unpacker = PortMapperUnpacker().init('') | 		self.unpacker = PortMapperUnpacker().init('') | ||||||
| 
 | 
 | ||||||
|  | 	def Set(self, mapping): | ||||||
|  | 		self.start_call(PMAPPROC_SET) | ||||||
|  | 		self.packer.pack_mapping(mapping) | ||||||
|  | 		self.do_call() | ||||||
|  | 		res = self.unpacker.unpack_uint() | ||||||
|  | 		self.end_call() | ||||||
|  | 		return res | ||||||
|  | 
 | ||||||
|  | 	def Unset(self, mapping): | ||||||
|  | 		self.start_call(PMAPPROC_UNSET) | ||||||
|  | 		self.packer.pack_mapping(mapping) | ||||||
|  | 		self.do_call() | ||||||
|  | 		res = self.unpacker.unpack_uint() | ||||||
|  | 		self.end_call() | ||||||
|  | 		return res | ||||||
|  | 
 | ||||||
| 	def Getport(self, mapping): | 	def Getport(self, mapping): | ||||||
| 		self.start_call(PMAPPROC_GETPORT) | 		self.start_call(PMAPPROC_GETPORT) | ||||||
| 		self.packer.pack_mapping(mapping) | 		self.packer.pack_mapping(mapping) | ||||||
|  | @ -394,6 +439,8 @@ class TCPClient(RawTCPClient): | ||||||
| 	def init(self, host, prog, vers): | 	def init(self, host, prog, vers): | ||||||
| 		pmap = TCPPortMapperClient().init(host) | 		pmap = TCPPortMapperClient().init(host) | ||||||
| 		port = pmap.Getport((prog, vers, IPPROTO_TCP, 0)) | 		port = pmap.Getport((prog, vers, IPPROTO_TCP, 0)) | ||||||
|  | 		if port == 0: | ||||||
|  | 			raise RuntimeError, 'program not registered' | ||||||
| 		pmap.close() | 		pmap.close() | ||||||
| 		return RawTCPClient.init(self, host, prog, vers, port) | 		return RawTCPClient.init(self, host, prog, vers, port) | ||||||
| 
 | 
 | ||||||
|  | @ -407,6 +454,156 @@ def init(self, host, prog, vers): | ||||||
| 		return RawUDPClient.init(self, host, prog, vers, port) | 		return RawUDPClient.init(self, host, prog, vers, port) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | # Server classes | ||||||
|  | 
 | ||||||
|  | class Server: | ||||||
|  | 
 | ||||||
|  | 	def init(self, host, prog, vers, port, type): | ||||||
|  | 		self.host = host # Should normally be '' for default interface | ||||||
|  | 		self.prog = prog | ||||||
|  | 		self.vers = vers | ||||||
|  | 		self.port = port # Should normally be 0 for random port | ||||||
|  | 		self.type = type # SOCK_STREAM or SOCK_DGRAM | ||||||
|  | 		self.sock = socket.socket(socket.AF_INET, type) | ||||||
|  | 		self.sock.bind((host, port)) | ||||||
|  | 		self.host, self.port = self.sock.getsockname() | ||||||
|  | 		self.addpackers() | ||||||
|  | 		return self | ||||||
|  | 
 | ||||||
|  | 	def register(self): | ||||||
|  | 		if self.type == socket.SOCK_STREAM: | ||||||
|  | 			type = IPPROTO_TCP | ||||||
|  | 		elif self.type == socket.SOCK_DGRAM: | ||||||
|  | 			type = IPPROTO_UDP | ||||||
|  | 		else: | ||||||
|  | 			raise ValueError, 'unknown protocol type' | ||||||
|  | 		mapping = self.prog, self.vers, type, self.port | ||||||
|  | 		p = TCPPortMapperClient().init(self.host) | ||||||
|  | 		if not p.Set(mapping): | ||||||
|  | 			raise RuntimeError, 'register failed' | ||||||
|  | 
 | ||||||
|  | 	def unregister(self): | ||||||
|  | 		if self.type == socket.SOCK_STREAM: | ||||||
|  | 			type = IPPROTO_TCP | ||||||
|  | 		elif self.type == socket.SOCK_DGRAM: | ||||||
|  | 			type = IPPROTO_UDP | ||||||
|  | 		else: | ||||||
|  | 			raise ValueError, 'unknown protocol type' | ||||||
|  | 		mapping = self.prog, self.vers, type, self.port | ||||||
|  | 		p = TCPPortMapperClient().init(self.host) | ||||||
|  | 		if not p.Unset(mapping): | ||||||
|  | 			raise RuntimeError, 'unregister failed' | ||||||
|  | 
 | ||||||
|  | 	def handle(self, call): | ||||||
|  | 		# Don't use unpack_header but parse the header piecewise | ||||||
|  | 		# XXX I have no idea if I am using the right error responses! | ||||||
|  | 		self.unpacker.reset(call) | ||||||
|  | 		self.packer.reset() | ||||||
|  | 		xid = self.unpacker.unpack_uint() | ||||||
|  | 		self.packer.pack_uint(xid) | ||||||
|  | 		temp = self.unpacker.unpack_enum() | ||||||
|  | 		if temp <> CALL: | ||||||
|  | 			return None # Not worthy of a reply | ||||||
|  | 		self.packer.pack_uint(REPLY) | ||||||
|  | 		temp = self.unpacker.unpack_uint() | ||||||
|  | 		if temp <> RPCVERSION: | ||||||
|  | 			self.packer.pack_uint(MSG_DENIED) | ||||||
|  | 			self.packer.pack_uint(RPC_MISMATCH) | ||||||
|  | 			self.packer.pack_uint(RPCVERSION) | ||||||
|  | 			self.packer.pack_uint(RPCVERSION) | ||||||
|  | 			return self.packer.get_buf() | ||||||
|  | 		self.packer.pack_uint(MSG_ACCEPTED) | ||||||
|  | 		self.packer.pack_auth((AUTH_NULL, make_auth_null())) | ||||||
|  | 		prog = self.unpacker.unpack_uint() | ||||||
|  | 		if prog <> self.prog: | ||||||
|  | 			self.packer.pack_uint(PROG_UNAVAIL) | ||||||
|  | 			return self.packer.get_buf() | ||||||
|  | 		vers = self.unpacker.unpack_uint() | ||||||
|  | 		if vers <> self.vers: | ||||||
|  | 			self.packer.pack_uint(PROG_MISMATCH) | ||||||
|  | 			self.packer.pack_uint(self.vers) | ||||||
|  | 			self.packer.pack_uint(self.vers) | ||||||
|  | 			return self.packer.get_buf() | ||||||
|  | 		proc = self.unpacker.unpack_uint() | ||||||
|  | 		methname = 'handle_' + `proc` | ||||||
|  | 		try: | ||||||
|  | 			meth = getattr(self, methname) | ||||||
|  | 		except AttributeError: | ||||||
|  | 			self.packer.pack_uint(PROC_UNAVAIL) | ||||||
|  | 			return self.packer.get_buf() | ||||||
|  | 		cred = self.unpacker.unpack_auth() | ||||||
|  | 		verf = self.unpacker.unpack_auth() | ||||||
|  | 		try: | ||||||
|  | 			meth() # Unpack args, call turn_around(), pack reply | ||||||
|  | 		except (EOFError, GarbageArgs): | ||||||
|  | 			# Too few or too many arguments | ||||||
|  | 			self.packer.reset() | ||||||
|  | 			self.packer.pack_uint(xid) | ||||||
|  | 			self.packer.pack_uint(REPLY) | ||||||
|  | 			self.packer.pack_uint(MSG_ACCEPTED) | ||||||
|  | 			self.packer.pack_auth((AUTH_NULL, make_auth_null())) | ||||||
|  | 			self.packer.pack_uint(GARBAGE_ARGS) | ||||||
|  | 		return self.packer.get_buf() | ||||||
|  | 
 | ||||||
|  | 	def turn_around(self): | ||||||
|  | 		try: | ||||||
|  | 			self.unpacker.done() | ||||||
|  | 		except RuntimeError: | ||||||
|  | 			raise GarbageArgs | ||||||
|  | 		self.packer.pack_uint(SUCCESS) | ||||||
|  | 
 | ||||||
|  | 	def handle_0(self): # Handle NULL message | ||||||
|  | 		self.turn_around() | ||||||
|  | 
 | ||||||
|  | 	# Functions that may be overridden by specific derived classes | ||||||
|  | 
 | ||||||
|  | 	def addpackers(self): | ||||||
|  | 		self.packer = Packer().init() | ||||||
|  | 		self.unpacker = Unpacker().init('') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class TCPServer(Server): | ||||||
|  | 
 | ||||||
|  | 	def init(self, host, prog, vers, port): | ||||||
|  | 		return Server.init(self, host, prog, vers, port, \ | ||||||
|  | 			socket.SOCK_STREAM) | ||||||
|  | 
 | ||||||
|  | 	def loop(self): | ||||||
|  | 		self.sock.listen(0) | ||||||
|  | 		while 1: | ||||||
|  | 			self.session(self.sock.accept()) | ||||||
|  | 
 | ||||||
|  | 	def session(self, connection): | ||||||
|  | 		sock, (host, port) = connection | ||||||
|  | 		while 1: | ||||||
|  | 			try: | ||||||
|  | 				call = recvrecord(sock) | ||||||
|  | 			except EOFError: | ||||||
|  | 				break | ||||||
|  | 			reply = self.handle(call) | ||||||
|  | 			if reply <> None: | ||||||
|  | 				sendrecord(sock, reply) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class UDPServer(Server): | ||||||
|  | 
 | ||||||
|  | 	def init(self, host, prog, vers, port): | ||||||
|  | 		return Server.init(self, host, prog, vers, port, \ | ||||||
|  | 			socket.SOCK_DGRAM) | ||||||
|  | 
 | ||||||
|  | 	def loop(self): | ||||||
|  | 		while 1: | ||||||
|  | 			session() | ||||||
|  | 
 | ||||||
|  | 	def session(self): | ||||||
|  | 		call, host_port = self.sock.recvfrom(8192) | ||||||
|  | 		reply = self.handle(call) | ||||||
|  | 		if reply <> None: | ||||||
|  | 			self.sock.sendto(reply, host_port) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Simple test program -- dump local portmapper status | ||||||
|  | 
 | ||||||
| def test(): | def test(): | ||||||
| 	import T | 	import T | ||||||
| 	T.TSTART() | 	T.TSTART() | ||||||
|  | @ -423,3 +620,50 @@ def test(): | ||||||
| 		elif prot == IPPROTO_UDP: print 'udp', | 		elif prot == IPPROTO_UDP: print 'udp', | ||||||
| 		else: print prot, | 		else: print prot, | ||||||
| 		print port | 		print port | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Server and client test program. | ||||||
|  | # On machine A: python -c 'import rpc; rpc.testsvr()' | ||||||
|  | # On machine B: python -c 'import rpc; rpc.testclt()' A | ||||||
|  | # (A may be == B) | ||||||
|  | 
 | ||||||
|  | def testsvr(): | ||||||
|  | 	# Simple test class -- proc 1 doubles its string argument as reply | ||||||
|  | 	class S(TCPServer): | ||||||
|  | 		def handle_1(self): | ||||||
|  | 			arg = self.unpacker.unpack_string() | ||||||
|  | 			self.turn_around() | ||||||
|  | 			print 'RPC function 1 called, arg', `arg` | ||||||
|  | 			self.packer.pack_string(arg + arg) | ||||||
|  | 	# | ||||||
|  | 	s = S().init('', 0x20000000, 1, 0) | ||||||
|  | 	try: | ||||||
|  | 		s.unregister() | ||||||
|  | 	except RuntimeError, msg: | ||||||
|  | 		print 'RuntimeError:', msg, '(ignored)' | ||||||
|  | 	s.register() | ||||||
|  | 	print 'Service started...' | ||||||
|  | 	try: | ||||||
|  | 		s.loop() | ||||||
|  | 	finally: | ||||||
|  | 		s.unregister() | ||||||
|  | 		print 'Service interrupted.' | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def testclt(): | ||||||
|  | 	import sys | ||||||
|  | 	if sys.argv[1:]: host = sys.argv[1] | ||||||
|  | 	else: host = '' | ||||||
|  | 	# Client for above server | ||||||
|  | 	class C(TCPClient): | ||||||
|  | 		def call_1(self, arg): | ||||||
|  | 			self.start_call(1) | ||||||
|  | 			self.packer.pack_string(arg) | ||||||
|  | 			self.do_call() | ||||||
|  | 			reply = self.unpacker.unpack_string() | ||||||
|  | 			self.end_call() | ||||||
|  | 			return reply | ||||||
|  | 	c = C().init(host, 0x20000000, 1) | ||||||
|  | 	print 'making call...' | ||||||
|  | 	reply = c.call_1('hello, world, ') | ||||||
|  | 	print 'call returned', `reply` | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Guido van Rossum
						Guido van Rossum