def testGetErrorFromHtml(self):
    """Test whether we can handle and report 502 errors."""
    trigger_msg = ('502 Server Error. The server encountered a temporary error'
                   ' and could not complete yourrequest. Please try again in 30'
                   ' seconds.')

    self.assertEqual(trigger_msg, Utils.GetErrorFromHtml(TEST_502))
Exemple #2
0
    def testError502(self):
        """Test whether we can handle and report 502 errors."""
        buf = DfaSoapBuffer()

        html_code = Utils.ReadFile(
            os.path.join('..', 'data', 'http_error_502.html'))
        buf.write(html_code)

        if not buf.IsHandshakeComplete():
            data = buf.GetBufferAsStr()
        else:
            data = ''

        self.assertEqual(Utils.GetErrorFromHtml(data),
                         self.__class__.TRIGGER_MSG)
    def testError502(self):
        """Test whether we can handle and report 502 errors."""
        # Temporarily redirect STDOUT into a buffer.
        buf = AdWordsSoapBuffer()
        sys.stdout = buf

        html_code = Utils.ReadFile(os.path.join('data', 'http_error_502.html'))
        print html_code

        # Restore STDOUT.
        sys.stdout = sys.__stdout__

        if not buf.IsHandshakeComplete():
            data = buf.GetBufferAsStr()
        else:
            data = ''

        self.assertEqual(Utils.GetErrorFromHtml(data),
                         self.__class__.TRIGGER_MSG)
