1+ package org .example .filter ;
2+
3+ import org .example .http .HttpResponseBuilder ;
4+ import org .example .httpparser .HttpRequest ;
5+ import org .junit .jupiter .api .Test ;
6+
7+ import java .io .ByteArrayInputStream ;
8+ import java .io .ByteArrayOutputStream ;
9+ import java .nio .charset .StandardCharsets ;
10+ import java .util .HashMap ;
11+ import java .util .Map ;
12+ import java .util .zip .GZIPInputStream ;
13+
14+ import static org .junit .jupiter .api .Assertions .*;
15+
16+ class CompressionFilterTest {
17+
18+ @ Test
19+ void testGzipCompressionWhenClientSupportsIt () throws Exception {
20+ Map <String , String > headers = new HashMap <>();
21+ headers .put ("Accept-Encoding" , "gzip, deflate" );
22+
23+ HttpRequest request = new HttpRequest (
24+ "GET" ,
25+ "/" ,
26+ "HTTP/1.1" ,
27+ headers ,
28+ null
29+ );
30+
31+ String largeBody = "<html><body>" + "Hello World! " .repeat (200 ) + "</body></html>" ;
32+ HttpResponseBuilder response = new HttpResponseBuilder ();
33+ response .setBody (largeBody );
34+ response .setHeaders (Map .of ("Content-Type" , "text/html" ));
35+
36+ FilterChain mockChain = (req , res ) -> {
37+ };
38+
39+ CompressionFilter filter = new CompressionFilter ();
40+ filter .init ();
41+ filter .doFilter (request , response , mockChain );
42+
43+ byte [] compressedBody = getBodyFromResponse (response );
44+ assertNotNull (compressedBody , "Body should not be null" );
45+ assertTrue (compressedBody .length < largeBody .getBytes (StandardCharsets .UTF_8 ).length ,
46+ "Compressed body should be smaller than original" );
47+
48+
49+ String decompressed = decompressGzip (compressedBody );
50+ assertEquals (largeBody , decompressed , "Decompressed data should match original" );
51+ }
52+
53+ @ Test
54+ void testNoCompressionWhenClientDoesNotSupport () {
55+ HttpRequest request = new HttpRequest (
56+ "GET" ,
57+ "/" ,
58+ "HTTP/1.1" ,
59+ Map .of (),
60+ null
61+ );
62+
63+ String body = "<html><body>" + "Hello World! " .repeat (200 ) + "</body></html>" ;
64+ HttpResponseBuilder response = new HttpResponseBuilder ();
65+ response .setBody (body );
66+
67+ FilterChain mockChain = (req , res ) -> {};
68+
69+ CompressionFilter filter = new CompressionFilter ();
70+ filter .doFilter (request , response , mockChain );
71+
72+ byte [] resultBody = getBodyFromResponse (response );
73+ assertArrayEquals (body .getBytes (StandardCharsets .UTF_8 ), resultBody ,
74+ "Body should not be compressed when client doesn't support gzip" );
75+ }
76+
77+ @ Test
78+ void testNoCompressionForSmallResponses () {
79+ Map <String , String > headers = new HashMap <>();
80+ headers .put ("Accept-Encoding" , "gzip" );
81+
82+ HttpRequest request = new HttpRequest ("GET" , "/" , "HTTP/1.1" , headers , null );
83+
84+ String smallBody = "Hello" ;
85+ HttpResponseBuilder response = new HttpResponseBuilder ();
86+ response .setBody (smallBody );
87+
88+ FilterChain mockChain = (req , res ) -> {};
89+
90+ CompressionFilter filter = new CompressionFilter ();
91+ filter .doFilter (request , response , mockChain );
92+
93+ byte [] resultBody = getBodyFromResponse (response );
94+ assertArrayEquals (smallBody .getBytes (StandardCharsets .UTF_8 ), resultBody ,
95+ "Small bodies should not be compressed" );
96+ }
97+
98+ private String decompressGzip (byte [] compressed ) throws Exception {
99+ ByteArrayInputStream bais = new ByteArrayInputStream (compressed );
100+ ByteArrayOutputStream baos = new ByteArrayOutputStream ();
101+
102+ try (GZIPInputStream gzis = new GZIPInputStream (bais )) {
103+ byte [] buffer = new byte [1024 ];
104+ int len ;
105+ while ((len = gzis .read (buffer )) > 0 ) {
106+ baos .write (buffer , 0 , len );
107+ }
108+ }
109+
110+ return baos .toString (StandardCharsets .UTF_8 );
111+ }
112+
113+ private byte [] getBodyFromResponse (HttpResponseBuilder response ) {
114+ try {
115+ var field = response .getClass ().getDeclaredField ("bytebody" );
116+ field .setAccessible (true );
117+ byte [] bytebody = (byte []) field .get (response );
118+
119+ if (bytebody != null ) {
120+ return bytebody ;
121+ }
122+
123+ var bodyField = response .getClass ().getDeclaredField ("body" );
124+ bodyField .setAccessible (true );
125+ String body = (String ) bodyField .get (response );
126+ return body .getBytes (StandardCharsets .UTF_8 );
127+
128+ } catch (Exception e ) {
129+ throw new RuntimeException ("Failed to get body" , e );
130+ }
131+ }
132+ @ Test
133+ void testSkipCompressionForImages () {
134+ Map <String , String > headers = new HashMap <>();
135+ headers .put ("Accept-Encoding" , "gzip" );
136+
137+ HttpRequest request = new HttpRequest ("GET" , "/image.jpg" , "HTTP/1.1" , headers , null );
138+
139+ String largeImageData = "fake image data " .repeat (200 );
140+ HttpResponseBuilder response = new HttpResponseBuilder ();
141+ response .setBody (largeImageData );
142+ response .setHeaders (Map .of ("Content-Type" , "image/jpeg" ));
143+
144+ FilterChain mockChain = (req , res ) -> {};
145+
146+ CompressionFilter filter = new CompressionFilter ();
147+ filter .doFilter (request , response , mockChain );
148+
149+ byte [] resultBody = getBodyFromResponse (response );
150+ assertArrayEquals (largeImageData .getBytes (StandardCharsets .UTF_8 ), resultBody ,
151+ "Images should not be compressed" );
152+ }
153+
154+ @ Test
155+ void testCompressJsonResponse () {
156+ Map <String , String > headers = new HashMap <>();
157+ headers .put ("Accept-Encoding" , "gzip" );
158+
159+ HttpRequest request = new HttpRequest ("GET" , "/api/data" , "HTTP/1.1" , headers , null );
160+
161+ String jsonData = "{\" data\" : " + "\" value\" ," .repeat (200 ) + "}" ;
162+ HttpResponseBuilder response = new HttpResponseBuilder ();
163+ response .setBody (jsonData );
164+ response .setHeaders (Map .of ("Content-Type" , "application/json" ));
165+
166+ FilterChain mockChain = (req , res ) -> {};
167+
168+ CompressionFilter filter = new CompressionFilter ();
169+ filter .doFilter (request , response , mockChain );
170+
171+ byte [] resultBody = getBodyFromResponse (response );
172+ assertTrue (resultBody .length < jsonData .getBytes (StandardCharsets .UTF_8 ).length ,
173+ "JSON should be compressed" );
174+ }
175+
176+ @ Test
177+ void testHandleContentTypeWithCharset () {
178+ Map <String , String > headers = new HashMap <>();
179+ headers .put ("Accept-Encoding" , "gzip" );
180+
181+ HttpRequest request = new HttpRequest ("GET" , "/" , "HTTP/1.1" , headers , null );
182+
183+ String body = "<html>" + "content " .repeat (200 ) + "</html>" ;
184+ HttpResponseBuilder response = new HttpResponseBuilder ();
185+ response .setBody (body );
186+ response .setHeaders (Map .of ("Content-Type" , "text/html; charset=UTF-8" ));
187+
188+ FilterChain mockChain = (req , res ) -> {};
189+
190+ CompressionFilter filter = new CompressionFilter ();
191+ filter .doFilter (request , response , mockChain );
192+
193+ byte [] resultBody = getBodyFromResponse (response );
194+ assertTrue (resultBody .length < body .getBytes (StandardCharsets .UTF_8 ).length ,
195+ "Should compress even when Content-Type has charset" );
196+ }
197+ }
0 commit comments