* Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
* @test
* @bug 8276559
* @summary HttpRequest[.Builder] API and behaviour checks
* @run testng RequestBuilderTest
*/
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import static java.time.Duration.ofNanos;
import static java.time.Duration.ofMinutes;
import static java.time.Duration.ofSeconds;
import static java.time.Duration.ZERO;
import static java.net.http.HttpClient.Version.HTTP_1_1;
import static java.net.http.HttpClient.Version.HTTP_2;
import static java.net.http.HttpRequest.newBuilder;
import static org.testng.Assert.*;
import org.testng.annotations.Test;
public class RequestBuilderTest {
static final URI uri = URI.create("http://foo.com/");
static final Class<NullPointerException> NPE = NullPointerException.class;
static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
static final Class<IllegalStateException> ISE = IllegalStateException.class;
static final Class<NumberFormatException> NFE = NumberFormatException.class;
static final Class<UnsupportedOperationException> UOE = UnsupportedOperationException.class;
@Test
public void testDefaults() {
List<HttpRequest.Builder> builders = List.of(newBuilder().uri(uri),
newBuilder(uri),
newBuilder().copy().uri(uri),
newBuilder(uri).copy());
for (HttpRequest.Builder builder : builders) {
assertFalse(builder.build().expectContinue());
assertEquals(builder.build().method(), "GET");
assertFalse(builder.build().bodyPublisher().isPresent());
assertFalse(builder.build().version().isPresent());
assertFalse(builder.build().timeout().isPresent());
assertTrue(builder.build().headers() != null);
assertEquals(builder.build().headers().map().size(), 0);
}
}
@Test
public void testNull() {
HttpRequest.Builder builder = newBuilder();
assertThrows(NPE, () -> newBuilder(null).build());
assertThrows(NPE, () -> newBuilder(uri).uri(null).build());
assertThrows(NPE, () -> builder.uri(null));
assertThrows(NPE, () -> builder.version(null));
assertThrows(NPE, () -> builder.header(null, null));
assertThrows(NPE, () -> builder.header("name", null));
assertThrows(NPE, () -> builder.header(null, "value"));
assertThrows(NPE, () -> builder.headers(null));
assertThrows(NPE, () -> builder.headers(new String[] { null, null }));
assertThrows(NPE, () -> builder.headers(new String[] { "name", null }));
assertThrows(NPE, () -> builder.headers(new String[] { null, "value" }));
assertThrows(NPE, () -> builder.method(null, null));
assertThrows(NPE, () -> builder.method("GET", null));
assertThrows(NPE, () -> builder.method("POST", null));
assertThrows(NPE, () -> builder.method("PUT", null));
assertThrows(NPE, () -> builder.method("DELETE", null));
assertThrows(NPE, () -> builder.setHeader(null, null));
assertThrows(NPE, () -> builder.setHeader("name", null));
assertThrows(NPE, () -> builder.setHeader(null, "value"));
assertThrows(NPE, () -> builder.timeout(null));
assertThrows(NPE, () -> builder.POST(null));
assertThrows(NPE, () -> builder.PUT(null));
}
@Test
public void testURI() {
assertThrows(ISE, () -> newBuilder().build());
List<URI> uris = List.of(
URI.create("ws://foo.com"),
URI.create("wss://foo.com"),
URI.create("ftp://foo.com"),
URI.create("mailto:a@b.com"),
URI.create("scheme:example.com"),
URI.create("scheme:example.com"),
URI.create("scheme:example.com/path"),
URI.create("path"),
URI.create("/path")
);
for (URI u : uris) {
assertThrows(IAE, () -> newBuilder(u));
assertThrows(IAE, () -> newBuilder().uri(u));
}
assertEquals(newBuilder(uri).build().uri(), uri);
assertEquals(newBuilder().uri(uri).build().uri(), uri);
URI https = URI.create("https://foo.com");
assertEquals(newBuilder(https).build().uri(), https);
assertEquals(newBuilder().uri(https).build().uri(), https);
}
@Test
public void testMethod() {
HttpRequest request = newBuilder(uri).build();
assertEquals(request.method(), "GET");
assertTrue(!request.bodyPublisher().isPresent());
request = newBuilder(uri).GET().build();
assertEquals(request.method(), "GET");
assertTrue(!request.bodyPublisher().isPresent());
request = newBuilder(uri).POST(BodyPublishers.ofString("")).GET().build();
assertEquals(request.method(), "GET");
assertTrue(!request.bodyPublisher().isPresent());
request = newBuilder(uri).PUT(BodyPublishers.ofString("")).GET().build();
assertEquals(request.method(), "GET");
assertTrue(!request.bodyPublisher().isPresent());
request = newBuilder(uri).DELETE().GET().build();
assertEquals(request.method(), "GET");
assertTrue(!request.bodyPublisher().isPresent());
request = newBuilder(uri).POST(BodyPublishers.ofString("")).build();
assertEquals(request.method(), "POST");
assertTrue(request.bodyPublisher().isPresent());
request = newBuilder(uri).PUT(BodyPublishers.ofString("")).build();
assertEquals(request.method(), "PUT");
assertTrue(request.bodyPublisher().isPresent());
request = newBuilder(uri).DELETE().build();
assertEquals(request.method(), "DELETE");
assertTrue(!request.bodyPublisher().isPresent());
request = newBuilder(uri).HEAD().build();
assertEquals(request.method(), "HEAD");
assertFalse(request.bodyPublisher().isPresent());
request = newBuilder(uri).GET().POST(BodyPublishers.ofString("")).build();
assertEquals(request.method(), "POST");
assertTrue(request.bodyPublisher().isPresent());
request = newBuilder(uri).GET().PUT(BodyPublishers.ofString("")).build();
assertEquals(request.method(), "PUT");
assertTrue(request.bodyPublisher().isPresent());
request = newBuilder(uri).GET().DELETE().build();
assertEquals(request.method(), "DELETE");
assertTrue(!request.bodyPublisher().isPresent());
assertThrows(IAE, () -> newBuilder(uri).method("CONNECT", BodyPublishers.noBody()).build());
request = newBuilder(uri).method("GET", BodyPublishers.noBody()).build();
assertEquals(request.method(), "GET");
assertTrue(request.bodyPublisher().isPresent());
request = newBuilder(uri).method("POST", BodyPublishers.ofString("")).build();
assertEquals(request.method(), "POST");
assertTrue(request.bodyPublisher().isPresent());
}
@Test
public void testHeaders() {
HttpRequest.Builder builder = newBuilder(uri);
String[] empty = new String[0];
assertThrows(IAE, () -> builder.headers(empty).build());
assertThrows(IAE, () -> builder.headers("1").build());
assertThrows(IAE, () -> builder.headers("1", "2", "3").build());
assertThrows(IAE, () -> builder.headers("1", "2", "3", "4", "5").build());
assertEquals(builder.build().headers().map().size(),0);
List<HttpRequest> requests = List.of(
newBuilder(uri).header("A", "B").build(),
newBuilder(uri).headers("A", "B").build(),
newBuilder(uri).setHeader("A", "B").build(),
newBuilder(uri).header("A", "F").setHeader("A", "B").build(),
newBuilder(uri).headers("A", "F").setHeader("A", "B").build()
);
for (HttpRequest r : requests) {
assertEquals(r.headers().map().size(), 1);
assertTrue(r.headers().firstValue("A").isPresent());
assertTrue(r.headers().firstValue("a").isPresent());
assertEquals(r.headers().firstValue("A").get(), "B");
assertEquals(r.headers().allValues("A"), List.of("B"));
assertEquals(r.headers().allValues("C").size(), 0);
assertEquals(r.headers().map().get("A"), List.of("B"));
assertThrows(NFE, () -> r.headers().firstValueAsLong("A"));
assertFalse(r.headers().firstValue("C").isPresent());
assertThrows(UOE, () -> r.headers().map().put("Z", List.of("Z")));
assertThrows(UOE, () -> r.headers().map().remove("A"));
assertThrows(UOE, () -> r.headers().map().remove("A", "B"));
assertThrows(UOE, () -> r.headers().map().clear());
assertThrows(UOE, () -> r.headers().allValues("A").remove("B"));
assertThrows(UOE, () -> r.headers().allValues("A").remove(1));
assertThrows(UOE, () -> r.headers().allValues("A").clear());
assertThrows(UOE, () -> r.headers().allValues("A").add("Z"));
assertThrows(UOE, () -> r.headers().allValues("A").addAll(List.of("Z")));
assertThrows(UOE, () -> r.headers().allValues("A").add(1, "Z"));
}
requests = List.of(
newBuilder(uri).header("A", "B")
.header("C", "D").build(),
newBuilder(uri).header("A", "B")
.headers("C", "D").build(),
newBuilder(uri).header("A", "B")
.setHeader("C", "D").build(),
newBuilder(uri).headers("A", "B")
.headers("C", "D").build(),
newBuilder(uri).headers("A", "B")
.header("C", "D").build(),
newBuilder(uri).headers("A", "B")
.setHeader("C", "D").build(),
newBuilder(uri).setHeader("A", "B")
.setHeader("C", "D").build(),
newBuilder(uri).setHeader("A", "B")
.header("C", "D").build(),
newBuilder(uri).setHeader("A", "B")
.headers("C", "D").build(),
newBuilder(uri).headers("A", "B", "C", "D").build()
);
for (HttpRequest r : requests) {
assertEquals(r.headers().map().size(), 2);
assertTrue(r.headers().firstValue("A").isPresent());
assertEquals(r.headers().firstValue("A").get(), "B");
assertEquals(r.headers().allValues("A"), List.of("B"));
assertTrue(r.headers().firstValue("C").isPresent());
assertEquals(r.headers().firstValue("C").get(), "D");
assertEquals(r.headers().allValues("C"), List.of("D"));
assertEquals(r.headers().map().get("C"), List.of("D"));
assertThrows(NFE, () -> r.headers().firstValueAsLong("C"));
assertFalse(r.headers().firstValue("E").isPresent());
assertThrows(UOE, () -> r.headers().map().put("Z", List.of("Z")));
assertThrows(UOE, () -> r.headers().map().remove("C"));
assertThrows(UOE, () -> r.headers().allValues("A").remove("B"));
assertThrows(UOE, () -> r.headers().allValues("A").clear());
assertThrows(UOE, () -> r.headers().allValues("C").add("Z"));
}
requests = List.of(
newBuilder(uri).header("A", "B")
.header("A", "C").build(),
newBuilder(uri).header("A", "B")
.headers("A", "C").build(),
newBuilder(uri).headers("A", "B")
.headers("A", "C").build(),
newBuilder(uri).headers("A", "B")
.header("A", "C").build(),
newBuilder(uri).setHeader("A", "B")
.header("A", "C").build(),
newBuilder(uri).setHeader("A", "B")
.headers("A", "C").build(),
newBuilder(uri).header("A", "D")
.setHeader("A", "B")
.headers("A", "C").build(),
newBuilder(uri).headers("A", "B", "A", "C").build()
);
for (HttpRequest r : requests) {
assertEquals(r.headers().map().size(), 1);
assertTrue(r.headers().firstValue("A").isPresent());
assertTrue(r.headers().allValues("A").containsAll(List.of("B", "C")));
assertEquals(r.headers().allValues("C").size(), 0);
assertEquals(r.headers().map().get("A"), List.of("B", "C"));
assertThrows(NFE, () -> r.headers().firstValueAsLong("A"));
assertFalse(r.headers().firstValue("C").isPresent());
assertThrows(UOE, () -> r.headers().map().put("Z", List.of("Z")));
assertThrows(UOE, () -> r.headers().map().remove("A"));
assertThrows(UOE, () -> r.headers().map().remove("A", "B"));
assertThrows(UOE, () -> r.headers().map().clear());
assertThrows(UOE, () -> r.headers().allValues("A").remove("B"));
assertThrows(UOE, () -> r.headers().allValues("A").remove(1));
assertThrows(UOE, () -> r.headers().allValues("A").clear());
assertThrows(UOE, () -> r.headers().allValues("A").add("Z"));
assertThrows(UOE, () -> r.headers().allValues("A").addAll(List.of("Z")));
assertThrows(UOE, () -> r.headers().allValues("A").add(1, "Z"));
}
requests = List.of(
newBuilder(uri)
.header("Accept-Encoding", "gzip, deflate").build(),
newBuilder(uri)
.header("accept-encoding", "gzip, deflate").build(),
newBuilder(uri)
.header("AccePt-EncodINg", "gzip, deflate").build(),
newBuilder(uri)
.header("AcCEpt-EncoDIng", "gzip, deflate").build()
);
for (HttpRequest r : requests) {
for (String name : List.of("Accept-Encoding", "accept-encoding",
"aCCept-EnCODing", "accepT-encodinG")) {
assertTrue(r.headers().firstValue(name).isPresent());
assertTrue(r.headers().allValues(name).contains("gzip, deflate"));
assertEquals(r.headers().firstValue(name).get(), "gzip, deflate");
assertEquals(r.headers().allValues(name).size(), 1);
assertEquals(r.headers().map().size(), 1);
assertEquals(r.headers().map().get(name).size(), 1);
assertEquals(r.headers().map().get(name).get(0), "gzip, deflate");
}
}
}
private static final Set<String> FORMERLY_RESTRICTED = Set.of("referer", "origin",
"OriGin", "Referer", "Date", "via", "WarnIng");
@Test
public void testFormerlyRestricted() throws URISyntaxException {
URI uri = new URI("http://localhost:80/test/");
URI otherURI = new URI("http://www.foo.com/test/");
for (String header : FORMERLY_RESTRICTED) {
HttpRequest req = HttpRequest.newBuilder(uri)
.header(header, otherURI.toString())
.GET()
.build();
}
}
private static final Set<String> RESTRICTED = Set.of("connection", "content-length",
"expect", "host", "upgrade", "Connection", "Content-Length",
"eXpect", "hosT", "upgradE", "CONNection", "CONTENT-LENGTH",
"EXPECT", "Host", "Upgrade");
interface WithHeader {
HttpRequest.Builder withHeader(HttpRequest.Builder builder, String name, String value);
}
@Test
public void testRestricted() throws URISyntaxException {
URI uri = new URI("http://localhost:80/test/");
Map<String, WithHeader> lambdas = Map.of(
"Builder::header", HttpRequest.Builder::header,
"Builder::headers", (b, n, v) -> b.headers(n,v),
"Builder::setHeader", HttpRequest.Builder::setHeader
);
for (Map.Entry<String, WithHeader> e : lambdas.entrySet()) {
System.out.println("Testing restricted headers with " + e.getKey());
WithHeader f = e.getValue();
for (String name : RESTRICTED) {
String value = name + "-value";
HttpRequest req = f.withHeader(HttpRequest.newBuilder(uri)
.GET(), "x-" + name, value).build();
String v = req.headers().firstValue("x-" + name).orElseThrow(
() -> new RuntimeException("header x-" + name + " not set"));
assertEquals(v, value);
try {
f.withHeader(HttpRequest.newBuilder(uri)
.GET(), name, value).build();
throw new RuntimeException("Expected IAE not thrown for " + name);
} catch (IllegalArgumentException x) {
System.out.println("Got expected IAE for " + name + ": " + x);
}
}
}
}
@Test
public void testCopy() {
HttpRequest.Builder builder = newBuilder(uri).expectContinue(true)
.header("A", "B")
.POST(BodyPublishers.ofString(""))
.timeout(ofSeconds(30))
.version(HTTP_1_1);
HttpRequest.Builder copy = builder.copy();
assertTrue(builder != copy);
builder.GET().timeout(ofSeconds(5)).version(HTTP_2).setHeader("A", "C");
HttpRequest copyRequest = copy.build();
assertEquals(copyRequest.uri(), uri);
assertEquals(copyRequest.expectContinue(), true);
assertEquals(copyRequest.headers().map().get("A"), List.of("B"));
assertEquals(copyRequest.method(), "POST");
assertEquals(copyRequest.bodyPublisher().isPresent(), true);
assertEquals(copyRequest.timeout().get(), ofSeconds(30));
assertTrue(copyRequest.version().isPresent());
assertEquals(copyRequest.version().get(), HTTP_1_1);
copyRequest = newBuilder().copy().uri(uri).build();
assertEquals(copyRequest.uri(), uri);
builder = newBuilder().header("C", "D");
copy = builder.copy();
copy.uri(uri);
copyRequest = copy.build();
assertEquals(copyRequest.uri(), uri);
assertEquals(copyRequest.headers().firstValue("C").get(), "D");
}
@Test
public void testTimeout() {
HttpRequest.Builder builder = newBuilder(uri);
assertThrows(IAE, () -> builder.timeout(ZERO));
assertThrows(IAE, () -> builder.timeout(ofSeconds(0)));
assertThrows(IAE, () -> builder.timeout(ofSeconds(-1)));
assertThrows(IAE, () -> builder.timeout(ofNanos(-100)));
assertEquals(builder.timeout(ofNanos(15)).build().timeout().get(), ofNanos(15));
assertEquals(builder.timeout(ofSeconds(50)).build().timeout().get(), ofSeconds(50));
assertEquals(builder.timeout(ofMinutes(30)).build().timeout().get(), ofMinutes(30));
}
@Test
public void testExpect() {
HttpRequest.Builder builder = newBuilder(uri);
assertEquals(builder.build().expectContinue(), false);
assertEquals(builder.expectContinue(true).build().expectContinue(), true);
assertEquals(builder.expectContinue(false).build().expectContinue(), false);
assertEquals(builder.expectContinue(true).build().expectContinue(), true);
}
@Test
public void testEquals() {
assertNotEquals(newBuilder(URI.create("http://foo.com")),
newBuilder(URI.create("http://bar.com")));
HttpRequest.Builder builder = newBuilder(uri);
assertEquals(builder.build(), builder.build());
assertEquals(builder.build(), newBuilder(uri).build());
builder.POST(BodyPublishers.noBody());
assertEquals(builder.build(), builder.build());
assertEquals(builder.build(), newBuilder(uri).POST(BodyPublishers.noBody()).build());
assertEquals(builder.build(), newBuilder(uri).POST(BodyPublishers.ofString("")).build());
assertNotEquals(builder.build(), newBuilder(uri).build());
assertNotEquals(builder.build(), newBuilder(uri).GET().build());
assertNotEquals(builder.build(), newBuilder(uri).PUT(BodyPublishers.noBody()).build());
builder = newBuilder(uri).header("x", "y");
assertEquals(builder.build(), builder.build());
assertEquals(builder.build(), newBuilder(uri).header("x", "y").build());
assertNotEquals(builder.build(), newBuilder(uri).header("x", "Z").build());
assertNotEquals(builder.build(), newBuilder(uri).header("z", "y").build());
}
}