1 /* 2 * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @summary HttpRequest[.Builder] API and behaviour checks 27 * @run testng RequestBuilderTest 28 */ 29 30 import java.net.URI; 31 import java.net.URISyntaxException; 32 import java.util.List; 33 import java.util.Map; 34 import java.util.Set; 35 36 import java.net.http.HttpRequest; 37 import java.net.http.HttpRequest.BodyPublishers; 38 import static java.time.Duration.ofNanos; 39 import static java.time.Duration.ofMinutes; 40 import static java.time.Duration.ofSeconds; 41 import static java.time.Duration.ZERO; 42 import static java.net.http.HttpClient.Version.HTTP_1_1; 43 import static java.net.http.HttpClient.Version.HTTP_2; 44 import static java.net.http.HttpRequest.newBuilder; 45 import static org.testng.Assert.*; 46 47 import org.testng.annotations.Test; 48 49 public class RequestBuilderTest { 50 51 static final URI uri = URI.create("http://foo.com/"); 52 static final Class<NullPointerException> NPE = NullPointerException.class; 53 static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class; 54 static final Class<IllegalStateException> ISE = IllegalStateException.class; 55 static final Class<NumberFormatException> NFE = NumberFormatException.class; 56 static final Class<UnsupportedOperationException> UOE = UnsupportedOperationException.class; 57 58 @Test 59 public void testDefaults() { 60 List<HttpRequest.Builder> builders = List.of(newBuilder().uri(uri), 61 newBuilder(uri), 62 newBuilder().copy().uri(uri), 63 newBuilder(uri).copy()); 64 for (HttpRequest.Builder builder : builders) { 65 assertFalse(builder.build().expectContinue()); 66 assertEquals(builder.build().method(), "GET"); 67 assertFalse(builder.build().bodyPublisher().isPresent()); 68 assertFalse(builder.build().version().isPresent()); 69 assertFalse(builder.build().timeout().isPresent()); 70 assertTrue(builder.build().headers() != null); 71 assertEquals(builder.build().headers().map().size(), 0); 72 } 73 } 74 75 @Test 76 public void testNull() { 77 HttpRequest.Builder builder = newBuilder(); 78 79 assertThrows(NPE, () -> newBuilder(null).build()); 80 assertThrows(NPE, () -> newBuilder(uri).uri(null).build()); 81 assertThrows(NPE, () -> builder.uri(null)); 82 assertThrows(NPE, () -> builder.version(null)); 83 assertThrows(NPE, () -> builder.header(null, null)); 84 assertThrows(NPE, () -> builder.header("name", null)); 85 assertThrows(NPE, () -> builder.header(null, "value")); 86 assertThrows(NPE, () -> builder.headers(null)); 87 assertThrows(NPE, () -> builder.headers(new String[] { null, null })); 88 assertThrows(NPE, () -> builder.headers(new String[] { "name", null })); 89 assertThrows(NPE, () -> builder.headers(new String[] { null, "value" })); 90 assertThrows(NPE, () -> builder.method(null, null)); 91 assertThrows(NPE, () -> builder.method("GET", null)); 92 assertThrows(NPE, () -> builder.method("POST", null)); 93 assertThrows(NPE, () -> builder.method("PUT", null)); 94 assertThrows(NPE, () -> builder.method("DELETE", null)); 95 assertThrows(NPE, () -> builder.setHeader(null, null)); 96 assertThrows(NPE, () -> builder.setHeader("name", null)); 97 assertThrows(NPE, () -> builder.setHeader(null, "value")); 98 assertThrows(NPE, () -> builder.timeout(null)); 99 assertThrows(NPE, () -> builder.POST(null)); 100 assertThrows(NPE, () -> builder.PUT(null)); 101 } 102 103 @Test 104 public void testURI() { 105 assertThrows(ISE, () -> newBuilder().build()); 106 List<URI> uris = List.of( 107 URI.create("ws://foo.com"), 108 URI.create("wss://foo.com"), 109 URI.create("ftp://foo.com"), 110 URI.create("gopher://foo.com"), 111 URI.create("mailto:a@b.com"), 112 URI.create("scheme:example.com"), 113 URI.create("scheme:example.com"), 114 URI.create("scheme:example.com/path"), 115 URI.create("path"), 116 URI.create("/path") 117 ); 118 for (URI u : uris) { 119 assertThrows(IAE, () -> newBuilder(u)); 120 assertThrows(IAE, () -> newBuilder().uri(u)); 121 } 122 123 assertEquals(newBuilder(uri).build().uri(), uri); 124 assertEquals(newBuilder().uri(uri).build().uri(), uri); 125 URI https = URI.create("https://foo.com"); 126 assertEquals(newBuilder(https).build().uri(), https); 127 assertEquals(newBuilder().uri(https).build().uri(), https); 128 } 129 130 @Test 131 public void testMethod() { 132 HttpRequest request = newBuilder(uri).build(); 133 assertEquals(request.method(), "GET"); 134 assertTrue(!request.bodyPublisher().isPresent()); 135 136 request = newBuilder(uri).GET().build(); 137 assertEquals(request.method(), "GET"); 138 assertTrue(!request.bodyPublisher().isPresent()); 139 140 request = newBuilder(uri).POST(BodyPublishers.ofString("")).GET().build(); 141 assertEquals(request.method(), "GET"); 142 assertTrue(!request.bodyPublisher().isPresent()); 143 144 request = newBuilder(uri).PUT(BodyPublishers.ofString("")).GET().build(); 145 assertEquals(request.method(), "GET"); 146 assertTrue(!request.bodyPublisher().isPresent()); 147 148 request = newBuilder(uri).DELETE().GET().build(); 149 assertEquals(request.method(), "GET"); 150 assertTrue(!request.bodyPublisher().isPresent()); 151 152 request = newBuilder(uri).POST(BodyPublishers.ofString("")).build(); 153 assertEquals(request.method(), "POST"); 154 assertTrue(request.bodyPublisher().isPresent()); 155 156 request = newBuilder(uri).PUT(BodyPublishers.ofString("")).build(); 157 assertEquals(request.method(), "PUT"); 158 assertTrue(request.bodyPublisher().isPresent()); 159 160 request = newBuilder(uri).DELETE().build(); 161 assertEquals(request.method(), "DELETE"); 162 assertTrue(!request.bodyPublisher().isPresent()); 163 164 request = newBuilder(uri).GET().POST(BodyPublishers.ofString("")).build(); 165 assertEquals(request.method(), "POST"); 166 assertTrue(request.bodyPublisher().isPresent()); 167 168 request = newBuilder(uri).GET().PUT(BodyPublishers.ofString("")).build(); 169 assertEquals(request.method(), "PUT"); 170 assertTrue(request.bodyPublisher().isPresent()); 171 172 request = newBuilder(uri).GET().DELETE().build(); 173 assertEquals(request.method(), "DELETE"); 174 assertTrue(!request.bodyPublisher().isPresent()); 175 176 // CONNECT is disallowed in the implementation, since it is used for 177 // tunneling, and is handled separately for security checks. 178 assertThrows(IAE, () -> newBuilder(uri).method("CONNECT", BodyPublishers.noBody()).build()); 179 180 request = newBuilder(uri).method("GET", BodyPublishers.noBody()).build(); 181 assertEquals(request.method(), "GET"); 182 assertTrue(request.bodyPublisher().isPresent()); 183 184 request = newBuilder(uri).method("POST", BodyPublishers.ofString("")).build(); 185 assertEquals(request.method(), "POST"); 186 assertTrue(request.bodyPublisher().isPresent()); 187 } 188 189 @Test 190 public void testHeaders() { 191 HttpRequest.Builder builder = newBuilder(uri); 192 193 String[] empty = new String[0]; 194 assertThrows(IAE, () -> builder.headers(empty).build()); 195 assertThrows(IAE, () -> builder.headers("1").build()); 196 assertThrows(IAE, () -> builder.headers("1", "2", "3").build()); 197 assertThrows(IAE, () -> builder.headers("1", "2", "3", "4", "5").build()); 198 assertEquals(builder.build().headers().map().size(),0); 199 200 List<HttpRequest> requests = List.of( 201 // same header built from different combinations of the API 202 newBuilder(uri).header("A", "B").build(), 203 newBuilder(uri).headers("A", "B").build(), 204 newBuilder(uri).setHeader("A", "B").build(), 205 newBuilder(uri).header("A", "F").setHeader("A", "B").build(), 206 newBuilder(uri).headers("A", "F").setHeader("A", "B").build() 207 ); 208 209 for (HttpRequest r : requests) { 210 assertEquals(r.headers().map().size(), 1); 211 assertTrue(r.headers().firstValue("A").isPresent()); 212 assertTrue(r.headers().firstValue("a").isPresent()); 213 assertEquals(r.headers().firstValue("A").get(), "B"); 214 assertEquals(r.headers().allValues("A"), List.of("B")); 215 assertEquals(r.headers().allValues("C").size(), 0); 216 assertEquals(r.headers().map().get("A"), List.of("B")); 217 assertThrows(NFE, () -> r.headers().firstValueAsLong("A")); 218 assertFalse(r.headers().firstValue("C").isPresent()); 219 // a non-exhaustive list of mutators 220 assertThrows(UOE, () -> r.headers().map().put("Z", List.of("Z"))); 221 assertThrows(UOE, () -> r.headers().map().remove("A")); 222 assertThrows(UOE, () -> r.headers().map().remove("A", "B")); 223 assertThrows(UOE, () -> r.headers().map().clear()); 224 assertThrows(UOE, () -> r.headers().allValues("A").remove("B")); 225 assertThrows(UOE, () -> r.headers().allValues("A").remove(1)); 226 assertThrows(UOE, () -> r.headers().allValues("A").clear()); 227 assertThrows(UOE, () -> r.headers().allValues("A").add("Z")); 228 assertThrows(UOE, () -> r.headers().allValues("A").addAll(List.of("Z"))); 229 assertThrows(UOE, () -> r.headers().allValues("A").add(1, "Z")); 230 } 231 232 requests = List.of( 233 // same headers built from different combinations of the API 234 newBuilder(uri).header("A", "B") 235 .header("C", "D").build(), 236 newBuilder(uri).header("A", "B") 237 .headers("C", "D").build(), 238 newBuilder(uri).header("A", "B") 239 .setHeader("C", "D").build(), 240 newBuilder(uri).headers("A", "B") 241 .headers("C", "D").build(), 242 newBuilder(uri).headers("A", "B") 243 .header("C", "D").build(), 244 newBuilder(uri).headers("A", "B") 245 .setHeader("C", "D").build(), 246 newBuilder(uri).setHeader("A", "B") 247 .setHeader("C", "D").build(), 248 newBuilder(uri).setHeader("A", "B") 249 .header("C", "D").build(), 250 newBuilder(uri).setHeader("A", "B") 251 .headers("C", "D").build(), 252 newBuilder(uri).headers("A", "B", "C", "D").build() 253 ); 254 255 for (HttpRequest r : requests) { 256 assertEquals(r.headers().map().size(), 2); 257 assertTrue(r.headers().firstValue("A").isPresent()); 258 assertEquals(r.headers().firstValue("A").get(), "B"); 259 assertEquals(r.headers().allValues("A"), List.of("B")); 260 assertTrue(r.headers().firstValue("C").isPresent()); 261 assertEquals(r.headers().firstValue("C").get(), "D"); 262 assertEquals(r.headers().allValues("C"), List.of("D")); 263 assertEquals(r.headers().map().get("C"), List.of("D")); 264 assertThrows(NFE, () -> r.headers().firstValueAsLong("C")); 265 assertFalse(r.headers().firstValue("E").isPresent()); 266 // a smaller non-exhaustive list of mutators 267 assertThrows(UOE, () -> r.headers().map().put("Z", List.of("Z"))); 268 assertThrows(UOE, () -> r.headers().map().remove("C")); 269 assertThrows(UOE, () -> r.headers().allValues("A").remove("B")); 270 assertThrows(UOE, () -> r.headers().allValues("A").clear()); 271 assertThrows(UOE, () -> r.headers().allValues("C").add("Z")); 272 } 273 274 requests = List.of( 275 // same multi-value headers built from different combinations of the API 276 newBuilder(uri).header("A", "B") 277 .header("A", "C").build(), 278 newBuilder(uri).header("A", "B") 279 .headers("A", "C").build(), 280 newBuilder(uri).headers("A", "B") 281 .headers("A", "C").build(), 282 newBuilder(uri).headers("A", "B") 283 .header("A", "C").build(), 284 newBuilder(uri).setHeader("A", "B") 285 .header("A", "C").build(), 286 newBuilder(uri).setHeader("A", "B") 287 .headers("A", "C").build(), 288 newBuilder(uri).header("A", "D") 289 .setHeader("A", "B") 290 .headers("A", "C").build(), 291 newBuilder(uri).headers("A", "B", "A", "C").build() 292 ); 293 294 for (HttpRequest r : requests) { 295 assertEquals(r.headers().map().size(), 1); 296 assertTrue(r.headers().firstValue("A").isPresent()); 297 assertTrue(r.headers().allValues("A").containsAll(List.of("B", "C"))); 298 assertEquals(r.headers().allValues("C").size(), 0); 299 assertEquals(r.headers().map().get("A"), List.of("B", "C")); 300 assertThrows(NFE, () -> r.headers().firstValueAsLong("A")); 301 assertFalse(r.headers().firstValue("C").isPresent()); 302 // a non-exhaustive list of mutators 303 assertThrows(UOE, () -> r.headers().map().put("Z", List.of("Z"))); 304 assertThrows(UOE, () -> r.headers().map().remove("A")); 305 assertThrows(UOE, () -> r.headers().map().remove("A", "B")); 306 assertThrows(UOE, () -> r.headers().map().clear()); 307 assertThrows(UOE, () -> r.headers().allValues("A").remove("B")); 308 assertThrows(UOE, () -> r.headers().allValues("A").remove(1)); 309 assertThrows(UOE, () -> r.headers().allValues("A").clear()); 310 assertThrows(UOE, () -> r.headers().allValues("A").add("Z")); 311 assertThrows(UOE, () -> r.headers().allValues("A").addAll(List.of("Z"))); 312 assertThrows(UOE, () -> r.headers().allValues("A").add(1, "Z")); 313 } 314 315 // case-insensitivity 316 requests = List.of( 317 newBuilder(uri) 318 .header("Accept-Encoding", "gzip, deflate").build(), 319 newBuilder(uri) 320 .header("accept-encoding", "gzip, deflate").build(), 321 newBuilder(uri) 322 .header("AccePt-EncodINg", "gzip, deflate").build(), 323 newBuilder(uri) 324 .header("AcCEpt-EncoDIng", "gzip, deflate").build() 325 ); 326 for (HttpRequest r : requests) { 327 for (String name : List.of("Accept-Encoding", "accept-encoding", 328 "aCCept-EnCODing", "accepT-encodinG")) { 329 assertTrue(r.headers().firstValue(name).isPresent()); 330 assertTrue(r.headers().allValues(name).contains("gzip, deflate")); 331 assertEquals(r.headers().firstValue(name).get(), "gzip, deflate"); 332 assertEquals(r.headers().allValues(name).size(), 1); 333 assertEquals(r.headers().map().size(), 1); 334 assertEquals(r.headers().map().get(name).size(), 1); 335 assertEquals(r.headers().map().get(name).get(0), "gzip, deflate"); 336 } 337 } 338 } 339 340 // headers that are allowed now, but weren't before 341 private static final Set<String> FORMERLY_RESTRICTED = Set.of("referer", "origin", 342 "OriGin", "Referer", "Date", "via", "WarnIng"); 343 344 @Test 345 public void testFormerlyRestricted() throws URISyntaxException { 346 URI uri = new URI("http://localhost:80/test/"); 347 URI otherURI = new URI("http://www.foo.com/test/"); 348 for (String header : FORMERLY_RESTRICTED) { 349 HttpRequest req = HttpRequest.newBuilder(uri) 350 .header(header, otherURI.toString()) 351 .GET() 352 .build(); 353 } 354 } 355 356 private static final Set<String> RESTRICTED = Set.of("connection", "content-length", 357 "expect", "host", "upgrade", "Connection", "Content-Length", 358 "eXpect", "hosT", "upgradE", "CONNection", "CONTENT-LENGTH", 359 "EXPECT", "Host", "Upgrade"); 360 361 interface WithHeader { 362 HttpRequest.Builder withHeader(HttpRequest.Builder builder, String name, String value); 363 } 364 365 @Test 366 public void testRestricted() throws URISyntaxException { 367 URI uri = new URI("http://localhost:80/test/"); 368 Map<String, WithHeader> lambdas = Map.of( 369 "Builder::header", HttpRequest.Builder::header, 370 "Builder::headers", (b, n, v) -> b.headers(n,v), 371 "Builder::setHeader", HttpRequest.Builder::setHeader 372 ); 373 for (Map.Entry<String, WithHeader> e : lambdas.entrySet()) { 374 System.out.println("Testing restricted headers with " + e.getKey()); 375 WithHeader f = e.getValue(); 376 for (String name : RESTRICTED) { 377 String value = name + "-value"; 378 HttpRequest req = f.withHeader(HttpRequest.newBuilder(uri) 379 .GET(), "x-" + name, value).build(); 380 String v = req.headers().firstValue("x-" + name).orElseThrow( 381 () -> new RuntimeException("header x-" + name + " not set")); 382 assertEquals(v, value); 383 try { 384 f.withHeader(HttpRequest.newBuilder(uri) 385 .GET(), name, value).build(); 386 throw new RuntimeException("Expected IAE not thrown for " + name); 387 } catch (IllegalArgumentException x) { 388 System.out.println("Got expected IAE for " + name + ": " + x); 389 } 390 } 391 } 392 } 393 394 395 @Test 396 public void testCopy() { 397 HttpRequest.Builder builder = newBuilder(uri).expectContinue(true) 398 .header("A", "B") 399 .POST(BodyPublishers.ofString("")) 400 .timeout(ofSeconds(30)) 401 .version(HTTP_1_1); 402 HttpRequest.Builder copy = builder.copy(); 403 assertTrue(builder != copy); 404 405 // modify the original builder before building from the copy 406 builder.GET().timeout(ofSeconds(5)).version(HTTP_2).setHeader("A", "C"); 407 408 HttpRequest copyRequest = copy.build(); 409 assertEquals(copyRequest.uri(), uri); 410 assertEquals(copyRequest.expectContinue(), true); 411 assertEquals(copyRequest.headers().map().get("A"), List.of("B")); 412 assertEquals(copyRequest.method(), "POST"); 413 assertEquals(copyRequest.bodyPublisher().isPresent(), true); 414 assertEquals(copyRequest.timeout().get(), ofSeconds(30)); 415 assertTrue(copyRequest.version().isPresent()); 416 assertEquals(copyRequest.version().get(), HTTP_1_1); 417 418 // lazy set URI ( maybe builder as a template ) 419 copyRequest = newBuilder().copy().uri(uri).build(); 420 assertEquals(copyRequest.uri(), uri); 421 422 builder = newBuilder().header("C", "D"); 423 copy = builder.copy(); 424 copy.uri(uri); 425 copyRequest = copy.build(); 426 assertEquals(copyRequest.uri(), uri); 427 assertEquals(copyRequest.headers().firstValue("C").get(), "D"); 428 } 429 430 @Test 431 public void testTimeout() { 432 HttpRequest.Builder builder = newBuilder(uri); 433 assertThrows(IAE, () -> builder.timeout(ZERO)); 434 assertThrows(IAE, () -> builder.timeout(ofSeconds(0))); 435 assertThrows(IAE, () -> builder.timeout(ofSeconds(-1))); 436 assertThrows(IAE, () -> builder.timeout(ofNanos(-100))); 437 assertEquals(builder.timeout(ofNanos(15)).build().timeout().get(), ofNanos(15)); 438 assertEquals(builder.timeout(ofSeconds(50)).build().timeout().get(), ofSeconds(50)); 439 assertEquals(builder.timeout(ofMinutes(30)).build().timeout().get(), ofMinutes(30)); 440 } 441 442 @Test 443 public void testExpect() { 444 HttpRequest.Builder builder = newBuilder(uri); 445 assertEquals(builder.build().expectContinue(), false); 446 assertEquals(builder.expectContinue(true).build().expectContinue(), true); 447 assertEquals(builder.expectContinue(false).build().expectContinue(), false); 448 assertEquals(builder.expectContinue(true).build().expectContinue(), true); 449 } 450 451 @Test 452 public void testEquals() { 453 assertNotEquals(newBuilder(URI.create("http://foo.com")), 454 newBuilder(URI.create("http://bar.com"))); 455 456 HttpRequest.Builder builder = newBuilder(uri); 457 assertEquals(builder.build(), builder.build()); 458 assertEquals(builder.build(), newBuilder(uri).build()); 459 460 builder.POST(BodyPublishers.noBody()); 461 assertEquals(builder.build(), builder.build()); 462 assertEquals(builder.build(), newBuilder(uri).POST(BodyPublishers.noBody()).build()); 463 assertEquals(builder.build(), newBuilder(uri).POST(BodyPublishers.ofString("")).build()); 464 assertNotEquals(builder.build(), newBuilder(uri).build()); 465 assertNotEquals(builder.build(), newBuilder(uri).GET().build()); 466 assertNotEquals(builder.build(), newBuilder(uri).PUT(BodyPublishers.noBody()).build()); 467 468 builder = newBuilder(uri).header("x", "y"); 469 assertEquals(builder.build(), builder.build()); 470 assertEquals(builder.build(), newBuilder(uri).header("x", "y").build()); 471 assertNotEquals(builder.build(), newBuilder(uri).header("x", "Z").build()); 472 assertNotEquals(builder.build(), newBuilder(uri).header("z", "y").build()); 473 } 474 }