View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.rest;
18  
19  import static org.apache.juneau.commons.utils.CollectionUtils.*;
20  import static org.apache.juneau.commons.utils.StringUtils.*;
21  import static org.junit.jupiter.api.Assertions.*;
22  
23  import org.apache.juneau.*;
24  import org.apache.juneau.encoders.*;
25  import org.apache.juneau.http.header.*;
26  import org.apache.juneau.rest.annotation.*;
27  import org.apache.juneau.rest.mock.*;
28  import org.junit.jupiter.api.*;
29  
30  class Header_AcceptEncoding_Test extends TestBase {
31  
32  	//------------------------------------------------------------------------------------------------------------------
33  	// Test with no compression enabled.
34  	//------------------------------------------------------------------------------------------------------------------
35  
36  	@Rest
37  	public static class A {
38  		@RestOp
39  		public String get() {
40  			return "foo";
41  		}
42  	}
43  
44  	@Test void a01_noCompression() throws Exception {
45  		var a = MockRestClient.buildLax(A.class);
46  		a.get("/").run().assertContent("foo");
47  		a.get("/").header(AcceptEncoding.of("")).run().assertContent("foo");
48  		a.get("/").header(AcceptEncoding.of("*")).run().assertContent("foo");
49  		a.get("/").header(AcceptEncoding.of("identity")).run().assertContent("foo");
50  
51  		// The following should all match identity.
52  		a.get("/").header(AcceptEncoding.of("mycoding")).run().assertContent("foo");
53  		a.get("/").header(AcceptEncoding.of("identity;q=0.8,mycoding;q=0.6")).run().assertContent("foo");
54  		a.get("/").header(AcceptEncoding.of("mycoding;q=0.8,identity;q=0.6")).run().assertContent("foo");
55  		a.get("/").header(AcceptEncoding.of("mycoding;q=0.8,*;q=0.6")).run().assertContent("foo");
56  		a.get("/").header(AcceptEncoding.of("*;q=0.8,myencoding;q=0.6")).run().assertContent("foo");
57  
58  		a.get("?noTrace=true").header(AcceptEncoding.of("mycoding,identity;q=0")).run()
59  			.assertStatus(406)
60  			.assertContent().isContains(
61  				"Unsupported encoding in request header 'Accept-Encoding': 'mycoding,identity;q=0'",
62  				"Supported codings: ['identity']"
63  			);
64  		a.get("?noTrace=true").header(AcceptEncoding.of("mycoding,*;q=0")).run()
65  			.assertStatus(406)
66  			.assertContent().isContains(
67  				"Unsupported encoding in request header 'Accept-Encoding': 'mycoding,*;q=0'",
68  				"Supported codings: ['identity']"
69  			);
70  		a.get("?noTrace=true").header(AcceptEncoding.of("identity;q=0")).run()
71  			.assertStatus(406)
72  			.assertContent().isContains(
73  				"Unsupported encoding in request header 'Accept-Encoding': 'identity;q=0'",
74  				"Supported codings: ['identity']"
75  			);
76  		a.get("?noTrace=true").header(AcceptEncoding.of("identity;q=0.0")).run()
77  			.assertStatus(406)
78  			.assertContent().isContains(
79  				"Unsupported encoding in request header 'Accept-Encoding': 'identity;q=0.0'",
80  				"Supported codings: ['identity']"
81  			);
82  		a.get("?noTrace=true").header(AcceptEncoding.of("*;q=0")).run()
83  			.assertStatus(406)
84  			.assertContent().isContains(
85  				"Unsupported encoding in request header 'Accept-Encoding': '*;q=0'",
86  				"Supported codings: ['identity']"
87  			);
88  		a.get("?noTrace=true").header(AcceptEncoding.of("*;q=0.0")).run()
89  			.assertStatus(406)
90  			.assertContent().isContains(
91  				"Unsupported encoding in request header 'Accept-Encoding': '*;q=0.0'",
92  				"Supported codings: ['identity']"
93  			);
94  	}
95  
96  	//------------------------------------------------------------------------------------------------------------------
97  	// Test with compression enabled.
98  	//------------------------------------------------------------------------------------------------------------------
99  
100 	@Rest(encoders=MyEncoder.class)
101 	public static class B {
102 		@RestOp
103 		public String get() {
104 			return "foo";
105 		}
106 	}
107 
108 	@Test void b01_withCompression() throws Exception {
109 		var b = MockRestClient.buildLax(B.class);
110 		b.get("/").run().assertContent("foo");
111 		b.get("/").header(AcceptEncoding.of("")).run().assertContent("foo");
112 		b.get("/").header(AcceptEncoding.of("identity")).run().assertContent("foo");
113 		b.get("/").header(AcceptEncoding.of("identity;q=0.8,mycoding;q=0.6")).run().assertContent("foo");
114 
115 		assertEquals("foo", decompress(b.get("/").header(AcceptEncoding.of("*")).run().getContent().asBytes()));
116 		assertEquals("foo", decompress(b.get("/").header(AcceptEncoding.of("mycoding")).run().getContent().asBytes()));
117 
118 		assertEquals("foo", decompress(b.get("/").header(AcceptEncoding.of("mycoding,identity;q=0")).run().getContent().asBytes()));
119 		assertEquals("foo", decompress(b.get("/").header(AcceptEncoding.of("mycoding,*;q=0")).run().getContent().asBytes()));
120 		assertEquals("foo", decompress(b.get("/").header(AcceptEncoding.of("mycoding;q=0.8,identity;q=0.6")).run().getContent().asBytes()));
121 		assertEquals("foo", decompress(b.get("/").header(AcceptEncoding.of("mycoding;q=0.8,*;q=0.6")).run().getContent().asBytes()));
122 
123 		b.get("?noTrace=true").header(AcceptEncoding.of("identity;q=0")).run()
124 			.assertStatus(406)
125 			.assertContent().isContains(
126 				"Unsupported encoding in request header 'Accept-Encoding': 'identity;q=0'",
127 				"Supported codings: ['mycoding','identity']"
128 			);
129 		b.get("?noTrace=true").header(AcceptEncoding.of("identity;q=0.0")).run()
130 			.assertStatus(406)
131 			.assertContent().isContains(
132 				"Unsupported encoding in request header 'Accept-Encoding': 'identity;q=0.0'",
133 				"Supported codings: ['mycoding','identity']"
134 			);
135 		b.get("?noTrace=true").header(AcceptEncoding.of("*;q=0")).run()
136 			.assertStatus(406)
137 			.assertContent().isContains(
138 				"Unsupported encoding in request header 'Accept-Encoding': '*;q=0'",
139 				"Supported codings: ['mycoding','identity']"
140 			);
141 		b.get("?noTrace=true").header(AcceptEncoding.of("*;q=0.0")).run()
142 			.assertStatus(406)
143 			.assertContent().isContains(
144 				"Unsupported encoding in request header 'Accept-Encoding': '*;q=0.0'",
145 				"Supported codings: ['mycoding','identity']"
146 			);
147 	}
148 
149 	//------------------------------------------------------------------------------------------------------------------
150 	// Test with compression enabled but with servlet using output stream directly.
151 	//------------------------------------------------------------------------------------------------------------------
152 
153 	@Rest(encoders=MyEncoder.class)
154 	public static class C {
155 		@RestGet
156 		public void a(RestResponse res) throws Exception {
157 			// This method bypasses the content type and encoding from
158 			// the serializers and encoders when calling getOutputStream() directly.
159 			res.setContentType("text/direct");
160 			var os = res.getOutputStream();
161 			os.write("foo".getBytes());
162 			os.flush();
163 		}
164 		@RestGet
165 		public void b(RestResponse res) throws Exception {
166 			// This method bypasses the content type and encoding from
167 			// the serializers and encoders when calling getWriter() directly.
168 			var w = res.getWriter();
169 			w.append("foo");
170 			w.flush();
171 		}
172 		@RestGet
173 		public void c(RestResponse res) throws Exception {
174 			// This method uses getNegotiatedWriter() which should use GZip encoding.
175 			var w = res.getNegotiatedWriter();
176 			w.append("foo");
177 			w.flush();
178 			w.close();
179 		}
180 		@RestGet(encoders={IdentityEncoder.class})
181 		public void d(RestResponse res) throws Exception {
182 			// This method overrides the set of encoders at the method level and so shouldn't use GZip encoding.
183 			var w = res.getNegotiatedWriter();
184 			w.append("foo");
185 			w.flush();
186 		}
187 	}
188 
189 	@Test void c01_direct() throws Exception {
190 		var c = MockRestClient.build(C.class);
191 
192 		c.get("/a")
193 			.header(AcceptEncoding.of("mycoding"))
194 			.run()
195 			.assertHeader("Content-Encoding").isNull() // Should not be set
196 			.assertHeader("Content-Type").is("text/direct")
197 			.assertContent("foo");
198 		c.get("/a")
199 			.header(AcceptEncoding.of("*"))
200 			.run()
201 			.assertHeader("Content-Encoding").isNull() // Should not be set
202 			.assertHeader("Content-Type").is("text/direct")
203 			.assertContent("foo");
204 
205 		c.get("/b")
206 			.header(AcceptEncoding.of("mycoding"))
207 			.run()
208 			.assertHeader("Content-Encoding").isNull() // Should not be set
209 			.assertContent("foo");
210 		c.get("/b")
211 			.header(AcceptEncoding.of("*"))
212 			.run()
213 			.assertHeader("Content-Encoding").isNull() // Should not be set
214 			.assertContent("foo");
215 
216 		byte[] body;
217 		body = c.get("/c")
218 			.header(AcceptEncoding.of("mycoding"))
219 			.run()
220 			.assertHeader("Content-Encoding").is("mycoding")
221 			.getContent().asBytes();
222 		assertEquals("foo", decompress(body));
223 		body = c.get("/c")
224 			.header(AcceptEncoding.of("*"))
225 			.run()
226 			.assertHeader("Content-Encoding").is("mycoding")
227 			.getContent().asBytes();
228 		assertEquals("foo", decompress(body));
229 
230 		c.get("/d")
231 			.header(AcceptEncoding.of("mycoding"))
232 			.run()
233 			.assertHeader("Content-Encoding").isNull() // Should not be set
234 			.assertContent("foo");
235 		c.get("/d")
236 			.header(AcceptEncoding.of("*"))
237 			.run()
238 			.assertHeader("Content-Encoding").isNull() // Should not be set
239 			.assertContent("foo");
240 	}
241 
242 	//------------------------------------------------------------------------------------------------------------------
243 	// Helpers
244 	//------------------------------------------------------------------------------------------------------------------
245 
246 	public static class MyEncoder extends GzipEncoder {
247 		@Override /* ConfigEncoder */
248 		public String[] getCodings() {
249 			return a("mycoding");
250 		}
251 	}
252 }