feat: API response compression & payload optimization (#129)#393
Open
sinatragianpaolo-oc wants to merge 2 commits intorohitdash08:mainfrom
Open
feat: API response compression & payload optimization (#129)#393sinatragianpaolo-oc wants to merge 2 commits intorohitdash08:mainfrom
sinatragianpaolo-oc wants to merge 2 commits intorohitdash08:mainfrom
Conversation
- app/compression.py: gzip middleware via after_request hook - Zero new dependencies (uses Python built-in gzip module) - Compresses JSON/text responses when client sends Accept-Encoding: gzip - Skips: small payloads (<512B), already-encoded, non-compressible types - Vary: Accept-Encoding + X-Compression-Ratio headers - 8 tests covering all compression scenarios Closes rohitdash08#129
…t compression test
Fix 1 — Skip direct_passthrough (streaming) responses
_maybe_compress now checks response.direct_passthrough before calling
get_data(). Buffering a streaming response in memory defeats the purpose
of streaming and can exhaust RAM on large payloads. Streaming responses
should be compressed at the proxy layer (e.g. nginx gzip_proxied).
Comment in code explains the rationale.
Fix 2 — X-Compression-Ratio gated on app.debug
The header reveals payload size information that could assist
compression-based side-channel attacks (e.g. BREACH) in production.
Now only emitted when current_app.debug is True.
Fix 3 — Replace slow DB-seeding test with mock-based test
test_large_json_is_compressed previously created 30 expenses via POST
(slow, fragile, DB-dependent). Replaced with a route registered on the
test app that returns a large in-memory JSON body directly — no DB
interaction, runs in milliseconds.
New / updated tests:
- test_large_json_is_compressed: mock route, no DB, fast
- test_streaming_response_skipped: asserts get_data() never called on
direct_passthrough responses (uses MagicMock)
- test_x_compression_ratio_absent_in_production: debug=False → no header
- test_x_compression_ratio_present_in_debug: debug=True → header present
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds gzip compression for JSON/text responses via Flask
after_requesthook to fix #129.Uses Python's built-in
gzip— no extra dependencies. Skips small payloads (< 512 bytes), non-compressible content types, and clients that don't sendAccept-Encoding: gzip.Review fixes (latest commit)
Streaming responses skipped in
_maybe_compress— added adirect_passthroughcheck beforeget_data(). Callingget_data()on adirect_passthrough/stream_with_contextresponse buffers the entire stream in memory, defeating the purpose of streaming. Streaming responses should be compressed at the proxy layer (e.g.nginx gzip_proxied). A comment in the code explains the rationale.X-Compression-Ratiogated onapp.debug— the header reveals payload size information that could assist compression-based side-channel attacks (e.g. BREACH) in production. Now only emitted whencurrent_app.debug is True.test_large_json_is_compressedrewritten — the original test created 30 expense rows via POST (slow, DB-dependent, fragile). Replaced with a lightweight route registered on the test app that returns a large in-memory JSON body directly — no DB interaction, runs in milliseconds.New tests:
test_streaming_response_skipped— usesMagicMockwithdirect_passthrough=True, assertsget_data()is never calledtest_x_compression_ratio_absent_in_production— togglesapp.debug = False, asserts header absenttest_x_compression_ratio_present_in_debug— togglesapp.debug = True, asserts header present with valid ratioCloses #129