Exemple #4
0
class WebService(object):
    """Implements WebService.

  Responsible for sending and recieving SOAP XML requests.
  """
    def __init__(self,
                 lib_sig,
                 headers,
                 config,
                 op_config,
                 url,
                 lock,
                 logger=None):
        """Inits WebService.

    Args:
      lib_sig: str Client library signature.
      headers: dict Dictionary object with populated authentication
               credentials.
      config: dict Dictionary object with populated configuration values.
      op_config: dict Dictionary object with additional configuration values
                 for this operation.
      url: str URL of the web service to call.
      lock: thread.lock Thread lock.
      logger: Logger Instance of Logger.
    """
        self._lib_sig = lib_sig
        self._headers = headers
        self._config = config
        self._op_config = op_config
        self._url = url
        self._lock = lock
        self._logger = logger
        self._start_time = 0
        self._stop_time = 0
        self._response = None

        if self._logger is None:
            self._logger = Logger(lib_sig, self._config['log_home'])

    def _ManageSoap(self,
                    buf,
                    log_handlers,
                    lib_url,
                    errors,
                    start_time,
                    stop_time,
                    error={}):
        """Manage SOAP XML message.

    Args:
      buf: SoapBuffer SOAP buffer.
      log_handlers: list Log handlers.
      lib_url: str URL of the project's home.
      errors: dict Map of errors available for the API.
      start_time: str Time before service call was invoked.
      stop_time: str Time after service call was invoked.
      [optional]
      error: dict Error, if any.
    """
        # Load trace errors, if any.
        if error and 'trace' in error:
            error_msg = error['trace']
        else:
            error_msg = ''

        # Check if response was successful or not.
        if error and 'data' in error:
            is_fault = True
        else:
            is_fault = False

        # Forward SOAP XML, errors, and other debugging data to console, external
        # file, both, or ignore. Each handler supports the following elements,
        #   tag: Config value for this handler. If left empty, will never write
        #        data to file.
        #   target: Target/destination represented by this handler (i.e. FILE,
        #           CONSOLE, etc.). Initially, it should be set to Logger.NONE.
        #   name: Name of the log file to use.
        #   data: Data to write.
        for handler in log_handlers:
            if handler['tag'] == 'xml_log':
                handler['target'] = Logger.NONE
                handler['data'] += str(
                    'StartTime: %s\n%s\n%s\n%s\n%s\nEndTime: %s' %
                    (start_time, buf.GetHeadersOut(), buf.GetSoapOut(),
                     buf.GetHeadersIn(), buf.GetSoapIn(), stop_time))
            elif handler['tag'] == 'request_log':
                handler['target'] = Logger.NONE
                handler['data'] += ' isFault=%s' % is_fault
            elif handler['tag'] == '':
                handler['target'] = Logger.NONE
                handler['data'] += 'DEBUG: %s' % error_msg
        for handler in log_handlers:
            if (handler['tag']
                    and Utils.BoolTypeConvert(self._config[handler['tag']])):
                handler['target'] = Logger.FILE
            # If debugging is On, raise handler's target two levels,
            #   NONE -> CONSOLE
            #   FILE -> FILE_AND_CONSOLE.
            if Utils.BoolTypeConvert(self._config['debug']):
                handler['target'] += 2

            if (handler['target'] != Logger.NONE and handler['data']
                    and handler['data'] != 'None'
                    and handler['data'] != 'DEBUG: '):
                self._logger.Log(handler['name'],
                                 handler['data'],
                                 log_level=Logger.DEBUG,
                                 log_handler=handler['target'])

        # If raw response is requested, no need to validate and throw appropriate
        # error. Up to the end user to handle successful or failed request.
        if Utils.BoolTypeConvert(self._config['raw_response']): return

        # Report SOAP fault.
        if is_fault:
            try:
                fault = buf.GetFaultAsDict()
                if not fault: msg = error['data']
            except:
                fault = None
                # An error is not a SOAP fault, but check if some other error.
                if error_msg:
                    msg = error_msg
                else:
                    msg = ('Unable to parse incoming SOAP XML. Please, file '
                           'a bug at %s/issues/list.' % lib_url)
            # Release thread lock.
            if self._lock.locked(): self._lock.release()
            if not fault and msg: return msg
            return fault
        return None

    def CallMethod(self,
                   headers,
                   config,
                   method_name,
                   params,
                   buf,
                   is_jaxb_api,
                   lib_sig,
                   lib_url,
                   service_name=None,
                   loc=None,
                   request=None):
        """Make an API call to specified method.

    Args:
      headers: dict Dictionary object with populated authentication
               credentials.
      config: dict Dictionary object with populated configuration values.
      method_name: str API method name.
      params: list List of parameters to send to the API method.
      buf: SoapBuffer SOAP buffer.
      is_jaxb_api: str Whether API uses JAXB.
      lib_sig: str Signature of the client library.
      lib_url: str URL of the project's home.
      [optional]
      service_name: str API service name.
      loc: Service locator.
      request: instance Holder of the SOAP request.

    Returns:
      tuple/str Response from the API method. If 'raw_response' flag enabled a
                string is returned, tuple otherwise.
    """
        # Temporarily redirect HTTP headers and SOAP from STDOUT into a buffer.
        if not Utils.BoolTypeConvert(self._config['raw_debug']):
            old_stdout = sys.stdout
            sys.stdout = buf

        response = ()
        error = {}
        try:
            # Fire off API request and handle the response.
            if config['soap_lib'] == SOAPPY:
                from adspygoogle.common.soappy import MessageHandler
                service = MessageHandler.GetServiceConnection(
                    headers, config, self._url, self._op_config['http_proxy'],
                    is_jaxb_api)

                if not is_jaxb_api:
                    response = MessageHandler.UnpackResponseAsDict(
                        service.invoke(method_name, params))
                else:
                    if not params: params = ('')
                    response = MessageHandler.UnpackResponseAsDict(
                        service._callWithBody(
                            MessageHandler.SetRequestParams(
                                config, method_name, params)))
            elif config['soap_lib'] == ZSI:
                from adspygoogle.common.zsi import MessageHandler
                service = MessageHandler.GetServiceConnection(
                    headers, config, self._op_config, self._url, service_name,
                    loc)
                request = MessageHandler.SetRequestParams(
                    request, params, is_jaxb_api)

                response = MessageHandler.UnpackResponseAsTuple(
                    eval('service.%s(request)' % method_name))

                # The response should always be tuple. If it's not, there must be
                # something wrong with MessageHandler.UnpackResponseAsTuple().
                if len(response) == 1 and isinstance(response[0], list):
                    response = tuple(response[0])

            if isinstance(response, list):
                response = tuple(response)
            elif isinstance(response, tuple):
                pass
            else:
                if response:
                    response = (response, )
                else:
                    response = ()
        except Exception, e:
            error['data'] = e

        # Restore STDOUT.
        if not Utils.BoolTypeConvert(self._config['raw_debug']):
            sys.stdout = old_stdout

        # When debugging mode is ON, fetch last traceback.
        if Utils.BoolTypeConvert(self._config['debug']):
            if Utils.LastStackTrace() and Utils.LastStackTrace() != 'None':
                error['trace'] = Utils.LastStackTrace()

        # Catch local errors prior to going down to the SOAP layer, which may not
        # exist for this error instance.
        if 'data' in error and not buf.IsHandshakeComplete():
            # Check if buffer contains non-XML data, most likely an HTML page. This
            # happens in the case of 502 errors (and similar). Otherwise, this is a
            # local error and API request was never made.
            html_error = Utils.GetErrorFromHtml(buf.GetBufferAsStr())
            if html_error:
                msg = '%s' % html_error
            else:
                msg = str(error['data'])
                if Utils.BoolTypeConvert(self._config['debug']):
                    msg += '\n%s' % error['trace']

            # When debugging mode is ON, store the raw content of the buffer.
            if Utils.BoolTypeConvert(self._config['debug']):
                error['raw_data'] = buf.GetBufferAsStr()

            # Catch errors from AuthToken and ValidationError levels, raised during
            # try/except above.
            if isinstance(error['data'], AuthTokenError):
                raise AuthTokenError(msg)
            elif isinstance(error['data'], ValidationError):
                raise ValidationError(error['data'])
            if 'raw_data' in error:
                msg = '%s [RAW DATA: %s]' % (msg, error['raw_data'])
            return Error(msg)

        if Utils.BoolTypeConvert(self._config['raw_response']):
            response = buf.GetRawSOAPIn()
        if error: response = error
        return response
