Index: fedd/fedd.py
===================================================================
--- fedd/fedd.py	(revision c922f23933185172b4e16710ef2d4751c92e6353)
+++ fedd/fedd.py	(revision 058f58e973a0a88bc6106dbaec3f9760e7823f79)
@@ -212,9 +212,10 @@
     def do_POST(self):
 	"""Treat an HTTP POST request as an XMLRPC service call"""
+	# NB: XMLRPC faults are not HTTP errors, so the code is always 200,
+	# unless an HTTP error occurs, which we don't handle.
 
 	resp = None
 	data = None
 	method = None
-	code = 200
 	cl = int(self.headers['content-length'])
 	data = self.rfile.read(cl)
@@ -225,5 +226,4 @@
 	    data = xmlrpclib.dumps(xmlrpclib.Fault("Client", 
 		"Malformed request"), methodresponse=True)
-	    code = 500
 
 	if method != None:
@@ -236,7 +236,6 @@
 		data = xmlrpclib.dumps(f, methodresponse=True)
 		resp = None
-		code = 500
-
-	self.send_xml(data, code)
+
+	self.send_xml(data)
 
     def log_request(self, code=0, size=0):
Index: fedd/fedd_access.py
===================================================================
--- fedd/fedd_access.py	(revision c922f23933185172b4e16710ef2d4751c92e6353)
+++ fedd/fedd_access.py	(revision 058f58e973a0a88bc6106dbaec3f9760e7823f79)
@@ -52,7 +52,8 @@
     id_list_attrs = ("restricted",)
 
-    proxy_request, proxy_xmlrpc_request = make_service_callers('RequestAccess', 
-	    'getfeddPortType', RequestAccessRequestMessage,
-	    'RequestAccessRequestBody')
+    proxy_RequestAccess= \
+	    service_caller('RequestAccess', 'getfeddPortType', 
+		    feddServiceLocator, RequestAccessRequestMessage,
+		    'RequestAccessRequestBody')
 
     def __init__(self, config=None):
