Example #1
0
    def expect(self, **kwargs):
        for name, routes in iteritems(kwargs):
            service = self._registry.setdefault(name, ServiceMock({}))
            service.routes.update(routes)
            setattr(self._config, name, 'http://' + name + '/')

        return self
Example #2
0
    def group(self, futures, callback=None, name=None):
        if callable(callback):
            results_holder = {}
            group_callback = self.handler.finish_group.add(
                partial(callback, results_holder))

            def delay_cb():
                IOLoop.instance().add_callback(
                    self.handler.check_finished(group_callback))

            async_group = AsyncGroup(delay_cb,
                                     logger=self.handler.log,
                                     name=name)

            def future_callback(name, future):
                results_holder[name] = future.result()

            for name, future in iteritems(futures):
                if future.done():
                    future_callback(name, future)
                else:
                    self.handler.add_future(
                        future, async_group.add(partial(future_callback,
                                                        name)))

            async_group.try_finish()

        return futures
Example #3
0
def request_to_xml(request):
    content_type = request.headers.get('Content-Type', '')
    body = etree.Element('body', content_type=content_type)

    if request.body:
        try:
            if 'json' in content_type:
                body.text = _pretty_print_json(json.loads(request.body))
            elif 'protobuf' in content_type:
                body.text = repr(request.body)
            else:
                body_query = urlparse.parse_qs(str(request.body), True)
                for name, values in iteritems(body_query):
                    for value in values:
                        body.append(
                            E.param(to_unicode(value), name=to_unicode(name)))
        except Exception:
            debug_log.exception('cannot parse request body')
            body.text = repr(request.body)

    try:
        request = E.request(body,
                            E.start_time(_format_number(request.start_time)),
                            E.method(request.method), E.url(request.url),
                            _params_to_xml(request.url),
                            _headers_to_xml(request.headers),
                            _cookies_to_xml(request.headers),
                            E.curl(request_to_curl_string(request)))
    except Exception:
        debug_log.exception('cannot parse request body')
        body.text = repr(request.body)
        request = E.request(body)

    return request
Example #4
0
    def _parse_response(self, future, callback, error_callback, parse_response,
                        parse_on_error, response):
        data = None
        result = RequestResult()

        try:
            if response.error and not parse_on_error:
                self._set_response_error(response)
            elif not parse_response:
                data = response.body
            elif response.code != 204:
                content_type = response.headers.get('Content-Type', '')
                for k, v in iteritems(DEFAULT_REQUEST_TYPES):
                    if k.search(content_type):
                        data = v(response, logger=self.handler.log)
                        break
        except FailedRequestException as ex:
            result.set_exception(ex)

        if callable(error_callback) and (response.error
                                         or result.exception is not None):
            error_callback(data, response)
        elif callable(callback):
            callback(data, response)

        result.set(data, response)
        future.set_result(result)
Example #5
0
    def _parse_response(self, future, callback, error_callback, parse_response, parse_on_error, response):
        data = None
        result = RequestResult()

        try:
            if response.error and not parse_on_error:
                self._set_response_error(response)
            elif not parse_response:
                data = response.body
            elif response.code != 204:
                content_type = response.headers.get('Content-Type', '')
                for k, v in iteritems(DEFAULT_REQUEST_TYPES):
                    if k.search(content_type):
                        data = v(response, logger=self.handler.log)
                        break
        except FailedRequestException as ex:
            result.set_exception(ex)

        if callable(error_callback) and (response.error or result.exception is not None):
            error_callback(data, response)
        elif callable(callback):
            callback(data, response)

        result.set(data, response)
        future.set_result(result)