Exemple #5
0
    def CallRawMethod(self, buf, host, soap_message):
        """Make an API call by posting raw SOAP XML message.

    Args:
      buf: SoapBuffer SOAP buffer.
      host: str Host against which to make API request.
      soap_message: str SOAP XML message.

    Returns:
      tuple Response from the API method.
    """
        http_header = {
            'post': '%s' % self._url,
            'host': host,
            'user_agent': '%s; WebService.py' % self._lib_sig,
            'content_type': 'text/xml; charset=\"UTF-8\"',
            'content_length': '%d' % len(soap_message),
            'soap_action': ''
        }

        self._start_time = time.strftime('%Y-%m-%d %H:%M:%S')
        buf.write((
            '%s Outgoing HTTP headers %s\nPOST %s\nHost: %s\nUser-Agent: '
            '%s\nContent-type: %s\nContent-length: %s\nSOAPAction: %s\n%s\n%s '
            'Outgoing SOAP %s\n%s\n%s\n' %
            ('*' * 3, '*' * 46, http_header['post'], http_header['host'],
             http_header['user_agent'], http_header['content_type'],
             http_header['content_length'], http_header['soap_action'],
             '*' * 72, '*' * 3, '*' * 54, soap_message, '*' * 72)))

        # Construct header and send SOAP message.
        web_service = httplib.HTTPS(http_header['host'])
        web_service.putrequest('POST', http_header['post'])
        web_service.putheader('Host', http_header['host'])
        web_service.putheader('User-Agent', http_header['user_agent'])
        web_service.putheader('Content-type', http_header['content_type'])
        web_service.putheader('Content-length', http_header['content_length'])
        web_service.putheader('SOAPAction', http_header['soap_action'])
        web_service.endheaders()
        web_service.send(soap_message)

        # Get response.
        status_code, status_message, header = web_service.getreply()
        self._response = web_service.getfile().read()

        header = str(header).replace('\r', '')
        buf.write(
            ('%s Incoming HTTP headers %s\n%s %s\n%s\n%s\n%s Incoming SOAP'
             ' %s\n%s\n%s\n' %
             ('*' * 3, '*' * 46, status_code, status_message, header, '*' * 72,
              '*' * 3, '*' * 54, self._response, '*' * 72)))
        self._stop_time = time.strftime('%Y-%m-%d %H:%M:%S')

        # Catch local errors prior to going down to the SOAP layer, which may not
        # exist for this error instance.
        if not buf.IsHandshakeComplete() or not buf.IsSoap():
            # The buffer contains non-XML data, most likely an HTML page. This
            # happens in the case of 502 errors.
            html_error = Utils.GetErrorFromHtml(buf.GetBufferAsStr())
            if html_error:
                msg = '%s' % html_error
            else:
                msg = 'Unknown error.'
            raise Error(msg)
