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