def _headers_to_xml(request_or_response_headers):
    headers = etree.Element('headers')
    for name, value in iteritems(request_or_response_headers):
        if name != 'Cookie':
            str_value = value if isinstance(value,
                                            basestring_type) else str(value)
            headers.append(E.header(to_unicode(str_value), name=name))
    return headers
 def update_user_info(self, user_id=None, ip=None, username=None, email=None):
     new_data = {
         'id': user_id,
         'username': username,
         'email': email,
         'ip_address': ip,
     }
     new_data = {k: v for k, v in iteritems(new_data) if v is not None}
     self.user_info.update(new_data)
Example #8
0
def make_qs(query_args):
    kv_pairs = []
    for key, val in iteritems(query_args):
        if val is not None:
            encoded_key = _encode(key)
            if isinstance(val, (set, frozenset, list, tuple)):
                for v in val:
                    kv_pairs.append((encoded_key, _encode(v)))
            else:
                kv_pairs.append((encoded_key, _encode(val)))

    return urlencode(kv_pairs)
Example #9
0
def make_qs(query_args):
    kv_pairs = []
    for key, val in iteritems(query_args):
        if val is not None:
            encoded_key = _encode(key)
            if isinstance(val, (set, frozenset, list, tuple)):
                for v in val:
                    kv_pairs.append((encoded_key, _encode(v)))
            else:
                kv_pairs.append((encoded_key, _encode(val)))

    return urlencode(kv_pairs)
def _params_to_xml(url, logger=debug_log):
    params = etree.Element('params')
    query = frontik.util.get_query_parameters(url)
    for name, values in iteritems(query):
        for value in values:
            try:
                params.append(E.param(to_unicode(value),
                                      name=to_unicode(name)))
            except UnicodeDecodeError:
                logger.exception('cannot decode parameter name or value')
                params.append(E.param(repr(value), name=repr(name)))
    return params
    def write_error(self, status_code=500, **kwargs):
        # write_error in Frontik must be asynchronous when handling custom errors (due to XSLT)
        # e.g. raise HTTPError(503) is syncronous and generates a standard Tornado error page,
        # whereas raise HTTPError(503, xml=...) will call finish_with_postprocessors()

        # the solution is to move self.finish() from send_error to write_error
        # so any write_error override must call either finish() or finish_with_postprocessors() in the end

        # in Tornado 3 it may be better to rewrite this mechanism with futures

        if 'exc_info' in kwargs:
            exception = kwargs['exc_info'][1]
        else:
            exception = None

        headers = getattr(exception, 'headers', None)
        override_content = any(
            getattr(exception, x, None) is not None
            for x in ('text', 'xml', 'json'))

        finish_with_exception = exception is not None and (
            199 < status_code < 400
            or  # raise HTTPError(200) to finish page immediately
            override_content)

        if headers:
            for (name, value) in iteritems(headers):
                self.set_header(name, value)

        if finish_with_exception:
            self.json.clear()

            if getattr(exception, 'text', None) is not None:
                self.doc.clear()
                self.text = exception.text
            elif getattr(exception, 'json', None) is not None:
                self.text = None
                self.doc.clear()
                self.json.put(exception.json)
            elif getattr(exception, 'xml', None) is not None:
                self.text = None
                # cannot clear self.doc due to backwards compatibility, a bug actually
                self.doc.put(exception.xml)

            self.finish_with_postprocessors()
            return

        self.set_header('Content-Type', 'text/html; charset=UTF-8')
        return super(BaseHandler, self).write_error(status_code, **kwargs)
Example #12
0
    def __init__(self, **application_kwargs):
        self.log = getLogger('service_mock')
        self._config = EmptyEnvironment.LocalHandlerConfig()

        self._request = tornado.httpserver.HTTPRequest('GET',
                                                       '/',
                                                       remote_ip='127.0.0.1')
        self._request.connection = DummyConnection()

        self._registry = {}
        self._response_text = None

        self.application_kwargs = application_kwargs
        for (key, value) in iteritems(application_kwargs):
            setattr(self, key, value)