@@ -153,9 +154,9 @@
 
 	self.soap_services = {\
-	    'RequestAccess': make_soap_handler(\
+	    'RequestAccess': soap_handler(\
 		RequestAccessRequestMessage.typecode,\
 		self.RequestAccess, RequestAccessResponseMessage,\
 		"RequestAccessResponseBody"), \
-	    'ReleaseAccess': make_soap_handler(\
+	    'ReleaseAccess': soap_handler(\
 		ReleaseAccessRequestMessage.typecode,\
 		self.ReleaseAccess, ReleaseAccessResponseMessage,\
@@ -163,7 +164,7 @@
 	    }
 	self.xmlrpc_services =  {\
-	    'RequestAccess': make_xmlrpc_handler(\
+	    'RequestAccess': xmlrpc_handler(\
 		self.RequestAccess, "RequestAccessResponseBody"),\
-	    'ReleaseAccess': make_xmlrpc_handler(\
+	    'ReleaseAccess': xmlrpc_handler(\
 		self.ReleaseAccess, "ReleaseAccessResponseBody")\
 	    }
@@ -698,41 +699,11 @@
 	    return resp
 	else:
-	    p_fault = None	# Any SOAP failure (sent unless XMLRPC works)
-	    try:
-		# Proxy the request using SOAP
-		self.log.debug("Sending proxy message to %s" % dt)
-		resp = self.proxy_request(dt, req, feddServiceLocator,
+	    resp = self.proxy_RequestAccess.call_service(dt, req,
 			self.proxy_cert_file, self.proxy_cert_pwd,
 			self.proxy_trusted_certs)
-		if resp.has_key('RequestAccessResponseBody'):
-		    return resp['RequestAccessResponseBody']
-		elif resp.has_key('Fedd_FaultBody'):
-		    raise service_error(resp['FeddFaultBody']['code'],
-			    resp['FeddFaultBody']['desc'])
-	    except service_error, e:
-		if e.code == service_error.proxy: p_fault = None
-		else: raise
-	    except ZSI.FaultException, f:
-		p_fault = f.fault.detail[0]
-		    
-
-	    # If we could not get a valid SOAP response to the request above,
-	    # try the same address using XMLRPC and let any faults flow back
-	    # out.
-	    if p_fault == None:
-		resp = self.proxy_xmlrpc_request(dt, req, self.proxy_cert_file,
-		    self.proxy_cert_pwd, self.proxy_trusted_certs)
-		if resp.has_key('RequestAccessResponseBody'):
-		    return resp['RequestAccessResponseBody']
+	    if resp.has_key('RequestAccessResponseBody'):
+		return resp['RequestAccessResponseBody']
 	    else:
-		# Build the fault
-		body = p_fault.get_element_FeddFaultBody()
-		if body != None:
-		    raise service_error(body.get_element_code(),
-				body.get_element_desc());
-		else:
-		    raise service_error(\
-			    service_error.proxy,
-			    "Undefined fault from proxy??");
+		return None
 
     def ReleaseAccess(self, req, fid):
Index: fedd/fedd_allocate_project.py
===================================================================
--- fedd/fedd_allocate_project.py	(revision c922f23933185172b4e16710ef2d4751c92e6353)
+++ fedd/fedd_allocate_project.py	(revision 058f58e973a0a88bc6106dbaec3f9760e7823f79)
@@ -74,13 +74,13 @@
 	# Internal services are SOAP only
 	self.soap_services = {\
-		"AllocateProject": make_soap_handler(\
+		"AllocateProject": soap_handler(\
 		AllocateProjectRequestMessage.typecode,\
 		self.dynamic_project, AllocateProjectResponseMessage,\
 		"AllocateProjectResponseBody"), 
-		"StaticProject": make_soap_handler(\
+		"StaticProject": soap_handler(\
 		StaticProjectRequestMessage.typecode,\
 		self.static_project, StaticProjectResponseMessage,\
 		"StaticProjectResponseBody"),\
-		"ReleaseProject": make_soap_handler(\
+		"ReleaseProject": soap_handler(\
 		ReleaseProjectRequestMessage.typecode,\
 		self.release_project, ReleaseProjectResponseMessage,\
@@ -345,74 +345,53 @@
 	return { 'project': req['ReleaseProjectRequestBody']['project']}
 
-def make_proxy(method, req_name, req_alloc, resp_name):
-    """
-    Construct the proxy calling function from the given parameters.
-    """
-
-    # Define the proxy, NB, the parameters to make_proxy are visible to the
-    # definition of proxy.
-    def proxy(self, req, fedid=None):
-	"""
-	Send req on to a remote project instantiator.
-
-	Req is just the message to be sent.  This function re-wraps it.
-	It also rethrows any faults.
-	"""
-
-	# No retry loop here.  Proxy servers must correctly authenticate
-	# themselves without help
-	try:
-	    ctx = fedd_ssl_context(self.cert_file, self.trusted_certs,
-		    password=self.cert_pwd)
-	except SSL.SSLError:
-	    raise service_error(service_error.server_config, 
-		    "Server certificates misconfigured")
-
-	loc = feddInternalServiceLocator();
-	port = loc.getfeddInternalPortType(self.url,
-		transport=M2Crypto.httpslib.HTTPSConnection, 
-		transdict={ 'ssl_context' : ctx })
-
-	if req.has_key(req_name):
-	    req = req[req_name]
-	else:
-	    raise service_error(service_error.req, "Bad formated request");
-
-	# Reconstruct the full request message
-	msg = req_alloc()
-	set_elem = getattr(msg, "set_element_%s" % req_name)
-	set_elem(pack_soap(msg, req_name, req))
-	try:
-	    mattr = getattr(port, method)
-	    resp = mattr(msg)
-	except ZSI.ParseException, e:
-	    raise service_error(service_error.proxy,
-		    "Bad format message (XMLRPC??): %s" % str(e))
-	except ZSI.FaultException, e:
-	    resp = e.fault.detail[0]
-
-	r = unpack_soap(resp)
-
-	if r.has_key(resp_name):
-	    return r[resp_name]
-	else:
-	    raise service_error(service_error.proxy, "Bad proxy response")
-    # NB: end of proxy function definition	
-    return proxy
-
 class fedd_allocate_project_remote:
     """
     Allocate projects on a remote machine using the internal SOAP interface
     """
-    dynamic_project = make_proxy("AllocateProject",
-	    "AllocateProjectRequestBody", AllocateProjectRequestMessage,
-	    "AllocateProjectResponseBody")
-    static_project = make_proxy("StaticProject",
-	    "StaticProjectRequestBody", StaticProjectRequestMessage,
-	    "StaticProjectResponseBody")
-    release_project = make_proxy("ReleaseProject",
-	    "ReleaseProjectRequestBody", ReleaseProjectRequestMessage,
-	    "ReleaseProjectResponseBody")
-
+    class proxy(service_caller):
+	"""
+	This class is a proxy functor (callable) that has the same signature as
+	a function called by soap_handler or xmlrpc_handler, but that used the
+	service_caller class to call the function remotely.
+	"""
+
+	def __init__(self, url, cert_file, cert_pwd, trusted_certs, 
+		method, req_name, req_alloc, resp_name):
+	    service_caller.__init__(self, method, 'getfeddInternalPortType',
+		    feddInternalServiceLocator, req_alloc, req_name)
+	    self.url = url
+	    self.cert_file = cert_file
+	    self.cert_pwd = cert_pwd
+	    self.trusted_certs = trusted_certs
+	    self.resp_name = resp_name
+	    # Calling the proxy object directly invokes the proxy_call method,
+	    # not the service_call method.
+	    self.__call__ = self.proxy_call
+	    
+
+	# Define the proxy, NB, the parameters to make_proxy are visible to the
+	# definition of proxy.
+	def proxy_call(self, req, fedid=None):
+	    """
+	    Send req on to a remote project instantiator.
+
+	    Req is just the message to be sent.  This function re-wraps it.
+	    It also rethrows any faults.
+	    """
+
+	    if req.has_key(self.request_body_name):
+		req = req[self.request_body_name]
+	    else:
+		raise service_error(service_error.req, "Bad formated request");
+
+	    r = self.call_service(self.url, req, self.cert_file, self.cert_pwd,
+		    self.trusted_certs)
+	    if r.has_key(self.resp_name):
+		return r[self.resp_name]
+	    else:
+		raise service_error(service_error.protocol, 
+			"Bad proxy response")
+
+    # back to defining the fedd_allocate_project_remote class
     def __init__(self, config):
 	"""
@@ -457,2 +436,16 @@
 	self.log = logging.getLogger("fedd.allocate.remote")
 	set_log_level(config, "access", self.log)
+	# The specializations of the proxy functions
+	self.dynamic_project = self.proxy(self.url, self.cert_file, 
+		self.cert_pwd, self.trusted_certs, "AllocateProject",
+		"AllocateProjectRequestBody", AllocateProjectRequestMessage,
+		"AllocateProjectResponseBody")
+	self.static_project = self.proxy(self.url, self.cert_file, 
+		self.cert_pwd, self.trusted_certs, "StaticProject",
+		"StaticProjectRequestBody", StaticProjectRequestMessage,
+		"StaticProjectResponseBody")
+	self.release_project = self.proxy(self.url, self.cert_file,
+		self.cert_pwd, self.trusted_certs, "ReleaseProject",
+		"ReleaseProjectRequestBody", ReleaseProjectRequestMessage,
+		"ReleaseProjectResponseBody")
+
Index: fedd/fedd_client.py
===================================================================
--- fedd/fedd_client.py	(revision c922f23933185172b4e16710ef2d4751c92e6353)
+++ fedd/fedd_client.py	(revision 058f58e973a0a88bc6106dbaec3f9760e7823f79)
@@ -21,5 +21,7 @@
 
 from fedd_util import fedid, fedd_ssl_context, pack_soap, unpack_soap, \
-	pack_id, unpack_id, encapsulate_binaries, decapsulate_binaries
+	pack_id, unpack_id, encapsulate_binaries, decapsulate_binaries, \
+	service_caller
+from service_error import *
 
 from optparse import OptionParser, OptionValueError
@@ -190,5 +192,5 @@
     def __init__(self, pre): 
 	"""
-	Specialize the class for the prc method
+	Specialize the class for the pre method
 	"""
 	self.RequestMessage = globals()["%sRequestMessage" % pre]
@@ -197,4 +199,17 @@
 	self.ResponseBody="%sResponseBody" % pre
 	self.method = pre
+
+	method_call = getattr(feddServiceLocator().getfeddPortType(),
+		self.method, None)
+	if method_call:
+	    # The pre method is a fedd external service
+	    self.caller = service_caller(self.method, 'getfeddPortType', 
+		    feddServiceLocator, self.RequestMessage, self.RequestBody)
+	else:
+	    # The pre method is a fedd internal service
+	    self.caller = service_caller(self.method, 
+		    'getfeddInternalPortType', feddInternalServiceLocator,
+		    self.RequestMessage, self.RequestBody)
+
 	self.RPCException = fedd_rpc.RPCException
 
@@ -265,26 +280,4 @@
 
 	if transport == "soap":
-	    loc = feddServiceLocator();
-	    port = loc.getfeddPortType(url,
-		    transport=M2Crypto.httpslib.HTTPSConnection, 
-		    transdict={ 'ssl_context' : context },
-		    tracefile=tracefile)
-	    method_call = getattr(port, self.method, None)
-
-	    if not method_call:
-		loc = feddInternalServiceLocator();
-		port = loc.getfeddInternalPortType(url,
-			transport=M2Crypto.httpslib.HTTPSConnection, 
-			transdict={ 'ssl_context' : context },
-			tracefile=tracefile)
-		method_call = getattr(port, self.method, None)
-		if not method_call:
-		    raise RuntimeError("Can't find method: %s" % self.method)
-
-	    req = self.RequestMessage()
-
-	    set_req = getattr(req, "set_element_%s" % self.RequestBody, None)
-	    set_req(pack_soap(req, self.RequestBody, req_dict))
-
 	    if serialize_only:
 		sw = SoapWriter()
@@ -292,35 +285,10 @@
 		print str(sw)
 		sys.exit(0)
-
-	    try:
-		method_call = getattr(port, self.method, None)
-		resp = method_call(req)
-	    except ZSI.ParseException, e:
-		raise RuntimeError("Malformed response (XMLPRC?): %s" % e)
-	    except ZSI.FaultException, e:
-		resp = e.fault.detail[0]
-
-	    if resp:
-		resp_call = getattr(resp, "get_element_%s" %self.ResponseBody,
-			None)
-		if resp_call: 
-		    resp_body = resp_call()
-		    if ( resp_body != None): 
-			try:
-			    return unpack_soap(resp_body)
-			except RuntimeError, e:
-			    raise RuntimeError("Bad response. %s" % e.message)
-		elif 'get_element_FeddFaultBody' in dir(resp): 
-		    resp_body = resp.get_element_FeddFaultBody()
-		    if resp_body != None: 
-			try:
-			    fb = unpack_soap(resp_body)
-			except RuntimeError, e:
-			    raise RuntimeError("Bad response. %s" % e.message)
-			raise self.RPCException(fb)
-		else: 
-		    raise RuntimeError("No body in response!?")
-	    else: 
-		raise RuntimeError("No response?!?")
+	    else:
+		try:
+		    resp = self.caller.call_soap_service(url, req_dict, 
+			    context=context, tracefile=tracefile)
+		except service_error, e:
+		    raise self.RPCException( {'code': e.code, 'desc': e.desc })
 	elif transport == "xmlrpc":
 	    if serialize_only:
@@ -328,30 +296,18 @@
 		print ser
 		sys.exit(0)
-
-	    xtransport = SSL_Transport(context)
-	    port = ServerProxy(url, transport=xtransport)
-
-	    try:
-		method_call = getattr(port, self.method, None)
-		resp = method_call(
-			encapsulate_binaries({ self.RequestBody: req_dict},\
-			    ('fedid',)))
-	    except Error, e:
-		resp = { 'FeddFaultBody': \
-			{ 'errstr' : getattr(e, "faultCode", "No fault code"),
-			'desc' : getattr(e, "faultString", "No fault string") }\
-			}
-	    if resp:
-		if resp.has_key(self.ResponseBody): 
-		    return decapsulate_binaries(resp[self.ResponseBody],
-			    ('fedid',))
-		elif resp.has_key('FeddFaultBody'):
-		    raise self.RPCException(resp['FeddFaultBody'])
-		else: 
-		    raise RuntimeError("No body in response!?")
-	    else: 
-		raise RuntimeError("No response?!?")
+	    else:
+		try:
+		    resp = self.caller.call_xmlrpc_service(url, req_dict, 
+			    context=context, tracefile=tracefile)
+		except service_error, e:
+		    raise self.RPCException( {'code': e.code, 'desc': e.desc })
+
 	else:
 	    raise RuntimeError("Unknown RPC transport: %s" % transport)
+
+	if resp.has_key(self.ResponseBody):
+	    return resp[self.ResponseBody]
+	else:
+	    raise RuntimeError("No body in response??")
 
 # Querying experiment data follows the same control flow regardless of the
@@ -606,6 +562,5 @@
 	    for id in eid:
 		for k in id.keys():
-		    if k == 'fedid': print "%s: %s" % (k,fedid(bits=id[k]))
-		    else: print "%s: %s" % (k, id[k])
+		    print "%s: %s" % (k, id[k])
 
 class split(fedd_rpc):
Index: fedd/fedd_experiment_control.py
===================================================================
--- fedd/fedd_experiment_control.py	(revision c922f23933185172b4e16710ef2d4751c92e6353)
+++ fedd/fedd_experiment_control.py	(revision 058f58e973a0a88bc6106dbaec3f9760e7823f79)
@@ -145,4 +145,16 @@
 		self.pdata.terminate()
 
+    call_RequestAccess = service_caller('RequestAccess',
+	    'getfeddPortType', feddServiceLocator, 
+	    RequestAccessRequestMessage, 'RequestAccessRequestBody')
+
+    call_ReleaseAccess = service_caller('ReleaseAccess',
+	    'getfeddPortType', feddServiceLocator, 
+	    ReleaseAccessRequestMessage, 'ReleaseAccessRequestBody')
+
+    call_Ns2Split = service_caller('Ns2Split',
+	    'getfeddInternalPortType', feddInternalServiceLocator, 
+	    Ns2SplitRequestMessage, 'Ns2SplitRequestBody')
+
     def __init__(self, config=None):
 	"""
@@ -242,25 +254,25 @@
 	# Dispatch tables
 	self.soap_services = {\
-		'Create': make_soap_handler(\
+		'Create': soap_handler(\
 			CreateRequestMessage.typecode,
 			getattr(self, "create_experiment"), 
 			CreateResponseMessage,
 			"CreateResponseBody"),
-		'Vtopo': make_soap_handler(\
+		'Vtopo': soap_handler(\
 			VtopoRequestMessage.typecode,
 			getattr(self, "get_vtopo"),
 			VtopoResponseMessage,
 			"VtopoResponseBody"),
-		'Vis': make_soap_handler(\
+		'Vis': soap_handler(\
 			VisRequestMessage.typecode,
 			getattr(self, "get_vis"),
 			VisResponseMessage,
 			"VisResponseBody"),
-		'Info': make_soap_handler(\
+		'Info': soap_handler(\
 			InfoRequestMessage.typecode,
 			getattr(self, "get_info"),
 			InfoResponseMessage,
 			"InfoResponseBody"),
-		'Terminate': make_soap_handler(\
+		'Terminate': soap_handler(\
 			TerminateRequestMessage.typecode,
 			getattr(self, "terminate_experiment"),
@@ -270,17 +282,17 @@
 
 	self.xmlrpc_services = {\
-		'Create': make_xmlrpc_handler(\
+		'Create': xmlrpc_handler(\
 			getattr(self, "create_experiment"), 
 			"CreateResponseBody"),
-		'Vtopo': make_xmlrpc_handler(\
+		'Vtopo': xmlrpc_handler(\
 			getattr(self, "get_vtopo"),
 			"VtopoResponseBody"),
-		'Vis': make_xmlrpc_handler(\
+		'Vis': xmlrpc_handler(\
 			getattr(self, "get_vis"),
 			"VisResponseBody"),
-		'Info': make_xmlrpc_handler(\
+		'Info': xmlrpc_handler(\
 			getattr(self, "get_info"),
 			"InfoResponseBody"),
-		'Terminate': make_xmlrpc_handler(\
+		'Terminate': xmlrpc_handler(\
 			getattr(self, "terminate_experiment"),
 			"TerminateResponseBody"),
@@ -816,46 +828,11 @@
 	    req['resources']['node'] = rnodes
 
-	# No retry loop here.  Proxy servers must correctly authenticate
-	# themselves without help
-
-	try:
-	    ctx = fedd_ssl_context(self.cert_file, 
-		    self.trusted_certs, password=self.cert_pwd)
-	except SSL.SSLError:
-	    raise service_error(service_error.server_config, 
-		    "Server certificates misconfigured")
-
-	loc = feddServiceLocator();
-	port = loc.getfeddPortType(uri,
-		transport=M2Crypto.httpslib.HTTPSConnection, 
-		transdict={ 'ssl_context' : ctx })
-
-	# Reconstruct the full request message
-	msg = RequestAccessRequestMessage()
-	msg.set_element_RequestAccessRequestBody(
-		pack_soap(msg, "RequestAccessRequestBody", req))
-
-	try:
-	    resp = port.RequestAccess(msg)
-	except ZSI.ParseException, e:
-	    raise service_error(service_error.req,
-		    "Bad format message (XMLRPC??): %s" % str(e))
-	except ZSI.FaultException, e:
-	    resp = e.fault.detail[0]
-
-	# Again, weird incompatibilities rear their head.  userRoles, which are
-	# restricted strings, seem to be encoded by ZSI as non-unicode strings
-	# in a way that confuses the pickling and XMLRPC sending systems.
-	# Explicitly unicoding them seems to fix this, though it concerns me
-	# some.  It may be that these things are actually a ZSI string
-	# subclass, and the character encoding is not the major issue.  In any
-	# case, making all the subclasses of basestring into unicode strings
-	# unifies the response format and solves the problem.
-	r = make_unicode(fedids_to_obj(unpack_soap(resp)))
+	r = self.call_RequestAccess(uri, req, 
+		self.cert_file, self.cert_pwd, self.trusted_certs)
 
 	if r.has_key('RequestAccessResponseBody'):
 	    r = r['RequestAccessResponseBody']
 	else:
-	    raise service_error(service_error.proxy,
+	    raise service_error(service_error.protocol,
 		    "Bad proxy response")
 
@@ -894,33 +871,6 @@
 		    "Unknown testbed: %s" % tb)
 
-	# The basic request
-	req = { 'allocID' : aid }
-
-	# No retry loop here.  Proxy servers must correctly authenticate
-	# themselves without help
-	try:
-	    ctx = fedd_ssl_context(self.cert_file, 
-		    self.trusted_certs, password=self.cert_pwd)
-	except SSL.SSLError:
-	    raise service_error(service_error.server_config, 
-		    "Server certificates misconfigured")
-
-	loc = feddServiceLocator();
-	port = loc.getfeddPortType(uri,
-		transport=M2Crypto.httpslib.HTTPSConnection, 
-		transdict={ 'ssl_context' : ctx })
-
-	# Reconstruct the full request message
-	msg = ReleaseAccessRequestMessage()
-	msg.set_element_ReleaseAccessRequestBody(
-		pack_soap(msg, "ReleaseAccessRequestBody", req))
-
-	try:
-	    resp = port.ReleaseAccess(msg)
-	except ZSI.ParseException, e:
-	    raise service_error(service_error.req,
-		    "Bad format message (XMLRPC??): %s" % str(e))
-	except ZSI.FaultException, e:
-	    resp = e.fault.detail[0]
+	resp = self.call_ReleaseAccess(uri, {'allocID': aid},
+		self.cert_file, self.cert_pwd, self.trusted_certs)
 
 	# better error coding
@@ -934,30 +884,7 @@
 	    }
 
-	# No retry loop here.  Proxy servers must correctly authenticate
-	# themselves without help
-	try:
-	    ctx = fedd_ssl_context(self.cert_file, 
-		    self.trusted_certs, password=self.cert_pwd)
-	except SSL.SSLError:
-	    raise service_error(service_error.server_config, 
-		    "Server certificates misconfigured")
-
-	loc = feddInternalServiceLocator();
-	port = loc.getfeddInternalPortType(uri,
-		transport=M2Crypto.httpslib.HTTPSConnection, 
-		transdict={ 'ssl_context' : ctx })
-
-	# Reconstruct the full request message
-	msg = Ns2SplitRequestMessage()
-	msg.set_element_Ns2SplitRequestBody(
-		pack_soap(msg, "Ns2SplitRequestBody", req))
-
-	try:
-	    resp = port.Ns2Split(msg)
-	except ZSI.ParseException, e:
-	    raise service_error(service_error.req,
-		    "Bad format message (XMLRPC??): %s" %
-		    str(e))
-	r = unpack_soap(resp)
+	r = self.call_Ns2Split(uri, req, self.cert_file, self.cert_pwd, 
+		self.trusted_certs)
+
 	if r.has_key('Ns2SplitResponseBody'):
 	    r = r['Ns2SplitResponseBody']
@@ -965,8 +892,8 @@
 		return r['output'].splitlines()
 	    else:
-		raise service_error(service_error.proxy, 
+		raise service_error(service_error.protocol, 
 			"Bad splitter response (no output)")
 	else:
-	    raise service_error(service_error.proxy, "Bad splitter response")
+	    raise service_error(service_error.protocol, "Bad splitter response")
 	
     class current_testbed:
Index: fedd/fedd_split.py
===================================================================
--- fedd/fedd_split.py	(revision c922f23933185172b4e16710ef2d4751c92e6353)
+++ fedd/fedd_split.py	(revision 058f58e973a0a88bc6106dbaec3f9760e7823f79)
@@ -62,5 +62,5 @@
 	# Dispatch tables
 	self.soap_services = {\
-		'Ns2Split': make_soap_handler(\
+		'Ns2Split': soap_handler(\
 			Ns2SplitRequestMessage.typecode,
 			getattr(self, "run_splitter"), 
@@ -70,5 +70,5 @@
 
 	self.xmlrpc_services = {\
-		'Ns2Split': make_xmlrpc_handler(\
+		'Ns2Split': xmlrpc_handler(\
 			getattr(self, "run_splitter"), 
 			"Ns2SplitResponseBody"),
Index: fedd/fedd_types.xsd
===================================================================
--- fedd/fedd_types.xsd	(revision c922f23933185172b4e16710ef2d4751c92e6353)
+++ fedd/fedd_types.xsd	(revision 058f58e973a0a88bc6106dbaec3f9760e7823f79)
@@ -629,5 +629,5 @@
 	<xsd:restriction>
 	  <xsd:enumeration value="1"/>	<!-- access denied -->
-	  <xsd:enumeration value="2"/>	<!-- proxy error -->
+	  <xsd:enumeration value="2"/>	<!-- protocol error -->
 	  <xsd:enumeration value="3"/>	<!-- badly formed request -->
 	  <xsd:enumeration value="4"/>	<!-- server configuration error -->
Index: fedd/fedd_util.py
===================================================================
--- fedd/fedd_util.py	(revision c922f23933185172b4e16710ef2d4751c92e6353)
+++ fedd/fedd_util.py	(revision 058f58e973a0a88bc6106dbaec3f9760e7823f79)
@@ -368,30 +368,35 @@
 
 
-def make_soap_handler(typecode, method, constructor, body_name):
-    """
-    Generate the handler code to unpack and pack SOAP requests and responses
+class soap_handler:
+    """
+    Encapsulate the handler code to unpack and pack SOAP requests and responses
     and call the given method.
 
     The code to decapsulate and encapsulate parameters encoded in SOAP is the
-    same modulo a few parameters.  This is basically a stub compiler for
-    calling a fedd service trhough a soap interface.  The parameters are the
-    typecode of the request parameters, the method to call (usually a bound
-    instance of a method on a fedd service providing class), the constructor of
-    a response packet and the name of the body element of that packet.  The
-    handler takes a ParsedSoap object (the request) and returns an instance of
-    the class created by constructor containing the response.  Failures of the
-    constructor or badly created constructors will result in None being
-    returned.
-    """
-    def handler(ps, fid):
-	req = ps.Parse(typecode)
-
-	msg = method(fedids_to_obj(unpack_soap(req)), fid)
-
-	resp = constructor()
-	set_element = getattr(resp, "set_element_%s" % body_name, None)
+    same modulo a few parameters.  This is a functor that calls a fedd service
+    trhough a soap interface.  The parameters are the typecode of the request
+    parameters, the method to call (usually a bound instance of a method on a
+    fedd service providing class), the constructor of a response packet and the
+    name of the body element of that packet.  The handler takes a ParsedSoap
+    object (the request) and returns an instance of the class created by
+    constructor containing the response.  Failures of the constructor or badly
+    created constructors will result in None being returned.
+    """
+    def __init__(self, typecode, method, constructor, body_name):
+	self.typecode = typecode
+	self.method = method
+	self.constructor = constructor
+	self.body_name = body_name
+
+    def __call__(self, ps, fid):
+	req = ps.Parse(self.typecode)
+
+	msg = self.method(fedids_to_obj(unpack_soap(req)), fid)
+
+	resp = self.constructor()
+	set_element = getattr(resp, "set_element_%s" % self.body_name, None)
 	if set_element and callable(set_element):
 	    try:
-		set_element(pack_soap(resp, body_name, msg))
+		set_element(pack_soap(resp, self.body_name, msg))
 		return resp
 	    except (NameError, TypeError):
@@ -400,9 +405,7 @@
 	    return None
 
-    return handler
-
-def make_xmlrpc_handler(method, body_name):
-    """
-    Generate the handler code to unpack and pack SOAP requests and responses
+class xmlrpc_handler:
+    """
+    Generate the handler code to unpack and pack XMLRPC requests and responses
     and call the given method.
 
@@ -416,37 +419,58 @@
     to fedid objects on input and encapsulated as Binaries on output.
     """
-    def handler(params, fid):
-	decap_fedids = (('fedid', lambda x: fedid(bits=x.data)),)
-
-	p = apply_to_tags(params[0], decap_fedids)
-	msg = method(p, fid)
-
+    def __init__(self, method, body_name):
+	self.method = method
+	self.body_name = body_name
+	# A map used by apply_to_tags to convert fedids from xmlrpclib.Binary
+	# objects to fedid objects in one sweep.
+	self.decap_fedids = (('fedid', lambda x: fedid(bits=x.data)),)
+
+    def __call__(self, params, fid):
+	msg = None
+
+	p = apply_to_tags(params[0], self.decap_fedids)
+	try:
+	    msg = self.method(p, fid)
+	except service_error, e:
+	    raise Fault(e.code_string(), e.desc)
 	if msg != None:
-	    return make_unicode(encapsulate_binaries({ body_name: msg }, ('fedid',)))
+	    return make_unicode(encapsulate_binaries(\
+		    { self.body_name: msg }, ('fedid',)))
 	else:
 	    return None
 
-    return handler
-
-def make_service_callers(service_name, port_name, request_message, 
-	request_body_name):
+class service_caller:
+    def __init__(self, service_name, port_name, locator, request_message, 
+	    request_body_name, tracefile=None):
+	self.service_name = service_name
+	self.port_name = port_name
+	self.locator = locator
+	self.request_message = request_message
+	self.request_body_name = request_body_name
+	self.tracefile = tracefile
+	self.__call__ = self.call_service
 
     def call_xmlrpc_service(self, url, req, cert_file=None, cert_pwd=None, 
-	    trusted_certs=None):
+	    trusted_certs=None, context=None, tracefile=None):
 	"""Send an XMLRPC request.  """
 	decap_fedids = (('fedid', lambda x: fedid(bits=x.data)),)
 
-	# No retry loop here.  Proxy servers must correctly authenticate
-	# themselves without help
-	try:
-	    ctx = fedd_ssl_context(cert_file, trusted_certs, password=cert_pwd)
-	except SSL.SSLError:
-	    raise service_error(service_error.server_config,
-		    "Server certificates misconfigured")
+
+	# If a context is given, use it.  Otherwise construct one from
+	# components.  The construction shouldn't call out for passwords.
+	if context:
+	    ctx = context
+	else:
+	    try:
+		ctx = fedd_ssl_context(cert_file, trusted_certs, 
+			password=cert_pwd)
+	    except SSL.SSLError:
+		raise service_error(service_error.server_config,
+			"Certificates misconfigured")
 
 	# Of all the dumbass things.  The XMLRPC library in use here won't
-	# properly encode unicode strings, so we make a copy of req with the
-	# unicode objects converted.  We also convert the url to a basic string
-	# if it isn't one already.
+	# properly encode unicode strings, so we make a copy of req with
+	# the unicode objects converted.  We also convert the url to a
+	# basic string if it isn't one already.
 	r = strip_unicode(copy.deepcopy(req))
 	url = str(url)
@@ -454,18 +478,19 @@
 	transport = SSL_Transport(ctx)
 	port = ServerProxy(url, transport=transport)
+	# Make the call, and convert faults back to service_errors
 	try:
-	    remote_method = getattr(port, service_name, None)
-	    resp = remote_method(encapsulate_binaries({ request_body_name: r},
-		('fedid',)))
+	    remote_method = getattr(port, self.service_name, None)
+	    resp = remote_method(encapsulate_binaries(\
+		    { self.request_body_name: r}, ('fedid',)))
 	except Fault, f:
 	    raise service_error(None, f.faultString, f.faultCode)
 	except Error, e:
-	    raise service_error(service_error.proxy, 
+	    raise service_error(service_error.protocol, 
 		    "Remote XMLRPC Fault: %s" % e)
 
 	return apply_to_tags(resp, decap_fedids) 
 
-    def call_soap_service(self, url, req, loc_const, cert_file=None, cert_pwd=None, 
-	    trusted_certs=None):
+    def call_soap_service(self, url, req, cert_file=None, cert_pwd=None,
+	    trusted_certs=None, context=None, tracefile=None):
 	"""
 	Send req on to the real destination in dt and return the response
@@ -474,21 +499,28 @@
 	also rethrows any faults.
 	"""
-	# No retry loop here.  Proxy servers must correctly authenticate
-	# themselves without help
-	try:
-	    ctx = fedd_ssl_context(cert_file, trusted_certs, password=cert_pwd)
-	except SSL.SSLError:
-	    raise service_error(service_error.server_config, 
-		    "Server certificates misconfigured")
-
-	loc = loc_const()
-	get_port = getattr(loc, port_name, None)
+
+	tf = tracefile or self.tracefile or None
+
+	# If a context is given, use it.  Otherwise construct one from
+	# components.  The construction shouldn't call out for passwords.
+	if context:
+	    ctx = context
+	else:
+	    try:
+		ctx = fedd_ssl_context(cert_file, trusted_certs, 
+			password=cert_pwd)
+	    except SSL.SSLError:
+		raise service_error(service_error.server_config,
+			"Certificates misconfigured")
+	loc = self.locator()
+	get_port = getattr(loc, self.port_name, None)
 	if not get_port:
 	    raise service_error(service_error.internal, 
-		    "Cannot get port %s from locator" % port_name)
+		    "Cannot get port %s from locator" % self.port_name)
 	port = get_port(url,
 		transport=M2Crypto.httpslib.HTTPSConnection, 
-		transdict={ 'ssl_context' : ctx })
-	remote_method = getattr(port, service_name, None)
+		transdict={ 'ssl_context' : ctx },
+		tracefile=tf)
+	remote_method = getattr(port, self.service_name, None)
 	if not remote_method:
 	    raise service_error(service_error.internal,
@@ -496,21 +528,59 @@
 
 	# Reconstruct the full request message
-	msg = request_message()
-	set_element = getattr(msg, "set_element_%s" % request_body_name, None)
+	msg = self.request_message()
+	set_element = getattr(msg, "set_element_%s" % self.request_body_name,
+		None)
 	if not set_element:
 	    raise service_error(service_error.internal,
 		    "Cannot get element setting method for %s" % \
-			    request_body_name)
-	set_element(pack_soap(msg, request_body_name, req))
+			    self.request_body_name)
+	set_element(pack_soap(msg, self.request_body_name, req))
 	try:
 	    resp = remote_method(msg)
 	except ZSI.ParseException, e:
-	    raise service_error(service_error.proxy,
-		    "Bad format message (XMLRPC??): %s" %
-		    str(e))
-	r = unpack_soap(resp)
+	    raise service_error(service_error.protocol,
+		    "Bad format message (XMLRPC??): %s" % e)
+	except ZSI.FaultException, e:
+	    ee = unpack_soap(e.fault.detail[0]).get('FeddFaultBody', { })
+	    if ee:
+		raise service_error(ee['code'], ee['desc'])
+	    else:
+		raise service_error(service_error.internal,
+			"Unexpected fault body")
+	r = make_unicode(fedids_to_obj(unpack_soap(resp)))
 	return r
 
-    return (call_soap_service, call_xmlrpc_service)
+    def call_service(self, url, req, cert_file=None, cert_pwd=None, 
+	trusted_certs=None, context=None, tracefile=None):
+	p_fault = None	# Any SOAP failure (sent unless XMLRPC works)
+	resp = None
+	try:
+	    # Try the SOAP request
+	    resp = self.call_soap_service(url, req, 
+		    cert_file, cert_pwd, trusted_certs, context, tracefile)
+	    return resp
+	except service_error, e:
+	    if e.code == service_error.protocol: p_fault = None
+	    else: raise
+	except ZSI.FaultException, f:
+	    p_fault = f.fault.detail[0]
+		
+
+	# If we could not get a valid SOAP response to the request above,
+	# try the same address using XMLRPC and let any faults flow back
+	# out.
+	if p_fault == None:
+	    resp = self.call_xmlrpc_service(url, req, cert_file,
+		    cert_pwd, trusted_certs, context, tracefile)
+	    return resp
+	else:
+	    # Build the fault
+	    ee = unpack_soap(p_fault).get('FeddFaultBody', { })
+	    if ee:
+		raise service_error(ee['code'], ee['desc'])
+	    else:
+		raise service_error(service_error.internal,
+			"Unexpected fault body")
+
 
 def set_log_level(config, sect, log):
Index: fedd/service_error.py
===================================================================
--- fedd/service_error.py	(revision c922f23933185172b4e16710ef2d4751c92e6353)
+++ fedd/service_error.py	(revision 058f58e973a0a88bc6106dbaec3f9760e7823f79)
@@ -6,5 +6,5 @@
 class service_error(RuntimeError):
     access = 1
-    proxy= 2
+    protocol= 2
     req = 3
     server_config = 4
@@ -14,5 +14,5 @@
     code_str = { 
 	access : "Access Denied",
-	proxy : "Proxy Error",
+	protocol : "Protocol Error",
 	req : "Badly Formed Request",
 	server_config: "Server Configuration Error",
@@ -23,5 +23,5 @@
     str_code = dict([ (v, k) for k, v in code_str.iteritems() ])
     client_errors = ( req )
-    server_errors = ( access, proxy, server_config, internal)
+    server_errors = ( access, protocol, server_config, internal)
 
     def __init__(self, code=None, desc=None, from_string=None):
