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