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.http.header;
18  
19  import static org.apache.juneau.TestUtils.*;
20  import static org.apache.juneau.commons.utils.CollectionUtils.*;
21  import static org.apache.juneau.http.HttpHeaders.*;
22  import static org.apache.juneau.junit.bct.BctAssertions.*;
23  import static org.junit.jupiter.api.Assertions.*;
24  
25  import java.util.*;
26  import java.util.concurrent.atomic.*;
27  
28  import org.apache.http.*;
29  import org.apache.juneau.*;
30  import org.apache.juneau.httppart.*;
31  import org.apache.juneau.oapi.*;
32  import org.junit.jupiter.api.*;
33  
34  /**
35   * Tests: {@link HeaderList}, {@link HeaderListBuilder}, {@link BasicHeaderIterator}
36   */
37  class HeaderList_Test extends TestBase {
38  
39  	private static final Header
40  		FOO_1 = header("Foo","1"),
41  		FOO_2 = header("Foo","2"),
42  		FOO_3 = header("Foo","3"),
43  		FOO_4 = header("Foo","4"),
44  		FOO_5 = header("Foo","5"),
45  		FOO_6 = header("Foo","6"),
46  		FOO_7 = header("Foo","7"),
47  		BAR_1 = header("Bar","1"),
48  		BAR_2 = header("Bar","2"),
49  
50  		X_x = header("X", "x");
51  
52  	@Test void a01_basic() {
53  		var x = HeaderList.create();
54  		assertEmpty(x);
55  		assertList(x.append(FOO_1), "Foo: 1");
56  		assertList(x.append(FOO_2), "Foo: 1", "Foo: 2");
57  		assertList(x.append(HeaderList.of().getAll()), "Foo: 1", "Foo: 2");
58  		assertList(x.append(HeaderList.of(FOO_3).getAll()), "Foo: 1", "Foo: 2", "Foo: 3");
59  		assertList(x.append(HeaderList.of(FOO_4, FOO_5).getAll()), "Foo: 1", "Foo: 2", "Foo: 3", "Foo: 4", "Foo: 5");
60  		assertList(x.append(HeaderList.of(FOO_6, FOO_7).getAll()), "Foo: 1", "Foo: 2", "Foo: 3", "Foo: 4", "Foo: 5", "Foo: 6", "Foo: 7");
61  		assertList(x.append((Header)null), "Foo: 1", "Foo: 2", "Foo: 3", "Foo: 4", "Foo: 5", "Foo: 6", "Foo: 7");
62  		assertList(x.append((List<Header>)null), "Foo: 1", "Foo: 2", "Foo: 3", "Foo: 4", "Foo: 5", "Foo: 6", "Foo: 7");
63  		assertEmpty(new HeaderList.Void());
64  	}
65  
66  	@Test void a02_creators() {
67  		assertList(headerList(FOO_1, FOO_2, null), "Foo: 1", "Foo: 2");
68  		assertList(headerList(l(FOO_1, FOO_2, null)), "Foo: 1", "Foo: 2");
69  		assertList(headerList("Foo","1","Foo","2"), "Foo: 1", "Foo: 2");
70  		assertThrowsWithMessage(IllegalArgumentException.class, "Odd number of parameters passed into HeaderList.ofPairs()", ()->headerList("Foo"));
71  		assertEmpty(HeaderList.of((List<Header>)null));
72  		assertEmpty(HeaderList.of(Collections.emptyList()));
73  		assertList(HeaderList.of(l(FOO_1)), "Foo: 1");
74  		assertEmpty(HeaderList.of((Header[])null));
75  		assertEmpty(HeaderList.of());
76  		assertList(HeaderList.of(FOO_1), "Foo: 1");
77  		assertEmpty(HeaderList.ofPairs((String[])null));
78  		assertEmpty(HeaderList.ofPairs());
79  	}
80  
81  	@Test void a03_addMethods() {
82  		var pname = "HeaderSupplierTest.x";
83  
84  		var x = HeaderList.create().resolving();
85  		System.setProperty(pname, "y");
86  
87  		x.append("X1","bar");
88  		x.append("X2","$S{"+pname+"}");
89  		x.append("X3","bar");
90  		x.append("X4",()->"$S{"+pname+"}");
91  		x.append(SerializedHeader.of("X5","bar",openApiSession(),null,false));
92  
93  		assertList(x, "X1: bar", "X2: y", "X3: bar", "X4: y", "X5: bar");
94  
95  		System.setProperty(pname, "z");
96  
97  		assertList(x, "X1: bar", "X2: z", "X3: bar", "X4: z", "X5: bar");
98  
99  		System.clearProperty(pname);
100 	}
101 
102 	@Test void a04_toArrayMethods() {
103 		var x = HeaderList
104 			.create()
105 			.append("X1","1")
106 			.append(headerList("X2","2").getAll());
107 		assertList(x, "X1: 1", "X2: 2");
108 	}
109 
110 	@Test void a05_copy() {
111 		var x = HeaderList.of(FOO_1).copy();
112 		assertList(x, "Foo: 1");
113 	}
114 
115 	@Test void a06_getCondensed() {
116 		var x = HeaderList.of(FOO_1);
117 		assertEmpty(x.get((String)null));
118 		assertString("Foo: 1", x.get("Foo"));
119 		assertEmpty(x.get("Bar"));
120 		x = HeaderList.of(FOO_1, FOO_2, FOO_3, X_x);
121 		assertString("Foo: 1, 2, 3", x.get("Foo"));
122 		assertEmpty(x.get("Bar"));
123 	}
124 
125 	@org.apache.juneau.http.annotation.Header("Foo")
126 	static class Foo extends BasicStringHeader {
127 		private static final long serialVersionUID = 1L;
128 
129 		public Foo(String value) {
130 			super("Foo", value);
131 		}
132 	}
133 
134 	@Test void a07_getCondensed_asType() {
135 		var x = HeaderList.of(FOO_1);
136 		assertEmpty(x.get(null, Allow.class));
137 		assertString("Allow: 1", x.get("Foo", Allow.class));
138 		assertEmpty(x.get("Bar", Allow.class));
139 		x = HeaderList.of(FOO_1, FOO_2, FOO_3, X_x);
140 		assertString("Allow: 1, 2, 3", x.get("Foo", Allow.class));
141 		assertEmpty(x.get("Bar", Allow.class));
142 		assertString("Foo: 1, 2, 3", x.get(Foo.class));
143 		final var x2 = x;
144 		assertThrowsWithMessage(IllegalArgumentException.class, "Header name could not be found on bean type 'java.lang.String'", ()->x2.get(String.class));
145 	}
146 
147 	@Test void a08_get() {
148 		var x = HeaderList.of(FOO_1, FOO_2, X_x);
149 		assertEmpty(x.getAll(null));
150 		assertList(x.getAll("Foo"), "Foo: 1", "Foo: 2");
151 		assertList(x.getAll("FOO"), "Foo: 1", "Foo: 2");
152 		assertEmpty(x.getAll("Bar"));
153 	}
154 
155 	@Test void a09_getFirst() {
156 		var x = HeaderList.of(FOO_1, FOO_2, X_x);
157 		assertEmpty(x.getFirst(null));
158 		assertString("Foo: 1", x.getFirst("Foo"));
159 		assertString("Foo: 1", x.getFirst("FOO"));
160 		assertEmpty(x.getFirst("Bar"));
161 	}
162 
163 	@Test void a10_getLast() {
164 		var x = HeaderList.of(FOO_1, FOO_2, X_x);
165 		assertEmpty(x.getLast(null));
166 		assertString("Foo: 2", x.getLast("Foo"));
167 		assertString("Foo: 2", x.getLast("FOO"));
168 		assertEmpty(x.getLast("Bar"));
169 	}
170 
171 	@Test void a11_contains() {
172 		var x = HeaderList.of(FOO_1, FOO_2, X_x);
173 		assertFalse(x.contains(null));
174 		assertTrue(x.contains("Foo"));
175 		assertTrue(x.contains("FOO"));
176 		assertFalse(x.contains("Bar"));
177 	}
178 
179 	@Test void a12_headerIterator_all() {
180 		assertFalse(HeaderList.of().headerIterator().hasNext());
181 		assertTrue(HeaderList.of(FOO_1).headerIterator().hasNext());
182 	}
183 
184 	@Test void a13_headerIterator_single() {
185 		var x = HeaderList.of();
186 		assertFalse(x.headerIterator("Foo").hasNext());
187 		x = HeaderList.of(FOO_1);
188 		assertTrue(x.headerIterator("Foo").hasNext());
189 		assertTrue(x.headerIterator("FOO").hasNext());
190 	}
191 
192 	@Test void a14_forEach_all() {
193 		var x = HeaderList.of();
194 
195 		var i1 = new AtomicInteger();
196 		x.forEach(h -> i1.incrementAndGet());
197 		assertEquals(0, i1.get());
198 
199 		x = HeaderList.of(FOO_1, FOO_2);
200 		var i2 = new AtomicInteger();
201 		x.forEach(h -> i2.incrementAndGet());
202 		assertEquals(2, i2.get());
203 	}
204 
205 	@Test void a15_forEach_single() {
206 		var x = HeaderList.of();
207 
208 		var i1 = new AtomicInteger();
209 		x.forEach("FOO", h -> i1.incrementAndGet());
210 		assertEquals(0, i1.get());
211 
212 		x = HeaderList.of(FOO_1, FOO_2, X_x);
213 		var i2 = new AtomicInteger();
214 		x.forEach("FOO", h -> i2.incrementAndGet());
215 		assertEquals(2, i2.get());
216 	}
217 
218 	@Test void a16_stream_all() {
219 		var x = HeaderList.of();
220 
221 		var i1 = new AtomicInteger();
222 		x.stream().forEach(h -> i1.incrementAndGet());
223 		assertEquals(0, i1.get());
224 
225 		x = HeaderList.of(FOO_1, FOO_2);
226 		var i2 = new AtomicInteger();
227 		x.stream().forEach(h -> i2.incrementAndGet());
228 		assertEquals(2, i2.get());
229 	}
230 
231 	@Test void a17_stream_single() {
232 		var x = HeaderList.of();
233 
234 		var i1 = new AtomicInteger();
235 		x.stream("FOO").forEach(h -> i1.incrementAndGet());
236 		assertEquals(0, i1.get());
237 
238 		x = HeaderList.of(FOO_1, FOO_2, X_x);
239 		var i2 = new AtomicInteger();
240 		x.stream("FOO").forEach(h -> i2.incrementAndGet());
241 		assertEquals(2, i2.get());
242 	}
243 
244 	@Test void a18_caseSensitive() {
245 		var x = HeaderList.create().caseSensitive(true).append(FOO_1, FOO_2, X_x);
246 		assertList(x.getAll("Foo"), "Foo: 1", "Foo: 2");
247 		assertEmpty(x.getAll("FOO"));
248 	}
249 
250 	@Test void a19_size() {
251 		assertSize(1, HeaderList.of(FOO_1));
252 	}
253 
254 	//-----------------------------------------------------------------------------------------------------------------
255 	// Builder methods
256 	//-----------------------------------------------------------------------------------------------------------------
257 
258 	@Test void b01_builder_clear() {
259 		var x = HeaderList.create().append(FOO_1);
260 		x.clear();
261 		assertEmpty(x);
262 	}
263 
264 	@Test void b02_builder_append() {
265 		var x1 = HeaderList.create().append(FOO_1);
266 		var x2 = HeaderList
267 			.create()
268 			.append()
269 			.append((HeaderList)null)
270 			.append((Header)null)
271 			.append((Header[])null)
272 			.append(x1)
273 			.append(FOO_2, FOO_3)
274 			.append("Bar", "b1")
275 			.append("Bar", ()->"b2")
276 			.append((List<Header>)null)
277 			.append(l(FOO_4));
278 		assertList(x2, "Foo: 1", "Foo: 2", "Foo: 3", "Bar: b1", "Bar: b2", "Foo: 4");
279 	}
280 
281 	@Test void b03_builder_prepend() {
282 		var x1 = HeaderList.create().append(FOO_1);
283 		var x2 = HeaderList
284 			.create()
285 			.prepend()
286 			.prepend((HeaderList)null)
287 			.prepend((Header)null)
288 			.prepend((Header[])null)
289 			.prepend(x1)
290 			.prepend(FOO_2, FOO_3)
291 			.prepend("Bar", "b1")
292 			.prepend("Bar", ()->"b2")
293 			.prepend((List<Header>)null)
294 			.prepend(l(FOO_4));
295 		assertList(x2, "Foo: 4", "Bar: b2", "Bar: b1", "Foo: 2", "Foo: 3", "Foo: 1");
296 	}
297 
298 	@Test void b04_builder_remove() {
299 		var x = HeaderList
300 			.create()
301 			.append(FOO_1,FOO_2,FOO_3,FOO_4,FOO_5,FOO_6,FOO_7)
302 			.remove((HeaderList)null)
303 			.remove((Header)null)
304 			.remove(HeaderList.of(FOO_1))
305 			.remove(FOO_2)
306 			.remove(FOO_3, FOO_4)
307 			.remove(l(FOO_5));
308 		assertList(x, "Foo: 6", "Foo: 7");
309 
310 		x = HeaderList.create().append(FOO_1,FOO_2).remove((String[])null).remove("Bar","Foo");
311 		assertEmpty(x);
312 	}
313 
314 	@Test void b05_builder_set() {
315 		var x = HeaderList
316 			.create()
317 			.append(FOO_1,FOO_2)
318 			.set(FOO_3)
319 			.set(BAR_1)
320 			.set((Header)null)
321 			.set((HeaderList)null);
322 		assertList(x, "Foo: 3", "Bar: 1");
323 
324 		x = HeaderList
325 			.create()
326 			.append(BAR_1,FOO_1,FOO_2,BAR_2)
327 			.set(FOO_3);
328 		assertList(x, "Bar: 1", "Foo: 3", "Bar: 2");
329 
330 		x = HeaderList
331 			.create()
332 			.append(BAR_1,FOO_1,FOO_2,BAR_2)
333 			.set((Header[])null)
334 			.set(null,FOO_3,FOO_4,FOO_5);
335 		assertList(x, "Bar: 1", "Bar: 2", "Foo: 3", "Foo: 4", "Foo: 5");
336 
337 		x = HeaderList
338 			.create()
339 			.append(BAR_1,FOO_1,FOO_2,BAR_2)
340 			.set((List<Header>)null)
341 			.set(l(null,FOO_3,FOO_4,FOO_5));
342 		assertList(x, "Bar: 1", "Bar: 2", "Foo: 3", "Foo: 4", "Foo: 5");
343 
344 		x = HeaderList
345 			.create()
346 			.append(BAR_1,FOO_1,FOO_2,BAR_2)
347 			.set("FOO", "x");
348 		assertList(x, "Bar: 1", "FOO: x", "Bar: 2");
349 
350 		x = HeaderList
351 			.create()
352 			.append(BAR_1,FOO_1,FOO_2,BAR_2)
353 			.set("FOO", ()->"x");
354 		assertList(x, "Bar: 1", "FOO: x", "Bar: 2");
355 
356 		x = HeaderList
357 			.create()
358 			.caseSensitive(true)
359 			.append(BAR_1,FOO_1,FOO_2,BAR_2)
360 			.set("FOO", ()->"x");
361 		assertList(x, "Bar: 1", "Foo: 1", "Foo: 2", "Bar: 2", "FOO: x");
362 
363 		x = HeaderList
364 			.create()
365 			.append(BAR_1,FOO_1,FOO_2,BAR_2)
366 			.set(HeaderList.of(FOO_3,FOO_4));
367 		assertList(x, "Bar: 1", "Bar: 2", "Foo: 3", "Foo: 4");
368 	}
369 
370 	//-----------------------------------------------------------------------------------------------------------------
371 	// BasicHeaderIterator
372 	//-----------------------------------------------------------------------------------------------------------------
373 
374 	@Test void c01_iterators() {
375 		var x = HeaderList.of(Accept.TEXT_XML,ContentType.TEXT_XML);
376 
377 		var i1 = x.headerIterator();
378 		assertString("Accept: text/xml", i1.nextHeader());
379 		assertString("Content-Type: text/xml", i1.nextHeader());
380 		assertThrowsWithMessage(NoSuchElementException.class, "Iteration already finished.", i1::nextHeader);
381 
382 		var i2 = x.headerIterator();
383 		assertString("Accept: text/xml", i2.next());
384 		assertString("Content-Type: text/xml", i2.nextHeader());
385 		assertThrowsWithMessage(NoSuchElementException.class, "Iteration already finished.", i2::next);
386 
387 		var i3 = x.headerIterator("accept");
388 		assertString("Accept: text/xml", i3.nextHeader());
389 		assertThrowsWithMessage(NoSuchElementException.class, "Iteration already finished.", i3::nextHeader);
390 
391 		var x2 = HeaderList.create().append(Accept.TEXT_XML,ContentType.TEXT_XML).caseSensitive(true);
392 
393 		var i4 = x2.headerIterator("Accept");
394 		assertString("Accept: text/xml", i4.nextHeader());
395 		assertThrowsWithMessage(NoSuchElementException.class, "Iteration already finished.", i4::nextHeader);
396 
397 		var i5 = x2.headerIterator("accept");
398 		assertThrowsWithMessage(NoSuchElementException.class, "Iteration already finished.", i5::nextHeader);
399 
400 		assertThrowsWithMessage(UnsupportedOperationException.class, "Not supported.", i5::remove);
401 	}
402 
403 	//-----------------------------------------------------------------------------------------------------------------
404 	// Default headers
405 	//-----------------------------------------------------------------------------------------------------------------
406 
407 	@Test void d01_defaultHeaders() {
408 		var x1 = HeaderList.create().setDefault(Accept.TEXT_XML);
409 		assertList(x1, "Accept: text/xml");
410 
411 		var x2 = HeaderList.create().set(Accept.TEXT_PLAIN).setDefault(Accept.TEXT_XML);
412 		assertList(x2, "Accept: text/plain");
413 
414 		var x3 = HeaderList.create().set(ContentType.TEXT_XML,Accept.TEXT_PLAIN,ContentType.TEXT_XML).setDefault(Accept.TEXT_XML);
415 		assertList(x3, "Content-Type: text/xml", "Accept: text/plain", "Content-Type: text/xml");
416 
417 		var x4 = HeaderList.create().set(ContentType.TEXT_XML,ContentType.TEXT_XML).setDefault(Accept.TEXT_XML);
418 		assertList(x4, "Content-Type: text/xml", "Content-Type: text/xml", "Accept: text/xml");
419 
420 		var x5 = HeaderList.create().set(ContentType.TEXT_XML,ContentType.TEXT_XML).setDefault(Accept.TEXT_XML).setDefault(ContentType.TEXT_HTML);
421 		assertList(x5, "Content-Type: text/xml", "Content-Type: text/xml", "Accept: text/xml");
422 
423 		var x6 = HeaderList.create().setDefault(Accept.TEXT_XML,Accept.TEXT_PLAIN);
424 		assertList(x6, "Accept: text/xml");
425 
426 		var x7 = HeaderList.create().setDefault(Accept.TEXT_XML).setDefault(Accept.TEXT_PLAIN);
427 		assertList(x7, "Accept: text/xml");
428 
429 		var x8 = HeaderList.create().setDefault(Accept.TEXT_XML,Accept.TEXT_HTML).setDefault(Accept.TEXT_PLAIN);
430 		assertList(x8, "Accept: text/xml");
431 
432 		var x9 = HeaderList
433 			.create()
434 			.setDefault((Header)null)
435 			.setDefault((HeaderList)null)
436 			.setDefault((Header[])null)
437 			.setDefault((List<Header>)null);
438 		assertEmpty(x9);
439 
440 		var x10 = HeaderList.create().setDefault("Accept","text/xml");
441 		assertList(x10, "Accept: text/xml");
442 
443 		var x11 = HeaderList.create().setDefault("Accept",()->"text/xml");
444 		assertList(x11, "Accept: text/xml");
445 
446 		var x12 = HeaderList.create().set(ContentType.TEXT_XML,ContentType.TEXT_PLAIN).setDefault(l(Accept.TEXT_XML,ContentType.TEXT_HTML,null));
447 		assertList(x12, "Content-Type: text/xml", "Content-Type: text/plain", "Accept: text/xml");
448 
449 		var x13 = HeaderList.create().set(ContentType.TEXT_XML,ContentType.TEXT_PLAIN).setDefault(HeaderList.of(Accept.TEXT_XML,ContentType.TEXT_HTML,null));
450 		assertList(x13, "Content-Type: text/xml", "Content-Type: text/plain", "Accept: text/xml");
451 
452 		var x14 = HeaderList.create().set(ContentType.TEXT_XML,ContentType.TEXT_PLAIN)
453 			.setDefault(l(Accept.TEXT_XML,ContentType.TEXT_HTML,null))
454 			.setDefault(l(Accept.TEXT_HTML,ContentType.TEXT_XML,null))
455 			.setDefault(l(Age.of(1)));
456 		assertList(x14, "Content-Type: text/xml", "Content-Type: text/plain", "Accept: text/xml", "Age: 1");
457 	}
458 
459 	//-----------------------------------------------------------------------------------------------------------------
460 	// Utility methods
461 	//-----------------------------------------------------------------------------------------------------------------
462 
463 	private static Header header(String name, Object val) {
464 		return basicHeader(name, val);
465 	}
466 
467 	private static HttpPartSerializerSession openApiSession() {
468 		return OpenApiSerializer.DEFAULT.getPartSession();
469 	}
470 }