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 }