Exemple #6
0
    def CallRawMethod(self, soap_message):
        """Makes an API call by POSTing a raw SOAP XML message to the server.

    Args:
      soap_message: str SOAP XML message.

    Returns:
      tuple Raw XML response from the API method.

    Raises:
      Error: if the SOAP call is not successful. Most likely this is the result
      of the server sending back an HTTP error, such as a 502.
    """

        self._lock.acquire()
        try:
            buf = self._buffer_class(xml_parser=self._config['xml_parser'],
                                     pretty_xml=Utils.BoolTypeConvert(
                                         self._config['pretty_xml']))

            http_header = {
                'post': self._service_url,
                'host': Utils.GetNetLocFromUrl(self._op_config['server']),
                'user_agent': '%s; CallRawMethod' % self.__class__.__name__,
                'content_type': 'text/xml; charset=\"UTF-8\"',
                'content_length': '%d' % len(soap_message),
                'soap_action': ''
            }

            if self._headers.get('oauth2credentials'):
                self._headers['oauth2credentials'].apply(http_header)

            self._start_time = time.strftime('%Y-%m-%d %H:%M:%S')
            buf.write(
                '%s Outgoing HTTP headers %s\nPOST %s\nHost: %s\nUser-Agent: '
                '%s\nContent-type: %s\nContent-length: %s\nSOAPAction: %s\n' %
                ('*' * 3, '*' * 46, http_header['post'], http_header['host'],
                 http_header['user_agent'], http_header['content_type'],
                 http_header['content_length'], http_header['soap_action']))
            if self._headers.get('oauth2credentials'):
                buf.write('Authorization: ' + http_header['Authorization'] +
                          '\n')
            buf.write('%s\n%s Outgoing SOAP %s\n%s\n%s\n' %
                      ('*' * 72, '*' * 3, '*' * 54, soap_message, '*' * 72))

            if self._op_config['http_proxy']:
                real_address = self._op_config['http_proxy']
            else:
                real_address = http_header['host']

            # Construct header and send SOAP message.
            web_service = httplib.HTTPS(real_address)
            web_service.putrequest('POST', http_header['post'])
            web_service.putheader('Host', http_header['host'])
            web_service.putheader('User-Agent', http_header['user_agent'])
            web_service.putheader('Content-type', http_header['content_type'])
            web_service.putheader('Content-length',
                                  http_header['content_length'])
            web_service.putheader('SOAPAction', http_header['soap_action'])
            if self._headers.get('oauth2credentials'):
                web_service.putheader('Authorization',
                                      http_header['Authorization'])
            web_service.endheaders()
            web_service.send(soap_message)

            # Get response.
            status_code, status_message, header = web_service.getreply()
            response = web_service.getfile().read()

            header = str(header).replace('\r', '')
            buf.write(
                ('%s Incoming HTTP headers %s\n%s %s\n%s\n%s\n%s Incoming SOAP'
                 ' %s\n%s\n%s\n' %
                 ('*' * 3, '*' * 46, status_code, status_message, header,
                  '*' * 72, '*' * 3, '*' * 54, response, '*' * 72)))
            self._stop_time = time.strftime('%Y-%m-%d %H:%M:%S')

            # Catch local errors prior to going down to the SOAP layer, which may not
            # exist for this error instance.
            if not buf.IsHandshakeComplete() or not buf.IsSoap():
                # The buffer contains non-XML data, most likely an HTML page. This
                # happens in the case of 502 errors.
                html_error = Utils.GetErrorFromHtml(buf.GetBufferAsStr())
                if html_error:
                    msg = html_error
                else:
                    msg = 'Unknown error.'
                raise Error(msg)

            self._HandleLogsAndErrors(buf, self._start_time, self._stop_time)
        finally:
            self._lock.release()
        if self._config['wrap_in_tuple']:
            response = MessageHandler.WrapInTuple(response)
        return response