Example #13
0
    def write_error(self, status_code=500, **kwargs):
        # write_error in Frontik must be asynchronous when handling custom errors (due to XSLT)
        # e.g. raise HTTPError(503) is syncronous and generates a standard Tornado error page,
        # whereas raise HTTPError(503, xml=...) will call finish_with_postprocessors()

        # the solution is to move self.finish() from send_error to write_error
        # so any write_error override must call either finish() or finish_with_postprocessors() in the end

        # in Tornado 3 it may be better to rewrite this mechanism with futures

        if 'exc_info' in kwargs:
            exception = kwargs['exc_info'][1]
        else:
            exception = None

        headers = getattr(exception, 'headers', None)
        override_content = any(getattr(exception, x, None) is not None for x in ('text', 'xml', 'json'))

        finish_with_exception = exception is not None and (
            199 < status_code < 400 or  # raise HTTPError(200) to finish page immediately
            override_content
        )

        if headers:
            for (name, value) in iteritems(headers):
                self.set_header(name, value)

        if finish_with_exception:
            self.json.clear()

            if getattr(exception, 'text', None) is not None:
                self.doc.clear()
                self.text = exception.text
            elif getattr(exception, 'json', None) is not None:
                self.text = None
                self.doc.clear()
                self.json.put(exception.json)
            elif getattr(exception, 'xml', None) is not None:
                self.text = None
                # cannot clear self.doc due to backwards compatibility, a bug actually
                self.doc.put(exception.xml)

            self.finish_with_postprocessors()
            return

        self.set_header('Content-Type', 'text/html; charset=UTF-8')
        return super(BaseHandler, self).write_error(status_code, **kwargs)
Example #14
0
    def _call_function(self, handler_class, raise_exceptions=True):
        # Create application with the only route — handler_class
        application = application_mock([('', handler_class)],
                                       self._config)(**{
                                           'app': 'frontik.testing',
                                       })

        for (key, value) in iteritems(self.application_kwargs):
            setattr(application, key, value)

        # Mock methods

        def fetch(request, callback, **kwargs):
            IOLoop.instance().add_callback(
                partial(self._fetch_mock, request, callback, **kwargs))

        application.curl_http_client.fetch = fetch

        # raise_exceptions kwarg is deprecated
        if raise_exceptions:
            exceptions = []
            old_handle_request_exception = handler_class._handle_request_exception

            def handle_request_exception(handler, e):
                old_handle_request_exception(handler, e)
                exceptions.append(sys.exc_info())

            handler_class._handle_request_exception = handle_request_exception

        old_flush = handler_class.flush

        def flush(handler, *args, **kwargs):
            self._response_text = b''.join(handler._write_buffer)
            old_flush(handler, *args, **kwargs)
            IOLoop.instance().add_callback(IOLoop.instance().stop)

        handler_class.flush = flush

        self._handler = application(self._request)
        IOLoop.instance().start()

        if raise_exceptions and exceptions:
            raise_exc_info(exceptions[0])

        return TestResult(self._config, self._request, self._handler,
                          self._response_text)
Example #15
0
    def handle_return_value(self, handler_method_name, return_value):
        def _fail_on_error_wrapper(name, data, response):
            error_method_name = handler_method_name + '_requests_failed'
            if hasattr(self, error_method_name):
                getattr(self, error_method_name)(name, data, response)

            status_code = response.code if 300 <= response.code < 500 else 502
            raise HTTPError(status_code, 'HTTP request failed with code {}'.format(response.code))

        def _httpfuture_fail_on_error_result_check(name, future):
            result = future.result()
            if not result.response.error and not result.exception:
                return

            error_method_name = handler_method_name + '_requests_failed'
            if hasattr(self, error_method_name):
                getattr(self, error_method_name)(name, result.data, result.response)

            status_code = result.response.code if 300 <= result.response.code < 500 else 502
            raise HTTPError(status_code, 'HTTP request failed with code {}'.format(result.response.code))

        if isinstance(return_value, dict):
            futures = {}
            for name, req in iteritems(return_value):
                if isinstance(req, Future):
                    if isinstance(req, HTTPResponseFuture) and req.fail_on_error:
                        self.add_future(req, partial(_httpfuture_fail_on_error_result_check, name))
                    futures[name] = req

                else:
                    req_type = getattr(req, 'method', None)
                    if req_type not in self._METHODS_MAPPING:
                        raise Exception('Invalid request object: {!r}'.format(req))

                    if req.kwargs.pop('fail_on_error'):
                        req.kwargs['error_callback'] = partial(_fail_on_error_wrapper, name)

                    method = self._METHODS_MAPPING[req_type]
                    url = self.make_url(req.host, req.uri)
                    futures[name] = method(url, **req.kwargs)

            done_method_name = handler_method_name + '_requests_done'
            self._http_client.group(futures, getattr(self, done_method_name, None), name='MicroHandler')

        elif return_value is not None:
            raise Exception('Invalid return type: {}'.format(type(return_value)))
