Repository: adamchainz/apig-wsgi

Issue:
Errors if Django gzip middleware used
### Python Version

3.11

### Package Version

2.18.0

### Description

```
[ERROR] UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8b in position 1: invalid start byte
Traceback (most recent call last):
  File "/var/task/app/handlers.py", line 102, in app
    return _real_handler(event, context)
  File "/var/task/apig_wsgi/__init__.py", line 99, in handler
    return response.as_apig_response()
  File "/var/task/apig_wsgi/__init__.py", line 329, in as_apig_response
    response["body"] = self.body.getvalue().decode("utf-8")
```

Called like:
```python
        from apig_wsgi import make_lambda_handler
        from django.core.wsgi import get_wsgi_application

        application = get_wsgi_application()
        _real_handler = make_lambda_handler(application)
```

with `"django.middleware.gzip.GZipMiddleware",` in middleware.

Discussion:
@OJFord: Ah do we perhaps just need `binary_support=False`? This _is_ with ALB, so I gather the implicit `None` there is enabling it.@adamchainz: I believe this was fixed in #80 , back in version 2.3.0 (2019-08-19).

~You’re not using version 2.18.0 as you report because the source in your traceback does not match. Please check which version you are actually using.~

Ah, no, sorry the source does match.@adamchainz: I think `binary_support=True` may fix it, but ideally we would send binary automatically. This method should be returning `True`, but isn’t:

https://github.com/adamchainz/apig-wsgi/blob/dc220949c4bf9b0aeb0eb49fdd4767af66fdb776/src/apig_wsgi/__init__.py#L276-L289@OJFord: That could be a problem with my headers rather than that detection though I suppose.

I will try it shortly, thanks for getting back to me so quickly.@OJFord: It's because of the first
```python
if not self.binary_support:
    return False
```

isn't it? I'm not setting it, and so:
https://github.com/adamchainz/apig-wsgi/blob/dc220949c4bf9b0aeb0eb49fdd4767af66fdb776/src/apig_wsgi/__init__.py#L74-L76

(which seems contrary to the readme, hence I got it the wrong way around in my first reply:
https://github.com/adamchainz/apig-wsgi/blob/dc220949c4bf9b0aeb0eb49fdd4767af66fdb776/README.rst?plain=1#L81?)@OJFord: > I think `binary_support=True` may fix it

and I can confirm it does indeed! Thanks again.

As above though I think the intention was for `version == "alb"` to do `binary_support != False` rather than `or False`, i.e. to default it `True` when `None`, so it wouldn't have been necessary for me to set it explicitly.@adamchainz: Okay, I just came back to this. I think I fixed it in #528, releasing shortly. Would you be able to check?@OJFord: Yep seems fine now without the `binary_support=True` arg too, thanks!

Solution patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index d429a57..2c372d6 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -10,6 +10,10 @@ Changelog
 
   Thanks to Zoe Guillen for the report in `PR #496 <https://github.com/adamchainz/apig-wsgi/pull/496>`__.
 
+* Enable binary support by default for ALB events.
+
+  Thanks to Oliver Ford for the report in `Issue #513 <https://github.com/adamchainz/apig-wsgi/issues/513>`__.
+
 * Treat the content-type header "application/problem+json" as non binary by default.
 
   Thanks to Ido Savion in `PR #503 <https://github.com/adamchainz/apig-wsgi/pull/503>`__.
diff --git a/src/apig_wsgi/__init__.py b/src/apig_wsgi/__init__.py
index e786628..4cf6373 100644
--- a/src/apig_wsgi/__init__.py
+++ b/src/apig_wsgi/__init__.py
@@ -75,13 +75,23 @@ def handler(event: dict[str, Any], context: Any) -> dict[str, Any]:
             version = event.get("version", "1.0")
 
         if version in ("1.0", "alb"):
-            # Binary support deafults 'off' on version 1
-            event_binary_support = binary_support or False
             environ = get_environ_v1(
                 event,
                 context,
                 encode_query_params=(version == "1.0"),
             )
+            if version == "1.0":
+                # Binary support defaults to 'off' on version 1
+                if binary_support is None:
+                    event_binary_support = False
+                else:
+                    event_binary_support = binary_support
+            else:
+                # Binary support defaults to 'on' on ALBs
+                if binary_support is None:
+                    event_binary_support = True
+                else:
+                    event_binary_support = binary_support
             response: BaseResponse = V1Response(
                 binary_support=event_binary_support,
                 non_binary_content_type_prefixes=non_binary_prefixes_tuple,


Test patch:
diff --git a/tests/test_apig_wsgi.py b/tests/test_apig_wsgi.py
index a2ec224..1228bb2 100644
--- a/tests/test_apig_wsgi.py
+++ b/tests/test_apig_wsgi.py
@@ -257,6 +257,34 @@ def test_get_binary_support_binary(self, simple_app: App) -> None:
             "body": b64encode(b"\x13\x37").decode("utf-8"),
         }
 
+    def test_get_binary_support_alb_default(self, simple_app: App) -> None:
+        simple_app.handler = make_lambda_handler(simple_app)
+        simple_app.headers = [("Content-Type", "application/octet-stream")]
+        simple_app.response = b"\x13\x37"
+
+        response = simple_app.handler(make_alb_event(), None)
+
+        assert response == {
+            "statusCode": 200,
+            "multiValueHeaders": {"Content-Type": ["application/octet-stream"]},
+            "isBase64Encoded": True,
+            "body": b64encode(b"\x13\x37").decode("utf-8"),
+        }
+
+    def test_get_binary_support_alb_enabled(self, simple_app: App) -> None:
+        simple_app.handler = make_lambda_handler(simple_app, binary_support=True)
+        simple_app.headers = [("Content-Type", "application/octet-stream")]
+        simple_app.response = b"\x13\x37"
+
+        response = simple_app.handler(make_alb_event(), None)
+
+        assert response == {
+            "statusCode": 200,
+            "multiValueHeaders": {"Content-Type": ["application/octet-stream"]},
+            "isBase64Encoded": True,
+            "body": b64encode(b"\x13\x37").decode("utf-8"),
+        }
+
     @parametrize_default_text_content_type
     def test_get_binary_support_binary_default_text_with_gzip_content_encoding(
         self, simple_app: App, text_content_type: str