def __merge(self, component = None): """ merge the config options between the default comp dictionaries and the file we're parsing now """ # Caches this component's configuration options if component is None: component = self.__component opts = UserDictCase() comps = parse_comps(component) for comp in comps: if not self.__defaults.has_key(comp): warn('key not found in config default dict', comp) continue opts.update(self.__defaults[comp]) # Now load the specific stuff, and perform syntax checking too for comp in comps: if not self.__parsedConfig.has_key(comp): # No such entry in the config file continue for key, (values, lineno) in self.__parsedConfig[comp].items(): ## XXX: we don't really want to force every item in the ## config file to have a default value first. If we do, ## uncomment this section #if not opts.has_key(key): # Unknown keyword # warn("Warning: in file %s, line %s: unknown " # "option name `%s'" % (self.file, lineno, key)) # continue opts[key] = values # and now save it self.__configs[component] = opts
def __init__(self, transfer=0, encoding=0, connection=None, method="POST"): # Assumes connection is an instance of HTTPConnection if connection: if not isinstance(connection, connections.HTTPConnection): raise Exception("Expected an HTTPConnection type object") self.method = method # Store the connection self._connection = connection self.data = None self.headers = UserDictCase() self.encoding = 0 self.transfer = 0 self.transport_flags = {} # for authenticated proxies self.username = None self.password = None # Fields to keep the information about the server self._host = None self._handler = None self._http_type = None self._protocol = None # Initialize self.transfer and self.encoding self.set_transport_flags(transfer=transfer, encoding=encoding) # internal flags self.__processed = 0
def __init__(self, transfer=0, encoding=0, refreshCallback=None, progressCallback=None, use_datetime=None, timeout=None): 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
def __init__(self, transfer=0, encoding=0, refreshCallback=None, progressCallback=None): 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
class BaseOutput: # DEFINES for instances use # Content-Encoding ENCODE_NONE = 0 ENCODE_GZIP = 1 ENCODE_ZLIB = 2 ENCODE_GPG = 3 # Content-Transfer-Encoding TRANSFER_NONE = 0 TRANSFER_BINARY = 1 TRANSFER_BASE64 = 2 # Mappings to make things easy encodings = [ [None, "__plain"], # ENCODE_NONE ["x-gzip", "gzip"], # ENCODE_GZIP ["x-zlib", "deflate"], # ENCODE_ZLIB ["x-gpg"], # ENCODE_GPG ] transfers = [ None, # TRANSFER_NONE "binary", # TRANSFRE_BINARY "base64", # TRANSFER_BASE64 ] def __init__(self, transfer=0, encoding=0, connection=None, method="POST"): # Assumes connection is an instance of HTTPConnection if connection: if not isinstance(connection, connections.HTTPConnection): raise Exception("Expected an HTTPConnection type object") self.method = method # Store the connection self._connection = connection self.data = None self.headers = UserDictCase() self.encoding = 0 self.transfer = 0 self.transport_flags = {} # for authenticated proxies self.username = None self.password = None # Fields to keep the information about the server self._host = None self._handler = None self._http_type = None self._protocol = None # Initialize self.transfer and self.encoding self.set_transport_flags(transfer=transfer, encoding=encoding) # internal flags self.__processed = 0 def set_header(self, name, arg): if type(arg) in [ type([]), type(()) ]: # Multi-valued header # # Per RFC 2616, section 4.2 (Message Headers): # Multiple message-header fields with the same field-name MAY be # present in a message if and only if the entire field-value for # the header field is defined as a comma-separated list [i.e. # #(values)]. It MUST be possible to combine the multiple header # fields into one "field-name: field-value" pair, without # changing the semantics of the message, by appending each # subsequent field-value to the first, each separated by a comma. self.headers[name] = ','.join(map(str, arg)) else: self.headers[name] = str(arg) def clear_header(self, name): if self.headers.has_key(name): del self.headers[name] def process(self, data): # Assume straight text/xml self.data = data # Content-Encoding header if self.encoding == self.ENCODE_GZIP: import gzip encoding_name = self.encodings[self.ENCODE_GZIP][0] self.set_header("Content-Encoding", encoding_name) f = SmartIO(force_mem=1) gz = gzip.GzipFile(mode="wb", compresslevel=COMPRESS_LEVEL, fileobj = f) gz.write(data) gz.close() self.data = f.getvalue() f.close() elif self.encoding == self.ENCODE_ZLIB: import zlib encoding_name = self.encodings[self.ENCODE_ZLIB][0] self.set_header("Content-Encoding", encoding_name) obj = zlib.compressobj(COMPRESS_LEVEL) self.data = obj.compress(data) + obj.flush() elif self.encoding == self.ENCODE_GPG: # XXX: fix me. raise NotImplementedError(self.transfer, self.encoding) encoding_name = self.encodings[self.ENCODE_GPG][0] self.set_header("Content-Encoding", encoding_name) # Content-Transfer-Encoding header if self.transfer == self.TRANSFER_BINARY: transfer_name = self.transfers[self.TRANSFER_BINARY] self.set_header("Content-Transfer-Encoding", transfer_name) self.set_header("Content-Type", "application/binary") elif self.transfer == self.TRANSFER_BASE64: import base64 transfer_name = self.transfers[self.TRANSFER_BASE64] self.set_header("Content-Transfer-Encoding", transfer_name) self.set_header("Content-Type", "text/base64") self.data = base64.encodestring(self.data) self.set_header("Content-Length", len(self.data)) rpc_version = __version__ if len(__version__.split()) > 1: rpc_version = __version__.split()[1] # other headers self.set_header("X-Transport-Info", 'Extended Capabilities Transport (C) Red Hat, Inc (version %s)' % rpc_version) self.__processed = 1 # reset the transport options def set_transport_flags(self, transfer=0, encoding=0, **kwargs): self.transfer = transfer self.encoding = encoding self.transport_flags.update(kwargs) def send_http(self, host, handler="/RPC2"): if not self.__processed: raise NotProcessed self._host = host if self._connection is None: raise Exception("No connection object found") self._connection.connect() self._connection.request(self.method, handler, body=self.data, headers=self.headers) response = self._connection.getresponse() if not self.response_acceptable(response): raise xmlrpclib.ProtocolError("%s %s" % (self._host, handler), response.status, response.reason, response.msg) # A response object has read() and close() methods, so we can safely # pass the whole object back return response.msg, response def response_acceptable(self, response): """Returns true if the response is acceptable""" if response.status == 200: return 1 if response.status in (301, 302): return 1 if response.status != 206: return 0 # If the flag is not set, it's unacceptable if not self.transport_flags.get('allow_partial_content'): return 0 if response.msg['Content-Type'] != 'application/octet-stream': # Don't allow anything else to be requested as a range, it could # break the XML parser return 0 return 1 def close(self): if self._connection: self._connection.close() self._connection = None
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._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] = map(str, arg) else: self._headers[name] = str(arg) def add_header(self, name, arg): if self._headers.has_key(name): 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) apply(req.set_transport_flags, (), self._transport_flags) # Add the extra headers req.set_header('User-Agent', self.user_agent) for header, value in 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 Server: """uri [,options] -> a logical connection to an XML-RPC server uri is the connection point on the server, given as scheme://host/target. The standard implementation always supports the "http" scheme. If SSL socket support is available (Python 2.0), it also supports "https". If the target part and the slash preceding it are both omitted, "/RPC2" is assumed. The following options can be given as keyword arguments: transport: a transport factory encoding: the request encoding (default is UTF-8) verbose: verbosity level proxy: use an HTTP proxy username: username for authenticated HTTP proxy password: password for authenticated HTTP proxy All 8-bit strings passed to the server proxy are assumed to use the given encoding. """ # Default factories _transport_class = transports.Transport _transport_class_https = transports.SafeTransport _transport_class_proxy = transports.ProxyTransport _transport_class_https_proxy = transports.SafeProxyTransport def __init__(self, uri, transport=None, encoding=None, verbose=0, proxy=None, username=None, password=None, refreshCallback=None, progressCallback=None): # establish a "logical" server connection # # First parse the proxy information if available # if proxy != None: (ph, pp, pu, pw) = get_proxy_info(proxy) if pp is not None: proxy = "%s:%s" % (ph, pp) else: proxy = ph # username and password will override whatever was passed in the # URL if pu is not None and username is None: username = pu if pw is not None and password is None: password = pw self._uri = uri self._refreshCallback = None self._progressCallback = None self._bufferSize = None self._proxy = proxy self._username = username self._password = password if len(__version__.split()) > 1: self.rpc_version = __version__.split()[1] else: self.rpc_version = __version__ self._reset_host_handler_and_type() if transport is None: self._allow_redirect = 1 transport = self.default_transport(self._type, proxy, username, password) else: # # dont allow redirect on unknow transports, that should be # set up independantly # self._allow_redirect = 0 self._redirected = None self.use_handler_path = 1 self._transport = transport self._trusted_cert_files = [] self._lang = None self._encoding = encoding self._verbose = verbose self.set_refresh_callback(refreshCallback) self.set_progress_callback(progressCallback) # referer, which redirect us to new handler self.send_handler=None self._headers = UserDictCase() def default_transport(self, type, proxy=None, username=None, password=None): if proxy: if type == 'https': transport = self._transport_class_https_proxy(proxy, proxyUsername=username, proxyPassword=password) else: transport = self._transport_class_proxy(proxy, proxyUsername=username, proxyPassword=password) else: if type == 'https': transport = self._transport_class_https() else: transport = self._transport_class() return transport def allow_redirect(self, allow): self._allow_redirect = allow def redirected(self): if not self._allow_redirect: return None return self._redirected def set_refresh_callback(self, refreshCallback): self._refreshCallback = refreshCallback self._transport.set_refresh_callback(refreshCallback) def set_buffer_size(self, bufferSize): self._bufferSize = bufferSize self._transport.set_buffer_size(bufferSize) def set_progress_callback(self, progressCallback, bufferSize=16384): self._progressCallback = progressCallback self._transport.set_progress_callback(progressCallback, bufferSize) def _req_body(self, params, methodname): return xmlrpclib.dumps(params, methodname, encoding=self._encoding) def get_response_headers(self): if self._transport: return self._transport.headers_in return None def get_response_status(self): if self._transport: return self._transport.response_status return None def get_response_reason(self): if self._transport: return self._transport.response_reason return None def get_content_range(self): """Returns a dictionary with three values: length: the total length of the entity-body (can be None) first_byte_pos: the position of the first byte (zero based) last_byte_pos: the position of the last byte (zero based) The range is inclusive; that is, a response 8-9/102 means two bytes """ headers = self.get_response_headers() if not headers: return None content_range = headers.get('Content-Range') if not content_range: return None arr = filter(None, content_range.split()) assert arr[0] == "bytes" assert len(arr) == 2 arr = arr[1].split('/') assert len(arr) == 2 brange, total_len = arr if total_len == '*': # Per RFC, the server is allowed to use * if the length of the # entity-body is unknown or difficult to determine total_len = None else: total_len = int(total_len) start, end = brange.split('-') result = { 'length' : total_len, 'first_byte_pos' : int(start), 'last_byte_pos' : int(end), } return result def accept_ranges(self): headers = self.get_response_headers() if not headers: return None if headers.has_key('Accept-Ranges'): return headers['Accept-Ranges'] return None def _reset_host_handler_and_type(self): """ Reset the attributes: self._host, self._handler, self._type according the value of self._uri. """ # get the url type, uri = urllib.splittype(self._uri) if type is None: raise MalformedURIError, "missing protocol in uri" # with a real uri passed in, uri will now contain "//hostname..." so we # need at least 3 chars for it to maybe be ok... if len(uri) < 3 or uri[0:2] != "//": raise MalformedURIError if type != None: self._type = type.lower() else: self._type = type if self._type not in ("http", "https"): raise IOError, "unsupported XML-RPC protocol" self._host, self._handler = urllib.splithost(uri) if not self._handler: self._handler = "/RPC2" def _strip_characters(self, *args): """ Strip characters, which are not allowed according: http://www.w3.org/TR/2006/REC-xml-20060816/#charsets From spec: Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] /* any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. */ """ regexp = r'[\x00-\x09]|[\x0b-\x0c]|[\x0e-\x1f]' result=[] for item in args: item_type = type(item) if item_type == StringType or item_type == UnicodeType: item = re.sub(regexp, '', item) elif item_type == TupleType: item = tuple(map(self._strip_characters, item)) elif item_type == ListType: item = map(self._strip_characters, item) elif item_type == DictType or item_type == DictionaryType: item = dict([(self._strip_characters(name, val)) for name, val in item.iteritems()]) # else: some object - should take care of himself # numbers - are safe result.append(item) if len(result) == 1: return result[0] else: return tuple(result) def _request(self, methodname, params): """ Call a method on the remote server we can handle redirections. """ # the loop is used to handle redirections redirect_response = 0 retry = 0 self._reset_host_handler_and_type() while 1: if retry >= MAX_REDIRECTIONS: raise InvalidRedirectionError( "Unable to fetch requested Package") # Clear the transport headers first self._transport.clear_headers() for k, v in self._headers.items(): self._transport.set_header(k, v) self._transport.add_header("X-Info", 'RPC Processor (C) Red Hat, Inc (version %s)' % self.rpc_version) # identify the capability set of this client to the server self._transport.set_header("X-Client-Version", 1) if self._allow_redirect: # Advertise that we follow redirects #changing the version from 1 to 2 to support backward compatibility self._transport.add_header("X-RHN-Transport-Capability", "follow-redirects=3") if redirect_response: self._transport.add_header('X-RHN-Redirect', '0') if self.send_handler: self._transport.add_header('X-RHN-Path', self.send_handler) request = self._req_body(self._strip_characters(params), methodname) try: response = self._transport.request(self._host, \ self._handler, request, verbose=self._verbose) save_response = self._transport.response_status except xmlrpclib.ProtocolError, pe: if self.use_handler_path: raise else: save_response = pe.errcode self._redirected = None retry += 1 if save_response == 200: # exit redirects loop and return response break elif save_response not in (301, 302): # Retry pkg fetch self.use_handler_path = 1 continue # rest of loop is run only if we are redirected (301, 302) self._redirected = self._transport.redirected() self.use_handler_path = 0 redirect_response = 1 if not self._allow_redirect: raise InvalidRedirectionError("Redirects not allowed") if self._verbose: print "%s redirected to %s" % (self._uri, self._redirected) typ, uri = urllib.splittype(self._redirected) if typ != None: typ = typ.lower() if typ not in ("http", "https"): raise InvalidRedirectionError( "Redirected to unsupported protocol %s" % typ) # # We forbid HTTPS -> HTTP for security reasons # Note that HTTP -> HTTPS -> HTTP is allowed (because we compare # the protocol for the redirect with the original one) # if self._type == "https" and typ == "http": raise InvalidRedirectionError( "HTTPS redirected to HTTP is not supported") self._host, self._handler = urllib.splithost(uri) if not self._handler: self._handler = "/RPC2" # Create a new transport for the redirected service and # set up the parameters on the new transport del self._transport self._transport = self.default_transport(typ, self._proxy, self._username, self._password) self.set_progress_callback(self._progressCallback) self.set_refresh_callback(self._refreshCallback) self.set_buffer_size(self._bufferSize) self.setlang(self._lang) if self._trusted_cert_files != [] and \ hasattr(self._transport, "add_trusted_cert"): for certfile in self._trusted_cert_files: self._transport.add_trusted_cert(certfile) # Then restart the loop to try the new entry point. if isinstance(response, transports.File): # Just return the file return response # an XML-RPC encoded data structure if isinstance(response, TupleType) and len(response) == 1: response = response[0] return response
def __init__(self, uri, transport=None, encoding=None, verbose=0, proxy=None, username=None, password=None, refreshCallback=None, progressCallback=None): # establish a "logical" server connection # # First parse the proxy information if available # if proxy != None: (ph, pp, pu, pw) = get_proxy_info(proxy) if pp is not None: proxy = "%s:%s" % (ph, pp) else: proxy = ph # username and password will override whatever was passed in the # URL if pu is not None and username is None: username = pu if pw is not None and password is None: password = pw self._uri = uri self._refreshCallback = None self._progressCallback = None self._bufferSize = None self._proxy = proxy self._username = username self._password = password if len(__version__.split()) > 1: self.rpc_version = __version__.split()[1] else: self.rpc_version = __version__ self._reset_host_handler_and_type() if transport is None: self._allow_redirect = 1 transport = self.default_transport(self._type, proxy, username, password) else: # # dont allow redirect on unknow transports, that should be # set up independantly # self._allow_redirect = 0 self._redirected = None self.use_handler_path = 1 self._transport = transport self._trusted_cert_files = [] self._lang = None self._encoding = encoding self._verbose = verbose self.set_refresh_callback(refreshCallback) self.set_progress_callback(progressCallback) # referer, which redirect us to new handler self.send_handler=None self._headers = UserDictCase()
class BaseOutput: # DEFINES for instances use # Content-Encoding ENCODE_NONE = 0 ENCODE_GZIP = 1 ENCODE_ZLIB = 2 ENCODE_GPG = 3 # Content-Transfer-Encoding TRANSFER_NONE = 0 TRANSFER_BINARY = 1 TRANSFER_BASE64 = 2 # Mappings to make things easy encodings = [ [None, "__plain"], # ENCODE_NONE ["x-gzip", "gzip"], # ENCODE_GZIP ["x-zlib", "deflate"], # ENCODE_ZLIB ["x-gpg"], # ENCODE_GPG ] transfers = [ None, # TRANSFER_NONE "binary", # TRANSFRE_BINARY "base64", # TRANSFER_BASE64 ] def __init__(self, transfer=0, encoding=0, connection=None, method="POST"): # Assumes connection is an instance of HTTPConnection if connection: if not isinstance(connection, connections.HTTPConnection): raise Exception("Expected an HTTPConnection type object") self.method = method # Store the connection self._connection = connection self.data = None self.headers = UserDictCase() self.encoding = 0 self.transfer = 0 self.transport_flags = {} # for authenticated proxies self.username = None self.password = None # Fields to keep the information about the server self._host = None self._handler = None self._http_type = None self._protocol = None # Initialize self.transfer and self.encoding self.set_transport_flags(transfer=transfer, encoding=encoding) # internal flags self.__processed = 0 def set_header(self, name, arg): if type(arg) in [type([]), type(())]: # Multi-valued header # # Per RFC 2616, section 4.2 (Message Headers): # Multiple message-header fields with the same field-name MAY be # present in a message if and only if the entire field-value for # the header field is defined as a comma-separated list [i.e. # #(values)]. It MUST be possible to combine the multiple header # fields into one "field-name: field-value" pair, without # changing the semantics of the message, by appending each # subsequent field-value to the first, each separated by a comma. self.headers[name] = ','.join(map(str, arg)) else: self.headers[name] = str(arg) def clear_header(self, name): if self.headers.has_key(name): del self.headers[name] def process(self, data): # Assume straight text/xml self.data = data # Content-Encoding header if self.encoding == self.ENCODE_GZIP: import gzip encoding_name = self.encodings[self.ENCODE_GZIP][0] self.set_header("Content-Encoding", encoding_name) f = SmartIO(force_mem=1) gz = gzip.GzipFile(mode="wb", compresslevel=COMPRESS_LEVEL, fileobj=f) gz.write(data) gz.close() self.data = f.getvalue() f.close() elif self.encoding == self.ENCODE_ZLIB: import zlib encoding_name = self.encodings[self.ENCODE_ZLIB][0] self.set_header("Content-Encoding", encoding_name) obj = zlib.compressobj(COMPRESS_LEVEL) self.data = obj.compress(data) + obj.flush() elif self.encoding == self.ENCODE_GPG: # XXX: fix me. raise NotImplementedError(self.transfer, self.encoding) encoding_name = self.encodings[self.ENCODE_GPG][0] self.set_header("Content-Encoding", encoding_name) # Content-Transfer-Encoding header if self.transfer == self.TRANSFER_BINARY: transfer_name = self.transfers[self.TRANSFER_BINARY] self.set_header("Content-Transfer-Encoding", transfer_name) self.set_header("Content-Type", "application/binary") elif self.transfer == self.TRANSFER_BASE64: import base64 transfer_name = self.transfers[self.TRANSFER_BASE64] self.set_header("Content-Transfer-Encoding", transfer_name) self.set_header("Content-Type", "text/base64") self.data = base64.encodestring(self.data) self.set_header("Content-Length", len(self.data)) rpc_version = __version__ if len(__version__.split()) > 1: rpc_version = __version__.split()[1] # other headers self.set_header( "X-Transport-Info", 'Extended Capabilities Transport (C) Red Hat, Inc (version %s)' % rpc_version) self.__processed = 1 # reset the transport options def set_transport_flags(self, transfer=0, encoding=0, **kwargs): self.transfer = transfer self.encoding = encoding self.transport_flags.update(kwargs) def send_http(self, host, handler="/RPC2"): if not self.__processed: raise NotProcessed self._host = host if self._connection is None: raise Exception("No connection object found") self._connection.connect() self._connection.request(self.method, handler, body=self.data, headers=self.headers) response = self._connection.getresponse() if not self.response_acceptable(response): raise xmlrpclib.ProtocolError("%s %s" % (self._host, handler), response.status, response.reason, response.msg) # A response object has read() and close() methods, so we can safely # pass the whole object back return response.msg, response def response_acceptable(self, response): """Returns true if the response is acceptable""" if response.status == 200: return 1 if response.status in (301, 302): return 1 if response.status != 206: return 0 # If the flag is not set, it's unacceptable if not self.transport_flags.get('allow_partial_content'): return 0 if response.msg['Content-Type'] != 'application/octet-stream': # Don't allow anything else to be requested as a range, it could # break the XML parser return 0 return 1 def close(self): if self._connection: self._connection.close() self._connection = None
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._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] = map(str, arg) else: self._headers[name] = str(arg) def add_header(self, name, arg): if self._headers.has_key(name): 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) apply(req.set_transport_flags, (), self._transport_flags) # Add the extra headers req.set_header('User-Agent', self.user_agent) for header, value in 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
def __init__(self, uri, transport=None, encoding=None, verbose=0, proxy=None, username=None, password=None, refreshCallback=None, progressCallback=None): # establish a "logical" server connection # # First parse the proxy information if available # if proxy != None: (ph, pp, pu, pw) = get_proxy_info(proxy) if pp is not None: proxy = "%s:%s" % (ph, pp) else: proxy = ph # username and password will override whatever was passed in the # URL if pu is not None and username is None: username = pu if pw is not None and password is None: password = pw self._uri = uri self._refreshCallback = None self._progressCallback = None self._bufferSize = None self._proxy = proxy self._username = username self._password = password # get the url type, uri = urllib.splittype(uri) type = (string.lower(type)).strip() self._type = type if type not in ("http", "https"): raise IOError, "unsupported XML-RPC protocol" self._host, self._handler = urllib.splithost(uri) if not self._handler: self._handler = "/RPC2" if transport is None: self._allow_redirect = 1 transport = self.default_transport(type, proxy, username, password) else: # # dont allow redirect on unknow transports, that should be # set up independantly # self._allow_redirect = 0 self._redirected = None self.use_handler_path = 1 self._transport = transport self._trusted_cert_files = [] self._lang = None self._encoding = encoding self._verbose = verbose self.set_refresh_callback(refreshCallback) self.set_progress_callback(progressCallback) self._headers = UserDictCase()
def __init__(self, uri, transport=None, encoding=None, verbose=0, proxy=None, username=None, password=None, refreshCallback=None, progressCallback=None, timeout=None): # establish a "logical" server connection # # First parse the proxy information if available # if proxy != None: (ph, pp, pu, pw) = get_proxy_info(proxy) if pp is not None: proxy = "%s:%s" % (ph, pp) else: proxy = ph # username and password will override whatever was passed in the # URL if pu is not None and username is None: username = pu if pw is not None and password is None: password = pw self._uri = uri self._refreshCallback = None self._progressCallback = None self._bufferSize = None self._proxy = proxy self._username = username self._password = password self._timeout = timeout if len(__version__.split()) > 1: self.rpc_version = __version__.split()[1] else: self.rpc_version = __version__ self._reset_host_handler_and_type() if transport is None: self._allow_redirect = 1 transport = self.default_transport(self._type, proxy, username, password, timeout) else: # # dont allow redirect on unknow transports, that should be # set up independantly # self._allow_redirect = 0 self._redirected = None self.use_handler_path = 1 self._transport = transport self._trusted_cert_files = [] self._lang = None self._encoding = encoding self._verbose = verbose self.set_refresh_callback(refreshCallback) self.set_progress_callback(progressCallback) # referer, which redirect us to new handler self.send_handler = None self._headers = UserDictCase()
class Server: """uri [,options] -> a logical connection to an XML-RPC server uri is the connection point on the server, given as scheme://host/target. The standard implementation always supports the "http" scheme. If SSL socket support is available (Python 2.0), it also supports "https". If the target part and the slash preceding it are both omitted, "/RPC2" is assumed. The following options can be given as keyword arguments: transport: a transport factory encoding: the request encoding (default is UTF-8) verbose: verbosity level proxy: use an HTTP proxy username: username for authenticated HTTP proxy password: password for authenticated HTTP proxy All 8-bit strings passed to the server proxy are assumed to use the given encoding. """ # Default factories _transport_class = transports.Transport _transport_class_https = transports.SafeTransport _transport_class_proxy = transports.ProxyTransport _transport_class_https_proxy = transports.SafeProxyTransport def __init__(self, uri, transport=None, encoding=None, verbose=0, proxy=None, username=None, password=None, refreshCallback=None, progressCallback=None, timeout=None): # establish a "logical" server connection # # First parse the proxy information if available # if proxy != None: (ph, pp, pu, pw) = get_proxy_info(proxy) if pp is not None: proxy = "%s:%s" % (ph, pp) else: proxy = ph # username and password will override whatever was passed in the # URL if pu is not None and username is None: username = pu if pw is not None and password is None: password = pw self._uri = uri self._refreshCallback = None self._progressCallback = None self._bufferSize = None self._proxy = proxy self._username = username self._password = password self._timeout = timeout if len(__version__.split()) > 1: self.rpc_version = __version__.split()[1] else: self.rpc_version = __version__ self._reset_host_handler_and_type() if transport is None: self._allow_redirect = 1 transport = self.default_transport(self._type, proxy, username, password, timeout) else: # # dont allow redirect on unknow transports, that should be # set up independantly # self._allow_redirect = 0 self._redirected = None self.use_handler_path = 1 self._transport = transport self._trusted_cert_files = [] self._lang = None self._encoding = encoding self._verbose = verbose self.set_refresh_callback(refreshCallback) self.set_progress_callback(progressCallback) # referer, which redirect us to new handler self.send_handler = None self._headers = UserDictCase() def default_transport(self, type, proxy=None, username=None, password=None, timeout=None): if proxy: if type == 'https': transport = self._transport_class_https_proxy( proxy, proxyUsername=username, proxyPassword=password, timeout=timeout) else: transport = self._transport_class_proxy(proxy, proxyUsername=username, proxyPassword=password, timeout=timeout) else: if type == 'https': transport = self._transport_class_https(timeout=timeout) else: transport = self._transport_class(timeout=timeout) return transport def allow_redirect(self, allow): self._allow_redirect = allow def redirected(self): if not self._allow_redirect: return None return self._redirected def set_refresh_callback(self, refreshCallback): self._refreshCallback = refreshCallback self._transport.set_refresh_callback(refreshCallback) def set_buffer_size(self, bufferSize): self._bufferSize = bufferSize self._transport.set_buffer_size(bufferSize) def set_progress_callback(self, progressCallback, bufferSize=16384): self._progressCallback = progressCallback self._transport.set_progress_callback(progressCallback, bufferSize) def _req_body(self, params, methodname): return xmlrpclib.dumps(params, methodname, encoding=self._encoding) def get_response_headers(self): if self._transport: return self._transport.headers_in return None def get_response_status(self): if self._transport: return self._transport.response_status return None def get_response_reason(self): if self._transport: return self._transport.response_reason return None def get_content_range(self): """Returns a dictionary with three values: length: the total length of the entity-body (can be None) first_byte_pos: the position of the first byte (zero based) last_byte_pos: the position of the last byte (zero based) The range is inclusive; that is, a response 8-9/102 means two bytes """ headers = self.get_response_headers() if not headers: return None content_range = headers.get('Content-Range') if not content_range: return None arr = filter(None, content_range.split()) assert arr[0] == "bytes" assert len(arr) == 2 arr = arr[1].split('/') assert len(arr) == 2 brange, total_len = arr if total_len == '*': # Per RFC, the server is allowed to use * if the length of the # entity-body is unknown or difficult to determine total_len = None else: total_len = int(total_len) start, end = brange.split('-') result = { 'length': total_len, 'first_byte_pos': int(start), 'last_byte_pos': int(end), } return result def accept_ranges(self): headers = self.get_response_headers() if not headers: return None if headers.has_key('Accept-Ranges'): return headers['Accept-Ranges'] return None def _reset_host_handler_and_type(self): """ Reset the attributes: self._host, self._handler, self._type according the value of self._uri. """ # get the url type, uri = urllib.splittype(self._uri) if type is None: raise MalformedURIError, "missing protocol in uri" # with a real uri passed in, uri will now contain "//hostname..." so we # need at least 3 chars for it to maybe be ok... if len(uri) < 3 or uri[0:2] != "//": raise MalformedURIError if type != None: self._type = type.lower() else: self._type = type if self._type not in ("http", "https"): raise IOError, "unsupported XML-RPC protocol" self._host, self._handler = urllib.splithost(uri) if not self._handler: self._handler = "/RPC2" def _strip_characters(self, *args): """ Strip characters, which are not allowed according: http://www.w3.org/TR/2006/REC-xml-20060816/#charsets From spec: Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] /* any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. */ """ regexp = r'[\x00-\x09]|[\x0b-\x0c]|[\x0e-\x1f]' result = [] for item in args: item_type = type(item) if item_type == StringType or item_type == UnicodeType: item = re.sub(regexp, '', item) elif item_type == TupleType: item = tuple(map(self._strip_characters, item)) elif item_type == ListType: item = map(self._strip_characters, item) elif item_type == DictType or item_type == DictionaryType: item = dict([(self._strip_characters(name, val)) for name, val in item.iteritems()]) # else: some object - should take care of himself # numbers - are safe result.append(item) if len(result) == 1: return result[0] else: return tuple(result) def _request(self, methodname, params): """ Call a method on the remote server we can handle redirections. """ # the loop is used to handle redirections redirect_response = 0 retry = 0 self._reset_host_handler_and_type() while 1: if retry >= MAX_REDIRECTIONS: raise InvalidRedirectionError( "Unable to fetch requested Package") # Clear the transport headers first self._transport.clear_headers() for k, v in self._headers.items(): self._transport.set_header(k, v) self._transport.add_header( "X-Info", 'RPC Processor (C) Red Hat, Inc (version %s)' % self.rpc_version) # identify the capability set of this client to the server self._transport.set_header("X-Client-Version", 1) if self._allow_redirect: # Advertise that we follow redirects #changing the version from 1 to 2 to support backward compatibility self._transport.add_header("X-RHN-Transport-Capability", "follow-redirects=3") if redirect_response: self._transport.add_header('X-RHN-Redirect', '0') if self.send_handler: self._transport.add_header('X-RHN-Path', self.send_handler) request = self._req_body(self._strip_characters(params), methodname) try: response = self._transport.request(self._host, \ self._handler, request, verbose=self._verbose) save_response = self._transport.response_status except xmlrpclib.ProtocolError, pe: if self.use_handler_path: raise else: save_response = pe.errcode self._redirected = None retry += 1 if save_response == 200: # exit redirects loop and return response break elif save_response not in (301, 302): # Retry pkg fetch self.use_handler_path = 1 continue # rest of loop is run only if we are redirected (301, 302) self._redirected = self._transport.redirected() self.use_handler_path = 0 redirect_response = 1 if not self._allow_redirect: raise InvalidRedirectionError("Redirects not allowed") if self._verbose: print "%s redirected to %s" % (self._uri, self._redirected) typ, uri = urllib.splittype(self._redirected) if typ != None: typ = typ.lower() if typ not in ("http", "https"): raise InvalidRedirectionError( "Redirected to unsupported protocol %s" % typ) # # We forbid HTTPS -> HTTP for security reasons # Note that HTTP -> HTTPS -> HTTP is allowed (because we compare # the protocol for the redirect with the original one) # if self._type == "https" and typ == "http": raise InvalidRedirectionError( "HTTPS redirected to HTTP is not supported") self._host, self._handler = urllib.splithost(uri) if not self._handler: self._handler = "/RPC2" # Create a new transport for the redirected service and # set up the parameters on the new transport del self._transport self._transport = self.default_transport(typ, self._proxy, self._username, self._password, self._timeout) self.set_progress_callback(self._progressCallback) self.set_refresh_callback(self._refreshCallback) self.set_buffer_size(self._bufferSize) self.setlang(self._lang) if self._trusted_cert_files != [] and \ hasattr(self._transport, "add_trusted_cert"): for certfile in self._trusted_cert_files: self._transport.add_trusted_cert(certfile) # Then restart the loop to try the new entry point. if isinstance(response, transports.File): # Just return the file return response # an XML-RPC encoded data structure if isinstance(response, TupleType) and len(response) == 1: response = response[0] return response
class Server: """uri [,options] -> a logical connection to an XML-RPC server uri is the connection point on the server, given as scheme://host/target. The standard implementation always supports the "http" scheme. If SSL socket support is available (Python 2.0), it also supports "https". If the target part and the slash preceding it are both omitted, "/RPC2" is assumed. The following options can be given as keyword arguments: transport: a transport factory encoding: the request encoding (default is UTF-8) verbose: verbosity level proxy: use an HTTP proxy username: username for authenticated HTTP proxy password: password for authenticated HTTP proxy All 8-bit strings passed to the server proxy are assumed to use the given encoding. """ # Default factories _transport_class = transports.Transport _transport_class_https = transports.SafeTransport _transport_class_proxy = transports.ProxyTransport _transport_class_https_proxy = transports.SafeProxyTransport def __init__(self, uri, transport=None, encoding=None, verbose=0, proxy=None, username=None, password=None, refreshCallback=None, progressCallback=None): # establish a "logical" server connection # # First parse the proxy information if available # if proxy != None: (ph, pp, pu, pw) = get_proxy_info(proxy) if pp is not None: proxy = "%s:%s" % (ph, pp) else: proxy = ph # username and password will override whatever was passed in the # URL if pu is not None and username is None: username = pu if pw is not None and password is None: password = pw self._uri = uri self._refreshCallback = None self._progressCallback = None self._bufferSize = None self._proxy = proxy self._username = username self._password = password # get the url type, uri = urllib.splittype(uri) type = (string.lower(type)).strip() self._type = type if type not in ("http", "https"): raise IOError, "unsupported XML-RPC protocol" self._host, self._handler = urllib.splithost(uri) if not self._handler: self._handler = "/RPC2" if transport is None: self._allow_redirect = 1 transport = self.default_transport(type, proxy, username, password) else: # # dont allow redirect on unknow transports, that should be # set up independantly # self._allow_redirect = 0 self._redirected = None self.use_handler_path = 1 self._transport = transport self._trusted_cert_files = [] self._lang = None self._encoding = encoding self._verbose = verbose self.set_refresh_callback(refreshCallback) self.set_progress_callback(progressCallback) self._headers = UserDictCase() def default_transport(self, type, proxy=None, username=None, password=None): if proxy: if type == 'https': transport = self._transport_class_https_proxy(proxy, proxyUsername=username, proxyPassword=password) else: transport = self._transport_class_proxy(proxy, proxyUsername=username, proxyPassword=password) else: if type == 'https': transport = self._transport_class_https() else: transport = self._transport_class() return transport def allow_redirect(self, allow): self._allow_redirect = allow def redirected(self): if not self._allow_redirect: return None return self._redirected def set_refresh_callback(self, refreshCallback): self._refreshCallback = refreshCallback self._transport.set_refresh_callback(refreshCallback) def set_buffer_size(self, bufferSize): self._bufferSize = bufferSize self._transport.set_buffer_size(bufferSize) def set_progress_callback(self, progressCallback, bufferSize=16384): self._progressCallback = progressCallback self._transport.set_progress_callback(progressCallback, bufferSize) def _req_body(self, params, methodname): return xmlrpclib.dumps(params, methodname, encoding=self._encoding) def get_response_headers(self): if self._transport: return self._transport.headers_in return None def get_response_status(self): if self._transport: return self._transport.response_status return None def get_response_reason(self): if self._transport: return self._transport.response_reason return None def get_content_range(self): """Returns a dictionary with three values: length: the total length of the entity-body (can be None) first_byte_pos: the position of the first byte (zero based) last_byte_pos: the position of the last byte (zero based) The range is inclusive; that is, a response 8-9/102 means two bytes """ headers = self.get_response_headers() if not headers: return None content_range = headers.get('Content-Range') if not content_range: return None arr = filter(None, string.split(content_range)) assert arr[0] == "bytes" assert len(arr) == 2 arr = string.split(arr[1], '/') assert len(arr) == 2 brange, total_len = arr if total_len == '*': # Per RFC, the server is allowed to use * if the length of the # entity-body is unknown or difficult to determine total_len = None else: total_len = int(total_len) start, end = string.split(brange, '-') result = { 'length' : total_len, 'first_byte_pos' : int(start), 'last_byte_pos' : int(end), } return result def accept_ranges(self): headers = self.get_response_headers() if not headers: return None if headers.has_key('Accept-Ranges'): return headers['Accept-Ranges'] return None def _request(self, methodname, params): # call a method on the remote server # the loop is used to handle redirections num = 0 redirect_response = 0 while 1: if num >= MAX_REDIRECTIONS: raise InvalidRedirectionError("Too many redirects") num = num + 1 # Clear the transport headers first self._transport.clear_headers() for k, v in self._headers.items(): self._transport.set_header(k, v) self._transport.add_header("X-Info", 'RPC Processor (C) Red Hat, Inc (version %s)' % string.split(__version__)[1]) # identify the capability set of this client to the server self._transport.set_header("X-Client-Version", 1) if self._allow_redirect: # Advertise that we follow redirects #changing the version from 1 to 2 to support backward compatibility self._transport.add_header("X-RHN-Transport-Capability", "follow-redirects=2") if redirect_response: self._transport.add_header('X-RHN-Redirect', '0') if send_handler: self._transport.add_header('X-RHN-Path', send_handler) request = self._req_body(params, methodname) try: response = self._transport.request(self._host, self._handler, request, verbose=self._verbose) save_response = self._transport.response_status except xmlrpclib.ProtocolError, pe: if self.use_handler_path: raise pe else: save_response = pe.errcode if not self._allow_redirect: raise InvalidRedirectionError("Redirects not allowed") if save_response == 200: break elif save_response not in [200,302]: self._redirected = self._uri self.use_handler_path = 1 else: self._redirected = self._transport.redirected() self.use_handler_path = 0 if self._verbose: print "%s redirected to %s" % (self._uri, self._redirected) typ, uri = urllib.splittype(self._redirected) if typ != None: typ = string.lower(typ) if typ not in ("http", "https"): raise InvalidRedirectionError( "Redirected to unsupported protocol %s" % typ) # # We forbid HTTPS -> HTTP for security reasons # Note that HTTP -> HTTPS -> HTTP is allowed (because we compare # the protocol for the redirect with the original one) # if self._type == "https" and typ == "http": raise InvalidRedirectionError( "HTTPS redirected to HTTP is not supported") self._host, self._handler = urllib.splithost(uri) if not self._handler: self._handler = "/RPC2" if save_response == 302: if not self._allow_redirect: raise InvalidRedirectionError("Redirects not allowed") else: redirect_response = 1 num = 0 continue # # Create a new transport for the redirected service and # set up the parameters on the new transport # del self._transport self._transport = self.default_transport(typ, self._proxy, self._username, self._password) self.set_progress_callback(self._progressCallback) self.set_refresh_callback(self._refreshCallback) self.set_buffer_size(self._bufferSize) self.setlang(self._lang) if self._trusted_cert_files != [] and \ hasattr(self._transport, "add_trusted_cert"): for certfile in self._trusted_cert_files: self._transport.add_trusted_cert(certfile) # # Then restart the loop to try the new entry point. # if isinstance(response, transports.File): # Just return the file return response # an XML-RPC encoded data structure if isinstance(response, TupleType) and len(response) == 1: response = response[0] return response