class Transport(xmlrpclib.Transport): user_agent = "rhn.rpclib.py/%s" % __version__ def __init__(self, transfer=0, encoding=0, refreshCallback=None, progressCallback=None, use_datetime=None, timeout=None): self._use_builtin_types = False self._transport_flags = {'transfer' : 0, 'encoding' : 0} self.set_transport_flags(transfer=transfer, encoding=encoding) self._headers = UserDictCase() self.verbose = 0 self.connection = None self.method = "POST" self._lang = None self.refreshCallback = refreshCallback self.progressCallback = progressCallback self.bufferSize = 16384 self.headers_in = None self.response_status = None self.response_reason = None self._redirected = None self._use_datetime = use_datetime self.timeout = timeout # set the progress callback def set_progress_callback(self, progressCallback, bufferSize=16384): self.progressCallback = progressCallback self.bufferSize = bufferSize # set the refresh callback def set_refresh_callback(self, refreshCallback): self.refreshCallback = refreshCallback # set the buffer size # The bigger this is, the faster the read is, but the more seldom is the # progress callback called def set_buffer_size(self, bufferSize): if bufferSize is None: # No buffer size specified; go with 16k bufferSize = 16384 self.bufferSize = bufferSize # set the request method def set_method(self, method): if method not in ("GET", "POST"): raise IOError("Unknown request method %s" % method) self.method = method # reset the transport options def set_transport_flags(self, transfer=None, encoding=None, **kwargs): # For backwards compatibility, we keep transfer and encoding as # positional parameters (they could come in as kwargs easily) self._transport_flags.update(kwargs) if transfer is not None: self._transport_flags['transfer'] = transfer if encoding is not None: self._transport_flags['encoding'] = encoding self.validate_transport_flags() def get_transport_flags(self): return self._transport_flags.copy() def validate_transport_flags(self): # Transfer and encoding are guaranteed to be there transfer = self._transport_flags.get('transfer') transfer = lookupTransfer(transfer, strict=1) self._transport_flags['transfer'] = transfer encoding = self._transport_flags.get('encoding') encoding = lookupEncoding(encoding, strict=1) self._transport_flags['encoding'] = encoding # Add arbitrary additional headers. def set_header(self, name, arg): if type(arg) in [ type([]), type(()) ]: # Multivalued header self._headers[name] = [str(a) for a in arg] else: self._headers[name] = str(arg) def add_header(self, name, arg): if name in self._headers: vlist = self._headers[name] if not isinstance(vlist, ListType): vlist = [ vlist ] else: vlist = self._headers[name] = [] vlist.append(str(arg)) def clear_headers(self): self._headers.clear() def get_connection(self, host): if self.verbose: print("Connecting via http to %s" % (host, )) if self.timeout: return connections.HTTPConnection(host, timeout=self.timeout) else: return connections.HTTPConnection(host) def request(self, host, handler, request_body, verbose=0): # issue XML-RPC request # XXX: automatically compute how to send depending on how much data # you want to send # XXX Deal with HTTP/1.1 if necessary self.verbose = verbose # implement BASIC HTTP AUTHENTICATION host, extra_headers, x509 = self.get_host_info(host) if not extra_headers: extra_headers = [] # Establish the connection connection = self.get_connection(host) # Setting the user agent. Only interesting for SSL tunnels, in any # other case the general headers are good enough. connection.set_user_agent(self.user_agent) if self.verbose: connection.set_debuglevel(self.verbose - 1) # Get the output object to push data with req = Output(connection=connection, method=self.method) req.set_transport_flags(**self._transport_flags) # Add the extra headers req.set_header('User-Agent', self.user_agent) for header, value in list(self._headers.items()) + extra_headers: # Output.set_header correctly deals with multivalued headers now req.set_header(header, value) # Content-Type req.set_header("Content-Type", "text/xml") req.process(request_body) # Host and Content-Length are set by HTTP*Connection for h in ['Content-Length', 'Host']: req.clear_header(h) headers, fd = req.send_http(host, handler) if self.verbose: print("Incoming headers:") for header, value in headers.items(): print("\t%s : %s" % (header, value)) if fd.status in (301, 302): self._redirected = headers["Location"] self.response_status = fd.status return None # Save the headers self.headers_in = headers self.response_status = fd.status self.response_reason = fd.reason return self._process_response(fd, connection) def _process_response(self, fd, connection): # Now use the Input class in case we get an enhanced response resp = Input(self.headers_in, progressCallback=self.progressCallback, bufferSize=self.bufferSize) fd = resp.decode(fd) if isinstance(fd, InputStream): # When the File object goes out of scope, so will the InputStream; # that will eventually call the connection's close() method and # cleanly reap it f = File(fd.fd, fd.length, fd.name, bufferSize=self.bufferSize, progressCallback=self.progressCallback) # Set the File's close method to the connection's # Note that calling the HTTPResponse's close() is not enough, # since the main socket would remain open, and this is # particularily bad with SSL f.close = connection.close return f # We can safely close the connection now; if we had an # application/octet/stream (for which Input.read passes the original # socket object), Input.decode would return an InputStream, # so we wouldn't reach this point connection.close() return self.parse_response(fd) # Give back the new URL if redirected def redirected(self): return self._redirected # Rewrite parse_response to provide refresh callbacks def parse_response(self, f): # read response from input file, and parse it p, u = self.getparser() while 1: response = f.read(1024) if not response: break if self.refreshCallback: self.refreshCallback() if self.verbose: print("body:", repr(response)) p.feed(response) f.close() p.close() return u.close() def setlang(self, lang): self._lang = lang
class Transport(xmlrpclib.Transport): user_agent = "rhn.rpclib.py/%s" % __version__ def __init__(self, transfer=0, encoding=0, refreshCallback=None, progressCallback=None, use_datetime=None, timeout=None): self._use_builtin_types = False self._transport_flags = {'transfer': 0, 'encoding': 0} self.set_transport_flags(transfer=transfer, encoding=encoding) self._headers = UserDictCase() self.verbose = 0 self.connection = None self.method = "POST" self._lang = None self.refreshCallback = refreshCallback self.progressCallback = progressCallback self.bufferSize = 16384 self.headers_in = None self.response_status = None self.response_reason = None self._redirected = None self._use_datetime = use_datetime self.timeout = timeout # set the progress callback def set_progress_callback(self, progressCallback, bufferSize=16384): self.progressCallback = progressCallback self.bufferSize = bufferSize # set the refresh callback def set_refresh_callback(self, refreshCallback): self.refreshCallback = refreshCallback # set the buffer size # The bigger this is, the faster the read is, but the more seldom is the # progress callback called def set_buffer_size(self, bufferSize): if bufferSize is None: # No buffer size specified; go with 16k bufferSize = 16384 self.bufferSize = bufferSize # set the request method def set_method(self, method): if method not in ("GET", "POST"): raise IOError("Unknown request method %s" % method) self.method = method # reset the transport options def set_transport_flags(self, transfer=None, encoding=None, **kwargs): # For backwards compatibility, we keep transfer and encoding as # positional parameters (they could come in as kwargs easily) self._transport_flags.update(kwargs) if transfer is not None: self._transport_flags['transfer'] = transfer if encoding is not None: self._transport_flags['encoding'] = encoding self.validate_transport_flags() def get_transport_flags(self): return self._transport_flags.copy() def validate_transport_flags(self): # Transfer and encoding are guaranteed to be there transfer = self._transport_flags.get('transfer') transfer = lookupTransfer(transfer, strict=1) self._transport_flags['transfer'] = transfer encoding = self._transport_flags.get('encoding') encoding = lookupEncoding(encoding, strict=1) self._transport_flags['encoding'] = encoding # Add arbitrary additional headers. def set_header(self, name, arg): if type(arg) in [type([]), type(())]: # Multivalued header self._headers[name] = [str(a) for a in arg] else: self._headers[name] = str(arg) def add_header(self, name, arg): if name in self._headers: vlist = self._headers[name] if not isinstance(vlist, ListType): vlist = [vlist] else: vlist = self._headers[name] = [] vlist.append(str(arg)) def clear_headers(self): self._headers.clear() def get_connection(self, host): if self.verbose: print("Connecting via http to %s" % (host, )) if self.timeout: return connections.HTTPConnection(host, timeout=self.timeout) else: return connections.HTTPConnection(host) def request(self, host, handler, request_body, verbose=0): # issue XML-RPC request # XXX: automatically compute how to send depending on how much data # you want to send # XXX Deal with HTTP/1.1 if necessary self.verbose = verbose # implement BASIC HTTP AUTHENTICATION host, extra_headers, x509 = self.get_host_info(host) if not extra_headers: extra_headers = [] # Establish the connection connection = self.get_connection(host) # Setting the user agent. Only interesting for SSL tunnels, in any # other case the general headers are good enough. connection.set_user_agent(self.user_agent) if self.verbose: connection.set_debuglevel(self.verbose - 1) # Get the output object to push data with req = Output(connection=connection, method=self.method) req.set_transport_flags(**self._transport_flags) # Add the extra headers req.set_header('User-Agent', self.user_agent) for header, value in list(self._headers.items()) + extra_headers: # Output.set_header correctly deals with multivalued headers now req.set_header(header, value) # Content-Type req.set_header("Content-Type", "text/xml") req.process(request_body) # Host and Content-Length are set by HTTP*Connection for h in ['Content-Length', 'Host']: req.clear_header(h) headers, fd = req.send_http(host, handler) if self.verbose: print("Incoming headers:") for header, value in headers.items(): print("\t%s : %s" % (header, value)) if fd.status in (301, 302): self._redirected = headers["Location"] self.response_status = fd.status return None # Save the headers self.headers_in = headers self.response_status = fd.status self.response_reason = fd.reason return self._process_response(fd, connection) def _process_response(self, fd, connection): # Now use the Input class in case we get an enhanced response resp = Input(self.headers_in, progressCallback=self.progressCallback, bufferSize=self.bufferSize) fd = resp.decode(fd) if isinstance(fd, InputStream): # When the File object goes out of scope, so will the InputStream; # that will eventually call the connection's close() method and # cleanly reap it f = File(fd.fd, fd.length, fd.name, bufferSize=self.bufferSize, progressCallback=self.progressCallback) # Set the File's close method to the connection's # Note that calling the HTTPResponse's close() is not enough, # since the main socket would remain open, and this is # particularily bad with SSL f.close = connection.close return f # We can safely close the connection now; if we had an # application/octet/stream (for which Input.read passes the original # socket object), Input.decode would return an InputStream, # so we wouldn't reach this point connection.close() return self.parse_response(fd) # Give back the new URL if redirected def redirected(self): return self._redirected # Rewrite parse_response to provide refresh callbacks def parse_response(self, f): # read response from input file, and parse it p, u = self.getparser() while 1: response = f.read(1024) if not response: break if self.refreshCallback: self.refreshCallback() if self.verbose: print("body:", repr(response)) p.feed(response) f.close() p.close() return u.close() def setlang(self, lang): self._lang = lang