slank/awsgi

How to return GZIP Content-Encoding

Closed this issue · 4 comments

Hi,

I am trying to return a GZIP encoded response but getting the error :

{
    "errorMessage": "'utf-8' codec can't decode byte 0x8b in position 1: invalid start byte",
    "errorType": "UnicodeDecodeError",
    "stackTrace": [
        "  File \"/var/task/lambda_handler.py\", line 75, in handler\n    return awsgi.response(app, event, context)\n",
        "  File \"/var/task/awsgi/__init__.py\", line 22, in response\n    return sr.response(output)\n",
        "  File \"/var/task/awsgi/__init__.py\", line 41, in response\n    'body': self.body.getvalue() + ''.join(map(convert_str, output)),\n",
        "  File \"/var/task/awsgi/__init__.py\", line 9, in convert_str\n    return s.decode('utf-8') if isinstance(s, bytes) else s\n"
    ]
}

Is there any way to skip the response transformed in String ?

slank commented

@fpozzobon Does this StackOverflow question help?

I've never needed to return content through this module in other encodings. I'm happy to help, but I think I'll need more information. Could you make a small complete code example that produces the error? I think it's important to see how you're calling the APIs and what data you're passing in headers and body.

Hi @slank thanks for your quick reply :)
Please find below how we zip the content, I am using the following annotation on top of the route :

def gzipped(f):
    @functools.wraps(f)
    def view_func(*args, **kwargs):

        @after_this_request
        def zipper(response):
            accept_encoding = request.headers.get('Accept-Encoding', '')

            if 'gzip' not in accept_encoding.lower():
                return response

            response.direct_passthrough = False

            if (response.status_code < 200 or
                response.status_code >= 300 or
                'Content-Encoding' in response.headers):
                return response
            gzip_buffer = IO()
            gzip_file = gzip.GzipFile(mode='wb',
                                      fileobj=gzip_buffer)
            gzip_file.write(response.data)
            gzip_file.close()

            response.data = gzip_buffer.getvalue()
            response.headers['Content-Encoding'] = 'gzip'
            response.headers['Vary'] = 'Accept-Encoding'
            response.headers['Content-Length'] = len(response.data)

            return response

        return f(*args, **kwargs)

    return view_func
cwalv commented

This isn't possible with isBase64Encoded hard-coded to False. There's a fork of this project that checks for common binary Content-Type headers and then encodes based on that.

I don't think this will really do what you want though (i.e., send a gzipped response to the client), because lambda's have to be exposed with an ALB or API Gateway. You would have to also base64 encode the gzipped body, in addition to setting the correct Content-Type header and specifying isBase64Encoded = True. In the end, it might end up actually increasing your response time.

slank commented

This is fixed in v0.1.0