Ejemplo n.º 1
0
class ResponseModelFactory(object):
    """given a response object, craft the silk response model"""

    def __init__(self, response):
        super(ResponseModelFactory, self).__init__()
        self.response = response
        self.request = DataCollector().request

    def body(self):
        body = ''
        content_type, char_set = _parse_content_type(self.response.get('Content-Type', ''))
        content = getattr(self.response, 'content', '')
        if char_set and content:
            try:
                content = content.decode(char_set)
            except AttributeError:
                pass
            except LookupError:  # If no encoding exists, default to UTF-8
                try:
                    content = content.decode('UTF-8')
                except AttributeError:
                    pass
                except UnicodeDecodeError:
                    content = ''
            except Exception as e:
                Logger.error('Unable to decode response body using char_set %s due to error: %s. Will ignore. Stacktrace:' % (char_set, e))
                traceback.print_exc()
        else:
            # Default to an attempt at UTF-8 decoding.
            try:
                content = content.decode('UTF-8')
            except AttributeError:
                pass
            except UnicodeDecodeError:
                content = ''
        if content:
            max_body_size = SilkyConfig().SILKY_MAX_RESPONSE_BODY_SIZE
            if max_body_size > -1:
                Logger.debug('Max size of response body defined so checking')
                size = sys.getsizeof(content, None)
                if not size:
                    Logger.error('Could not get size of response body. Ignoring')
                    content = ''
                else:
                    if size > max_body_size:
                        content = ''
                        Logger.debug('Size of %d for %s is bigger than %d so ignoring response body' % (size, self.request.path, max_body_size))
                    else:
                        Logger.debug('Size of %d for %s is less than %d so saving response body' % (size, self.request.path, max_body_size))
            if content_type in content_types_json:
                # TODO: Perhaps theres a way to format the JSON without parsing it?
                try:
                    body = json.dumps(json.loads(content), sort_keys=True, indent=4)
                except (TypeError, ValueError):
                    Logger.warn('Response to request with pk %s has content type %s but was unable to parse it' % (self.request.pk, content_type))
        return body, content

    def construct_response_model(self):
        assert self.request, 'Cant construct a response model if there is no request model'
        Logger.debug('Creating response model for request model with pk %s' % self.request.pk)
        b, content = self.body()
        raw_headers = self.response._headers
        headers = {}
        for k, v in raw_headers.items():
            try:
                header, val = v
            except ValueError:
                header, val = k, v
            finally:
                headers[header] = val

        silky_response = models.Response.objects.create(request=self.request,
                                                        status_code=self.response.status_code,
                                                        encoded_headers=json.dumps(headers),
                                                        body=b)
        # Text fields are encoded as UTF-8 in Django and hence will try to coerce
        # anything to we pass to UTF-8. Some stuff like binary will fail.
        try:
            silky_response.raw_body = content
        except UnicodeDecodeError:
            Logger.debug('NYI: Saving of binary response body')  # TODO
        self.request.response = silky_response
        self.request.save()

        return silky_response