Example #16
0
    def handle_return_value(self, handler_method_name, return_value):
        def _future_fail_on_error_handler(name, future):
            result = future.result()
            if not isinstance(result, RequestResult):
                return

            if not result.response.error and not result.exception:
                return

            error_method_name = handler_method_name + '_requests_failed'
            if hasattr(self, error_method_name):
                getattr(self, error_method_name)(name, result.data,
                                                 result.response)

            status_code = result.response.code if 300 <= result.response.code < 500 else 502
            raise HTTPError(
                status_code, 'HTTP request failed with code {}'.format(
                    result.response.code))

        if isinstance(return_value, dict):
            futures = {}
            for name, future in iteritems(return_value):
                # Use is_future with Tornado 4
                if not isinstance(future, Future):
                    raise Exception(
                        'Invalid MicroHandler return value: {!r}'.format(
                            future))

                if getattr(future, 'fail_on_error', False):
                    self.add_future(
                        future,
                        self.finish_group.add(
                            partial(_future_fail_on_error_handler, name)))

                futures[name] = future

            done_method_name = handler_method_name + '_requests_done'
            self._http_client.group(futures,
                                    getattr(self, done_method_name, None),
                                    name='MicroHandler')

        elif return_value is not None:
            raise Exception('Invalid return type: {}'.format(
                type(return_value)))
Example #17
0
    def group(self, futures, callback=None, name=None):
        if callable(callback):
            results_holder = {}
            group_callback = self.handler.finish_group.add(
                self.handler.check_finished(callback, results_holder))

            async_group = AsyncGroup(group_callback, name=name)

            def future_callback(name, future):
                results_holder[name] = future.result()

            for name, future in iteritems(futures):
                if future.done():
                    future_callback(name, future)
                else:
                    self.handler.add_future(
                        future, async_group.add(partial(future_callback,
                                                        name)))

            async_group.try_finish_async()

        return futures
Example #18
0
    def group(self, futures, callback=None, name=None):
        if callable(callback):
            results_holder = {}
            group_callback = self.handler.finish_group.add(partial(callback, results_holder))

            def delay_cb():
                IOLoop.instance().add_callback(self.handler.check_finished(group_callback))

            async_group = AsyncGroup(delay_cb, logger=self.handler.log, name=name)

            def future_callback(name, future):
                results_holder[name] = future.result()

            for name, future in iteritems(futures):
                if future.done():
                    future_callback(name, future)
                else:
                    self.handler.add_future(future, async_group.add(partial(future_callback, name)))

            async_group.try_finish()

        return futures
