@@ -62,6 +62,11 @@ def request_to_receive(req: RestateLambdaRequest) -> Receive:
6262 assert req ["isBase64Encoded" ]
6363 body = base64 .b64decode (req ["body" ])
6464
65+ # Decompress zstd-encoded request body
66+ headers = {k .lower (): v for k , v in req .get ("headers" , {}).items ()}
67+ if "zstd" in headers .get ("content-encoding" , "" ):
68+ body = zstd_decompress (body )
69+
6570 events = cast (
6671 list [HTTPRequestEvent ],
6772 [
@@ -80,15 +85,19 @@ async def recv() -> HTTPRequestEvent:
8085 return recv
8186
8287
88+ RESPONSE_COMPRESSION_THRESHOLD = 3 * 1024 * 1024
89+
90+
8391class ResponseCollector :
8492 """
8593 Response collector from ASGI Send to Lambda
8694 """
8795
88- def __init__ (self ):
96+ def __init__ (self , accept_encoding : str = "" ):
8997 self .body = bytearray ()
90- self .headers = {}
98+ self .headers : dict [ str , str ] = {}
9199 self .status_code = 500
100+ self .accept_encoding = accept_encoding
92101
93102 async def __call__ (self , message : Union [HTTPResponseStartEvent , HTTPResponseBodyEvent ]) -> None :
94103 """
@@ -105,11 +114,18 @@ def to_lambda_response(self) -> RestateLambdaResponse:
105114 """
106115 Convert collected values to lambda response
107116 """
117+ body : bytes | bytearray = self .body
118+
119+ # Compress response if it exceeds threshold and client accepts zstd
120+ if len (body ) > RESPONSE_COMPRESSION_THRESHOLD and "zstd" in self .accept_encoding and ZSTD_AVAILABLE :
121+ body = zstd_compress (body )
122+ self .headers ["content-encoding" ] = "zstd"
123+
108124 return {
109125 "statusCode" : self .status_code ,
110126 "headers" : self .headers ,
111127 "isBase64Encoded" : True ,
112- "body" : base64 .b64encode (self . body ).decode (),
128+ "body" : base64 .b64encode (body ).decode (),
113129 }
114130
115131
@@ -134,7 +150,8 @@ def lambda_handler(event: RestateLambdaRequest, _context: Any) -> RestateLambdaR
134150
135151 scope = create_scope (event )
136152 recv = request_to_receive (event )
137- send = ResponseCollector ()
153+ req_headers = {k .lower (): v for k , v in event .get ("headers" , {}).items ()}
154+ send = ResponseCollector (accept_encoding = req_headers .get ("accept-encoding" , "" ))
138155
139156 asgi_instance = asgi_app (scope , recv , send )
140157 asgi_task = loop .create_task (asgi_instance ) # type: ignore[var-annotated, arg-type]
@@ -143,3 +160,47 @@ def lambda_handler(event: RestateLambdaRequest, _context: Any) -> RestateLambdaR
143160 return send .to_lambda_response ()
144161
145162 return lambda_handler
163+
164+
165+ def _check_zstd_available () -> bool :
166+ """Return True if zstd compression is available (Python 3.14+)."""
167+ try :
168+ import compression .zstd # type: ignore[import-not-found]
169+
170+ return compression .zstd is not None
171+ except ImportError :
172+ return False
173+
174+
175+ ZSTD_AVAILABLE = _check_zstd_available ()
176+
177+
178+ def is_lambda_compression_supported ():
179+ """Return 'zstd' if running on Lambda and compression.zstd is available (Python 3.14+), else None."""
180+ if is_running_on_lambda () and ZSTD_AVAILABLE :
181+ return "zstd"
182+ return None
183+
184+
185+ def zstd_compress (data : bytes | bytearray ) -> bytes :
186+ """Compress data using zstd."""
187+ try :
188+ import compression .zstd # type: ignore[import-not-found]
189+ except ImportError as e :
190+ raise RuntimeError (
191+ "zstd compression requested but compression.zstd is not available. "
192+ "Python 3.14+ is required for zstd compression support."
193+ ) from e
194+ return compression .zstd .compress (data )
195+
196+
197+ def zstd_decompress (data : bytes ) -> bytes :
198+ """Decompress zstd-compressed data."""
199+ try :
200+ import compression .zstd # type: ignore[import-not-found]
201+ except ImportError as e :
202+ raise RuntimeError (
203+ "Received zstd-compressed request but compression.zstd is not available. "
204+ "Python 3.14+ is required for zstd compression support."
205+ ) from e
206+ return compression .zstd .decompress (data )
0 commit comments