1 /*
   2  * Copyright (c) 2015, 2017, 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  * @bug 8161157
  27  * @summary Test response body handlers/subscribers when there is no body
  28  * @library /lib/testlibrary http2/server
  29  * @build jdk.testlibrary.SimpleSSLContext
  30  * @modules java.base/sun.net.www.http
  31  *          jdk.incubator.httpclient/jdk.incubator.http.internal.common
  32  *          jdk.incubator.httpclient/jdk.incubator.http.internal.frame
  33  *          jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
  34  * @run testng/othervm NoBody
  35  */
  36 
  37 import java.io.IOException;
  38 import java.io.InputStream;
  39 import java.net.InetSocketAddress;
  40 import java.net.URI;
  41 import java.nio.file.Files;
  42 import java.nio.file.Path;
  43 import java.nio.file.Paths;
  44 import java.util.Optional;
  45 import java.util.concurrent.Executor;
  46 import java.util.concurrent.Executors;
  47 import java.util.function.Consumer;
  48 import com.sun.net.httpserver.HttpExchange;
  49 import com.sun.net.httpserver.HttpHandler;
  50 import com.sun.net.httpserver.HttpServer;
  51 import com.sun.net.httpserver.HttpsConfigurator;
  52 import com.sun.net.httpserver.HttpsServer;
  53 import jdk.incubator.http.HttpClient;
  54 import jdk.incubator.http.HttpRequest;
  55 import jdk.incubator.http.HttpResponse;
  56 import jdk.incubator.http.HttpResponse.BodyHandler;
  57 import javax.net.ssl.SSLContext;
  58 import jdk.testlibrary.SimpleSSLContext;
  59 import org.testng.annotations.AfterTest;
  60 import org.testng.annotations.BeforeTest;
  61 import org.testng.annotations.DataProvider;
  62 import org.testng.annotations.Test;
  63 import static java.lang.System.out;
  64 import static java.nio.charset.StandardCharsets.UTF_8;
  65 import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString;
  66 import static jdk.incubator.http.HttpResponse.BodyHandler.asByteArray;
  67 import static jdk.incubator.http.HttpResponse.BodyHandler.asByteArrayConsumer;
  68 import static jdk.incubator.http.HttpResponse.BodyHandler.asFile;
  69 import static jdk.incubator.http.HttpResponse.BodyHandler.asInputStream;
  70 import static jdk.incubator.http.HttpResponse.BodyHandler.asString;
  71 import static jdk.incubator.http.HttpResponse.BodyHandler.buffering;
  72 import static jdk.incubator.http.HttpResponse.BodyHandler.discard;
  73 import static org.testng.Assert.assertEquals;
  74 import static org.testng.Assert.assertTrue;
  75 import static org.testng.Assert.fail;
  76 
  77 public class NoBody {
  78 
  79     SSLContext sslContext;
  80     HttpServer httpTestServer;         // HTTP/1.1    [ 4 servers ]
  81     HttpsServer httpsTestServer;       // HTTPS/1.1
  82     Http2TestServer http2TestServer;   // HTTP/2 ( h2c )
  83     Http2TestServer https2TestServer;  // HTTP/2 ( h2  )
  84     String httpURI_fixed;
  85     String httpURI_chunk;
  86     String httpsURI_fixed;
  87     String httpsURI_chunk;
  88     String http2URI_fixed;
  89     String http2URI_chunk;
  90     String https2URI_fixed;
  91     String https2URI_chunk;
  92 
  93     static final String SIMPLE_STRING = "Hello world. Goodbye world";
  94     static final int ITERATION_COUNT = 10;
  95     // a shared executor helps reduce the amount of threads created by the test
  96     static final Executor executor = Executors.newCachedThreadPool();
  97 
  98     @DataProvider(name = "variants")
  99     public Object[][] variants() {
 100         return new Object[][]{
 101                 { httpURI_fixed,    false },
 102                 { httpURI_chunk,    false },
 103                 { httpsURI_fixed,   false },
 104                 { httpsURI_chunk,   false },
 105                 { http2URI_fixed,   false },
 106                 { http2URI_chunk,   false },
 107                 { https2URI_fixed,  false,},
 108                 { https2URI_chunk,  false },
 109 
 110                 { httpURI_fixed,    true },
 111                 { httpURI_chunk,    true },
 112                 { httpsURI_fixed,   true },
 113                 { httpsURI_chunk,   true },
 114                 { http2URI_fixed,   true },
 115                 { http2URI_chunk,   true },
 116                 { https2URI_fixed,  true,},
 117                 { https2URI_chunk,  true },
 118         };
 119     }
 120 
 121     HttpClient newHttpClient() {
 122         return HttpClient.newBuilder()
 123                          .executor(executor)
 124                          .sslContext(sslContext)
 125                          .build();
 126     }
 127 
 128     @Test(dataProvider = "variants")
 129     public void testAsString(String uri, boolean sameClient) throws Exception {
 130         HttpClient client = null;
 131         for (int i=0; i< ITERATION_COUNT; i++) {
 132             if (!sameClient || client == null)
 133                 client = newHttpClient();
 134 
 135             HttpRequest req = HttpRequest.newBuilder(URI.create(uri))
 136                                          .PUT(fromString(SIMPLE_STRING))
 137                                          .build();
 138             BodyHandler<String> handler = i % 2 == 0 ? asString() : asString(UTF_8);
 139             HttpResponse<String> response = client.send(req, handler);
 140             String body = response.body();
 141             assertEquals(body, "");
 142         }
 143     }
 144 
 145     @Test(dataProvider = "variants")
 146     public void testAsFile(String uri, boolean sameClient) throws Exception {
 147         HttpClient client = null;
 148         for (int i=0; i< ITERATION_COUNT; i++) {
 149             if (!sameClient || client == null)
 150                 client = newHttpClient();
 151 
 152             HttpRequest req = HttpRequest.newBuilder(URI.create(uri))
 153                                          .PUT(fromString(SIMPLE_STRING))
 154                                          .build();
 155             Path p = Paths.get("NoBody_testAsFile.txt");
 156             HttpResponse<Path> response = client.send(req, asFile(p));
 157             Path bodyPath = response.body();
 158             assertTrue(Files.exists(bodyPath));
 159             assertEquals(Files.size(bodyPath), 0);
 160         }
 161     }
 162 
 163     @Test(dataProvider = "variants")
 164     public void testAsByteArray(String uri, boolean sameClient) throws Exception {
 165         HttpClient client = null;
 166         for (int i=0; i< ITERATION_COUNT; i++) {
 167             if (!sameClient || client == null)
 168                 client = newHttpClient();
 169 
 170             HttpRequest req = HttpRequest.newBuilder(URI.create(uri))
 171                                          .PUT(fromString(SIMPLE_STRING))
 172                                          .build();
 173             HttpResponse<byte[]> response = client.send(req, asByteArray());
 174             byte[] body = response.body();
 175             assertEquals(body.length, 0);
 176         }
 177     }
 178 
 179     volatile boolean consumerHasBeenCalled;
 180     @Test(dataProvider = "variants")
 181     public void testAsByteArrayConsumer(String uri, boolean sameClient) throws Exception {
 182         HttpClient client = null;
 183         for (int i=0; i< ITERATION_COUNT; i++) {
 184             if (!sameClient || client == null)
 185                 client = newHttpClient();
 186 
 187             HttpRequest req = HttpRequest.newBuilder(URI.create(uri))
 188                                          .PUT(fromString(SIMPLE_STRING))
 189                                          .build();
 190             Consumer<Optional<byte[]>>  consumer = oba -> {
 191                 consumerHasBeenCalled = true;
 192                 oba.ifPresent(ba -> fail("Unexpected non-empty optional:" + ba));
 193             };
 194             consumerHasBeenCalled = false;
 195             client.send(req, asByteArrayConsumer(consumer));
 196             assertTrue(consumerHasBeenCalled);
 197         }
 198     }
 199 
 200     @Test(dataProvider = "variants")
 201     public void testAsInputStream(String uri, boolean sameClient) throws Exception {
 202         HttpClient client = null;
 203         for (int i=0; i< ITERATION_COUNT; i++) {
 204             if (!sameClient || client == null)
 205                 client = newHttpClient();
 206 
 207             HttpRequest req = HttpRequest.newBuilder(URI.create(uri))
 208                                          .PUT(fromString(SIMPLE_STRING))
 209                                          .build();
 210             HttpResponse<InputStream> response = client.send(req, asInputStream());
 211             byte[] body = response.body().readAllBytes();
 212             assertEquals(body.length, 0);
 213         }
 214     }
 215 
 216     @Test(dataProvider = "variants")
 217     public void testBuffering(String uri, boolean sameClient) throws Exception {
 218         HttpClient client = null;
 219         for (int i=0; i< ITERATION_COUNT; i++) {
 220             if (!sameClient || client == null)
 221                 client = newHttpClient();
 222 
 223             HttpRequest req = HttpRequest.newBuilder(URI.create(uri))
 224                                          .PUT(fromString(SIMPLE_STRING))
 225                                          .build();
 226             HttpResponse<byte[]> response = client.send(req, buffering(asByteArray(), 1024));
 227             byte[] body = response.body();
 228             assertEquals(body.length, 0);
 229         }
 230     }
 231 
 232     @Test(dataProvider = "variants")
 233     public void testDiscard(String uri, boolean sameClient) throws Exception {
 234         HttpClient client = null;
 235         for (int i=0; i< ITERATION_COUNT; i++) {
 236             if (!sameClient || client == null)
 237                 client = newHttpClient();
 238 
 239             HttpRequest req = HttpRequest.newBuilder(URI.create(uri))
 240                                          .PUT(fromString(SIMPLE_STRING))
 241                                          .build();
 242             Object obj = new Object();
 243             HttpResponse<Object> response = client.send(req, discard(obj));
 244             assertEquals(response.body(), obj);
 245         }
 246     }
 247 
 248 
 249     @BeforeTest
 250     public void setup() throws Exception {
 251         sslContext = new SimpleSSLContext().get();
 252         if (sslContext == null)
 253             throw new AssertionError("Unexpected null sslContext");
 254 
 255         // HTTP/1.1
 256         HttpHandler h1_fixedLengthNoBodyHandler = new HTTP1_FixedLengthNoBodyHandler();
 257         HttpHandler h1_chunkNoBodyHandler = new HTTP1_ChunkedNoBodyHandler();
 258         InetSocketAddress sa = new InetSocketAddress("localhost", 0);
 259         httpTestServer = HttpServer.create(sa, 0);
 260         httpTestServer.createContext("/http1/noBodyFixed", h1_fixedLengthNoBodyHandler);
 261         httpTestServer.createContext("/http1/noBodyChunk", h1_chunkNoBodyHandler);
 262         httpURI_fixed = "http://127.0.0.1:" + httpTestServer.getAddress().getPort() + "/http1/noBodyFixed";
 263         httpURI_chunk = "http://127.0.0.1:" + httpTestServer.getAddress().getPort() + "/http1/noBodyChunk";
 264 
 265         httpsTestServer = HttpsServer.create(sa, 0);
 266         httpsTestServer.setHttpsConfigurator(new HttpsConfigurator(sslContext));
 267         httpsTestServer.createContext("/https1/noBodyFixed", h1_fixedLengthNoBodyHandler);
 268         httpsTestServer.createContext("/https1/noBodyChunk", h1_chunkNoBodyHandler);
 269         httpsURI_fixed = "https://127.0.0.1:" + httpsTestServer.getAddress().getPort() + "/https1/noBodyFixed";
 270         httpsURI_chunk = "https://127.0.0.1:" + httpsTestServer.getAddress().getPort() + "/https1/noBodyChunk";
 271 
 272         // HTTP/2
 273         Http2Handler h2_fixedLengthNoBodyHandler = new HTTP2_FixedLengthNoBodyHandler();
 274         Http2Handler h2_chunkedNoBodyHandler = new HTTP2_ChunkedNoBodyHandler();
 275 
 276         http2TestServer = new Http2TestServer("127.0.0.1", false, 0);
 277         http2TestServer.addHandler(h2_fixedLengthNoBodyHandler, "/http2/noBodyFixed");
 278         http2TestServer.addHandler(h2_chunkedNoBodyHandler, "/http2/noBodyChunk");
 279         int port = http2TestServer.getAddress().getPort();
 280         http2URI_fixed = "http://127.0.0.1:" + port + "/http2/noBodyFixed";
 281         http2URI_chunk = "http://127.0.0.1:" + port + "/http2/noBodyChunk";
 282 
 283         https2TestServer = new Http2TestServer("127.0.0.1", true, 0);
 284         https2TestServer.addHandler(h2_fixedLengthNoBodyHandler, "/https2/noBodyFixed");
 285         https2TestServer.addHandler(h2_chunkedNoBodyHandler, "/https2/noBodyChunk");
 286         port = https2TestServer.getAddress().getPort();
 287         https2URI_fixed = "https://127.0.0.1:" + port + "/https2/noBodyFixed";
 288         https2URI_chunk = "https://127.0.0.1:" + port + "/https2/noBodyChunk";
 289 
 290         httpTestServer.start();
 291         httpsTestServer.start();
 292         http2TestServer.start();
 293         https2TestServer.start();
 294     }
 295 
 296     @AfterTest
 297     public void teardown() throws Exception {
 298         httpTestServer.stop(0);
 299         httpsTestServer.stop(0);
 300         http2TestServer.stop();
 301         https2TestServer.stop();
 302     }
 303 
 304     static class HTTP1_FixedLengthNoBodyHandler implements HttpHandler {
 305         @Override
 306         public void handle(HttpExchange t) throws IOException {
 307             //out.println("NoBodyHandler received request to " + t.getRequestURI());
 308             try (InputStream is = t.getRequestBody()) {
 309                 is.readAllBytes();
 310             }
 311             t.sendResponseHeaders(200, -1); // no body
 312         }
 313     }
 314 
 315     static class HTTP1_ChunkedNoBodyHandler implements HttpHandler {
 316         @Override
 317         public void handle(HttpExchange t) throws IOException {
 318             //out.println("NoBodyHandler received request to " + t.getRequestURI());
 319             try (InputStream is = t.getRequestBody()) {
 320                 is.readAllBytes();
 321             }
 322             t.sendResponseHeaders(200, 0); // chunked
 323             t.getResponseBody().close();  // write nothing
 324         }
 325     }
 326 
 327     static class HTTP2_FixedLengthNoBodyHandler implements Http2Handler {
 328         @Override
 329         public void handle(Http2TestExchange t) throws IOException {
 330             //out.println("NoBodyHandler received request to " + t.getRequestURI());
 331             try (InputStream is = t.getRequestBody()) {
 332                 is.readAllBytes();
 333             }
 334             t.sendResponseHeaders(200, 0);
 335         }
 336     }
 337 
 338     static class HTTP2_ChunkedNoBodyHandler implements Http2Handler {
 339         @Override
 340         public void handle(Http2TestExchange t) throws IOException {
 341             //out.println("NoBodyHandler received request to " + t.getRequestURI());
 342             try (InputStream is = t.getRequestBody()) {
 343                 is.readAllBytes();
 344             }
 345             t.sendResponseHeaders(200, -1);
 346             t.getResponseBody().close();  // write nothing
 347         }
 348     }
 349 }