Example #19
0
    def write_error(self, status_code=500, **kwargs):
        """`write_error` can call `finish` asynchronously.
        This allows, for example, asynchronous templating on error pages.
        """

        if 'exc_info' in kwargs:
            exception = kwargs['exc_info'][1]
        else:
            exception = None

        headers = getattr(exception, 'headers', None)
        override_content = any(getattr(exception, x, None) is not None for x in ('text', 'xml', 'json'))
        finish_with_exception = isinstance(exception, HTTPError) and override_content

        if headers:
            for (name, value) in iteritems(headers):
                self.set_header(name, value)

        if finish_with_exception:
            self.json.clear()

            if getattr(exception, 'text', None) is not None:
                self.doc.clear()
                self.text = exception.text
            elif getattr(exception, 'json', None) is not None:
                self.text = None
                self.doc.clear()
                self.json.put(exception.json)
            elif getattr(exception, 'xml', None) is not None:
                self.text = None
                # cannot clear self.doc due to backwards compatibility, a bug actually
                self.doc.put(exception.xml)

            self.finish_with_postprocessors()
            return

        self.set_header('Content-Type', 'text/html; charset=UTF-8')
        return super(BaseHandler, self).write_error(status_code, **kwargs)
Example #20
0
 def add_arguments(self, arguments):
     for key, val in iteritems(arguments):
         self._request.arguments[key] = [val] if isinstance(
             val, basestring_type) else val
     return self
Example #21
0
 def get_error_node(exception):
     return {
         'error': {k: v for k, v in iteritems(exception.attrs)}
     }
def response_to_xml(response):
    time_info = etree.Element('time_info')
    content_type = response.headers.get('Content-Type', '')
    mode = ''

    if 'charset' in content_type:
        charset = content_type.partition('=')[-1]
    else:
        charset = 'utf-8'

    try_charsets = (charset, 'cp1251')

    try:
        if 'text/html' in content_type:
            body = frontik.util.decode_string_from_charset(
                response.body, try_charsets)
            body = body.replace('\n',
                                '\\n').replace("'",
                                               "\\'").replace("<", "&lt;")
        elif 'protobuf' in content_type:
            body = repr(response.body)
        elif response.body is None:
            body = ''
        elif 'xml' in content_type:
            mode = 'xml'
            body = _pretty_print_xml(etree.fromstring(response.body))
        elif 'json' in content_type:
            mode = 'javascript'
            body = _pretty_print_json(json.loads(response.body))
        else:
            if 'javascript' in content_type:
                mode = 'javascript'
            body = frontik.util.decode_string_from_charset(
                response.body, try_charsets)

    except Exception:
        debug_log.exception('cannot parse response body')
        body = repr(response.body)

    try:
        for name, value in iteritems(response.time_info):
            time_info.append(E.time(str(value), name=name))
    except Exception:
        debug_log.exception('cannot append time info')

    try:
        response = E.response(
            E.body(body, content_type=content_type, mode=mode),
            E.code(str(response.code)),
            E.effective_url(response.effective_url),
            E.error(str(response.error)),
            E.size(
                str(len(response.body)) if response.body is not None else '0'),
            E.request_time(_format_number(response.request_time * 1000)),
            _headers_to_xml(response.headers),
            time_info,
        )
    except Exception:
        debug_log.exception('cannot log response info')
        response = E.response(E.body('Cannot log response info'))
    return response