Exemple #7
0
        def CallMethod(*args):
            """Perform a SOAP call."""
            try:
                self._lock.acquire()
                self._ReadyOAuth()
                self._ReadyCompression()
                self._SetHeaders()

                args = self._TakeActionOnSoapCall(method_name, args)
                method_info = self._GetMethodInfo(method_name)
                method_attrs_holder = None

                if len(method_info[MethodInfoKeys.INPUTS]) > 1:
                    self._ConfigureArgOrder(method_name,
                                            method_info[MethodInfoKeys.INPUTS])

                if not method_info[MethodInfoKeys.INPUTS]:
                    # Don't put any namespaces other than this service's namespace on
                    # calls with no input params.
                    method_attrs_holder = self._soappyservice.soapproxy.methodattrs
                    self._soappyservice.soapproxy.methodattrs = {
                        'xmlns': self._namespace
                    }

                if len(args) != len(method_info[MethodInfoKeys.INPUTS]):
                    raise TypeError(''.join([
                        method_name + '() takes exactly ',
                        str(
                            len(self._soappyservice.methods[method_name].
                                inparams)), ' argument(s). (',
                        str(len(args)), ' given)'
                    ]))

                ksoap_args = {}
                for i in range(len(method_info[MethodInfoKeys.INPUTS])):
                    if Utils.BoolTypeConvert(self._config['strict']):
                        SanityCheck.SoappySanityCheck(
                            self._soappyservice, args[i], method_info[
                                MethodInfoKeys.INPUTS][i][MethodInfoKeys.NS],
                            method_info[MethodInfoKeys.INPUTS][i][
                                MethodInfoKeys.TYPE],
                            method_info[MethodInfoKeys.INPUTS][i][
                                MethodInfoKeys.MAX_OCCURS])

                    element_name = str(method_info[MethodInfoKeys.INPUTS][i][
                        MethodInfoKeys.ELEMENT_NAME])

                    ksoap_args[element_name] = MessageHandler.PackForSoappy(
                        args[i], method_info[MethodInfoKeys.INPUTS][i][
                            MethodInfoKeys.NS], method_info[
                                MethodInfoKeys.INPUTS][i][MethodInfoKeys.TYPE],
                        self._soappyservice, self._wrap_lists,
                        self._namespace_extractor)

                ksoap_args = self._TakeActionOnPackedArgs(
                    method_name, ksoap_args)

                buf = self._buffer_class(xml_parser=self._config['xml_parser'],
                                         pretty_xml=Utils.BoolTypeConvert(
                                             self._config['pretty_xml']))
                sys_stdout_monkey_lock.acquire()
                try:
                    old_stdout = sys.stdout
                    sys.stdout = buf

                    error = {}
                    response = None
                    start_time = time.strftime('%Y-%m-%d %H:%M:%S')
                    try:
                        response = MessageHandler.UnpackResponseAsDict(
                            soap_service_method(**ksoap_args))
                    except Exception, e:
                        error['data'] = e
                    stop_time = time.strftime('%Y-%m-%d %H:%M:%S')
                    # Restore stdout
                    sys.stdout = old_stdout
                finally:
                    sys_stdout_monkey_lock.release()

                if isinstance(response, Error):
                    error = response

                if not Utils.BoolTypeConvert(self._config['raw_debug']):
                    self._HandleLogsAndErrors(buf, start_time, stop_time,
                                              error)

                # When debugging mode is ON, fetch last traceback.
                if Utils.BoolTypeConvert(self._config['debug']):
                    if Utils.LastStackTrace(
                    ) and Utils.LastStackTrace() != 'None':
                        error['trace'] = Utils.LastStackTrace()

                # Catch local errors prior to going down to the SOAP layer, which may
                # not exist for this error instance.
                if 'data' in error and not buf.IsHandshakeComplete():
                    # Check if buffer contains non-XML data, most likely an HTML page.
                    # This happens in the case of 502 errors (and similar). Otherwise,
                    # this is a local error and API request was never made.
                    html_error = Utils.GetErrorFromHtml(buf.GetBufferAsStr())
                    if html_error:
                        msg = html_error
                    else:
                        msg = str(error['data'])
                        if Utils.BoolTypeConvert(self._config['debug']):
                            msg += '\n%s' % error['trace']

                    # When debugging mode is ON, store the raw content of the buffer.
                    if Utils.BoolTypeConvert(self._config['debug']):
                        error['raw_data'] = buf.GetBufferAsStr()

                    # Catch errors from AuthToken and ValidationError levels, raised
                    # during try/except above.
                    if isinstance(error['data'], AuthTokenError):
                        raise AuthTokenError(msg)
                    elif isinstance(error['data'], ValidationError):
                        raise ValidationError(error['data'])
                    if 'raw_data' in error:
                        msg = '%s [RAW DATA: %s]' % (msg, error['raw_data'])
                    return Error(msg)

                if Utils.BoolTypeConvert(self._config['raw_response']):
                    response = buf.GetRawSoapIn()
                elif error:
                    response = error
                else:
                    output_types = [
                        (out_param[MethodInfoKeys.NS],
                         out_param[MethodInfoKeys.TYPE],
                         out_param[MethodInfoKeys.MAX_OCCURS])
                        for out_param in method_info[MethodInfoKeys.OUTPUTS]
                    ]
                    response = MessageHandler.RestoreListTypeWithSoappy(
                        response, self._soappyservice, output_types)

                if Utils.BoolTypeConvert(self._config['wrap_in_tuple']):
                    response = MessageHandler.WrapInTuple(response)

                # Restore method_attrs if they were over-ridden
                if method_attrs_holder:
                    self._soappyservice.soapproxy.methodattrs = method_attrs_holder

                return response