Example #23
0
def make_mfd(fields, files):
    """
    Constructs request body in multipart/form-data format

    fields :: { field_name : field_value }
    files :: { field_name: [{ "filename" : fn, "body" : bytes }]}
    """
    def addslashes(text):
        for s in (b'\\', b'"'):
            if s in text:
                text = text.replace(s, b'\\' + s)
        return text

    def create_field(name, data):
        name = addslashes(any_to_bytes(name))

        return [
            b'--', BOUNDARY,
            b'\r\nContent-Disposition: form-data; name="', name,
            b'"\r\n\r\n', any_to_bytes(data), b'\r\n'
        ]

    def create_file_field(name, filename, data, content_type):
        if content_type == 'application/unknown':
            content_type = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
        else:
            content_type = content_type.replace('\n', ' ').replace('\r', ' ')

        name = addslashes(any_to_bytes(name))
        filename = addslashes(any_to_bytes(filename))

        return [
            b'--', BOUNDARY,
            b'\r\nContent-Disposition: form-data; name="', name, b'"; filename="', filename,
            b'"\r\nContent-Type: ', any_to_bytes(content_type),
            b'\r\n\r\n', any_to_bytes(data), b'\r\n'
        ]

    body = []

    for name, data in iteritems(fields):
        if data is None:
            continue

        if isinstance(data, list):
            for value in data:
                if value is not None:
                    body.extend(create_field(name, value))
        else:
            body.extend(create_field(name, data))

    for name, files in iteritems(files):
        for file in files:
            body.extend(create_file_field(
                name, file['filename'], file['body'], file.get('content_type', 'application/unknown')
            ))

    body.extend([b'--', BOUNDARY, b'--\r\n'])
    content_type = b'multipart/form-data; boundary=' + BOUNDARY

    return b''.join(body), content_type
Example #24
0
 def get_error_node(exception):
     return etree.Element('error', **{k: str(v) for k, v in iteritems(exception.attrs)})
Example #25
0
 def _encode_dict(d):
     return {k: _encode_value(v) for k, v in iteritems(d)}
Example #26
0
def extend_request_arguments(request, match):
    arguments = match.groupdict()
    for name, value in iteritems(arguments):
        if value:
            request.arguments.setdefault(name, []).append(value)
Example #27
0
def make_mfd(fields, files):
    """
    Constructs request body in multipart/form-data format

    fields :: { field_name : field_value }
    files :: { field_name: [{ "filename" : fn, "body" : bytes }]}
    """
    def addslashes(text):
        for s in (b'\\', b'"'):
            if s in text:
                text = text.replace(s, b'\\' + s)
        return text

    def create_field(name, data):
        name = addslashes(any_to_bytes(name))

        return [
            b'--', BOUNDARY,
            b'\r\nContent-Disposition: form-data; name="', name,
            b'"\r\n\r\n', any_to_bytes(data), b'\r\n'
        ]

    def create_file_field(name, filename, data, content_type):
        if content_type == 'application/unknown':
            content_type = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
        else:
            content_type = content_type.replace('\n', ' ').replace('\r', ' ')

        name = addslashes(any_to_bytes(name))
        filename = addslashes(any_to_bytes(filename))

        return [
            b'--', BOUNDARY,
            b'\r\nContent-Disposition: form-data; name="', name, b'"; filename="', filename,
            b'"\r\nContent-Type: ', any_to_bytes(content_type),
            b'\r\n\r\n', any_to_bytes(data), b'\r\n'
        ]

    body = []

    for name, data in iteritems(fields):
        if data is None:
            continue

        if isinstance(data, list):
            for value in data:
                if value is not None:
                    body.extend(create_field(name, value))
        else:
            body.extend(create_field(name, data))

    for name, files in iteritems(files):
        for file in files:
            body.extend(create_file_field(
                name, file['filename'], file['body'], file.get('content_type', 'application/unknown')
            ))

    body.extend([b'--', BOUNDARY, b'--\r\n'])
    content_type = b'multipart/form-data; boundary=' + BOUNDARY

    return b''.join(body), content_type
Example #28
0
 def get_error_node(exception):
     return etree.Element(
         'error', **{k: str(v)
                     for k, v in iteritems(exception.attrs)})
Example #29
0
    def configure_app(self, **kwargs):
        """Updates or adds options to application config."""
        for name, val in iteritems(kwargs):
            setattr(self._app.config, name, val)

        return self
Example #30
0
 def get_error_node(exception):
     return {'error': {k: v for k, v in iteritems(exception.attrs)}}
Example #31
0
 def _encode_dict(d):
     return {k: _encode_value(v) for k, v in iteritems(d)}
Example #32
0
 def configure(self, **kwargs):
     for name, val in iteritems(kwargs):
         setattr(self._config, name, val)
     return self