1 /* 2 * Copyright (c) 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 * @summary Tests response body subscribers's onComplete is not invoked before onSubscribe 27 * @library /lib/testlibrary http2/server 28 * @build jdk.testlibrary.SimpleSSLContext 29 * @modules java.base/sun.net.www.http 30 * jdk.incubator.httpclient/jdk.incubator.http.internal.common 31 * jdk.incubator.httpclient/jdk.incubator.http.internal.frame 32 * jdk.incubator.httpclient/jdk.incubator.http.internal.hpack 33 * @run testng/othervm CustomResponseSubscriber 34 */ 35 36 import java.io.IOException; 37 import java.io.InputStream; 38 import java.net.InetSocketAddress; 39 import java.net.URI; 40 import java.nio.ByteBuffer; 41 import java.util.List; 42 import java.util.concurrent.CompletionStage; 43 import java.util.concurrent.Executor; 44 import java.util.concurrent.Executors; 45 import java.util.concurrent.Flow; 46 import com.sun.net.httpserver.HttpExchange; 47 import com.sun.net.httpserver.HttpHandler; 48 import com.sun.net.httpserver.HttpServer; 49 import com.sun.net.httpserver.HttpsConfigurator; 50 import com.sun.net.httpserver.HttpsServer; 51 import jdk.incubator.http.HttpClient; 52 import jdk.incubator.http.HttpHeaders; 53 import jdk.incubator.http.HttpRequest; 54 import jdk.incubator.http.HttpResponse; 55 import jdk.incubator.http.HttpResponse.BodyHandler; 56 import jdk.incubator.http.HttpResponse.BodySubscriber; 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.HttpResponse.BodySubscriber.asString; 66 import static org.testng.Assert.assertEquals; 67 import static org.testng.Assert.assertTrue; 68 69 public class CustomResponseSubscriber { 70 71 SSLContext sslContext; 72 HttpServer httpTestServer; // HTTP/1.1 [ 4 servers ] 73 HttpsServer httpsTestServer; // HTTPS/1.1 74 Http2TestServer http2TestServer; // HTTP/2 ( h2c ) 75 Http2TestServer https2TestServer; // HTTP/2 ( h2 ) 76 String httpURI_fixed; 77 String httpURI_chunk; 78 String httpsURI_fixed; 79 String httpsURI_chunk; 80 String http2URI_fixed; 81 String http2URI_chunk; 82 String https2URI_fixed; 83 String https2URI_chunk; 84 85 static final int ITERATION_COUNT = 10; 86 // a shared executor helps reduce the amount of threads created by the test 87 static final Executor executor = Executors.newCachedThreadPool(); 88 89 @DataProvider(name = "variants") 90 public Object[][] variants() { 91 return new Object[][]{ 92 { httpURI_fixed, false }, 93 { httpURI_chunk, false }, 94 { httpsURI_fixed, false }, 95 { httpsURI_chunk, false }, 96 { http2URI_fixed, false }, 97 { http2URI_chunk, false }, 98 { https2URI_fixed, false,}, 99 { https2URI_chunk, false }, 100 101 { httpURI_fixed, true }, 102 { httpURI_chunk, true }, 103 { httpsURI_fixed, true }, 104 { httpsURI_chunk, true }, 105 { http2URI_fixed, true }, 106 { http2URI_chunk, true }, 107 { https2URI_fixed, true,}, 108 { https2URI_chunk, true }, 109 }; 110 } 111 112 HttpClient newHttpClient() { 113 return HttpClient.newBuilder() 114 .executor(executor) 115 .sslContext(sslContext) 116 .build(); 117 } 118 119 @Test(dataProvider = "variants") 120 public void testAsString(String uri, boolean sameClient) throws Exception { 121 HttpClient client = null; 122 for (int i=0; i< ITERATION_COUNT; i++) { 123 if (!sameClient || client == null) 124 client = newHttpClient(); 125 126 HttpRequest req = HttpRequest.newBuilder(URI.create(uri)) 127 .build(); 128 BodyHandler<String> handler = new CRSBodyHandler(); 129 HttpResponse<String> response = client.send(req, handler); 130 String body = response.body(); 131 assertEquals(body, ""); 132 } 133 } 134 135 static class CRSBodyHandler implements BodyHandler<String> { 136 @Override 137 public BodySubscriber<String> apply(int statusCode, HttpHeaders responseHeaders) { 138 assertEquals(statusCode, 200); 139 return new CRSBodySubscriber(); 140 } 141 } 142 143 static class CRSBodySubscriber implements BodySubscriber<String> { 144 private final BodySubscriber<String> asString = asString(UTF_8); 145 volatile boolean onSubscribeCalled; 146 147 @Override 148 public void onSubscribe(Flow.Subscription subscription) { 149 //out.println("onSubscribe "); 150 onSubscribeCalled = true; 151 asString.onSubscribe(subscription); 152 } 153 154 @Override 155 public void onNext(List<ByteBuffer> item) { 156 // out.println("onNext " + item); 157 assertTrue(onSubscribeCalled); 158 asString.onNext(item); 159 } 160 161 @Override 162 public void onError(Throwable throwable) { 163 //out.println("onError"); 164 assertTrue(onSubscribeCalled); 165 asString.onError(throwable); 166 } 167 168 @Override 169 public void onComplete() { 170 //out.println("onComplete"); 171 assertTrue(onSubscribeCalled, "onComplete called before onSubscribe"); 172 asString.onComplete(); 173 } 174 175 @Override 176 public CompletionStage<String> getBody() { 177 return asString.getBody(); 178 } 179 } 180 181 182 @BeforeTest 183 public void setup() throws Exception { 184 sslContext = new SimpleSSLContext().get(); 185 if (sslContext == null) 186 throw new AssertionError("Unexpected null sslContext"); 187 188 // HTTP/1.1 189 HttpHandler h1_fixedLengthHandler = new HTTP1_FixedLengthHandler(); 190 HttpHandler h1_chunkHandler = new HTTP1_ChunkedHandler(); 191 InetSocketAddress sa = new InetSocketAddress("localhost", 0); 192 httpTestServer = HttpServer.create(sa, 0); 193 httpTestServer.createContext("/http1/fixed", h1_fixedLengthHandler); 194 httpTestServer.createContext("/http1/chunk", h1_chunkHandler); 195 httpURI_fixed = "http://127.0.0.1:" + httpTestServer.getAddress().getPort() + "/http1/fixed"; 196 httpURI_chunk = "http://127.0.0.1:" + httpTestServer.getAddress().getPort() + "/http1/chunk"; 197 198 httpsTestServer = HttpsServer.create(sa, 0); 199 httpsTestServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); 200 httpsTestServer.createContext("/https1/fixed", h1_fixedLengthHandler); 201 httpsTestServer.createContext("/https1/chunk", h1_chunkHandler); 202 httpsURI_fixed = "https://127.0.0.1:" + httpsTestServer.getAddress().getPort() + "/https1/fixed"; 203 httpsURI_chunk = "https://127.0.0.1:" + httpsTestServer.getAddress().getPort() + "/https1/chunk"; 204 205 // HTTP/2 206 Http2Handler h2_fixedLengthHandler = new HTTP2_FixedLengthHandler(); 207 Http2Handler h2_chunkedHandler = new HTTP2_VariableHandler(); 208 209 http2TestServer = new Http2TestServer("127.0.0.1", false, 0); 210 http2TestServer.addHandler(h2_fixedLengthHandler, "/http2/fixed"); 211 http2TestServer.addHandler(h2_chunkedHandler, "/http2/chunk"); 212 int port = http2TestServer.getAddress().getPort(); 213 http2URI_fixed = "http://127.0.0.1:" + port + "/http2/fixed"; 214 http2URI_chunk = "http://127.0.0.1:" + port + "/http2/chunk"; 215 216 https2TestServer = new Http2TestServer("127.0.0.1", true, 0); 217 https2TestServer.addHandler(h2_fixedLengthHandler, "/https2/fixed"); 218 https2TestServer.addHandler(h2_chunkedHandler, "/https2/chunk"); 219 port = https2TestServer.getAddress().getPort(); 220 https2URI_fixed = "https://127.0.0.1:" + port + "/https2/fixed"; 221 https2URI_chunk = "https://127.0.0.1:" + port + "/https2/chunk"; 222 223 httpTestServer.start(); 224 httpsTestServer.start(); 225 http2TestServer.start(); 226 https2TestServer.start(); 227 } 228 229 @AfterTest 230 public void teardown() throws Exception { 231 httpTestServer.stop(0); 232 httpsTestServer.stop(0); 233 http2TestServer.stop(); 234 https2TestServer.stop(); 235 } 236 237 static class HTTP1_FixedLengthHandler implements HttpHandler { 238 @Override 239 public void handle(HttpExchange t) throws IOException { 240 out.println("HTTP1_FixedLengthHandler received request to " + t.getRequestURI()); 241 try (InputStream is = t.getRequestBody()) { 242 is.readAllBytes(); 243 } 244 t.sendResponseHeaders(200, -1); //no body 245 } 246 } 247 248 static class HTTP1_ChunkedHandler implements HttpHandler { 249 @Override 250 public void handle(HttpExchange t) throws IOException { 251 out.println("HTTP1_ChunkedHandler received request to " + t.getRequestURI()); 252 try (InputStream is = t.getRequestBody()) { 253 is.readAllBytes(); 254 } 255 t.sendResponseHeaders(200, 0); // chunked 256 t.getResponseBody().close(); // no body 257 } 258 } 259 260 static class HTTP2_FixedLengthHandler implements Http2Handler { 261 @Override 262 public void handle(Http2TestExchange t) throws IOException { 263 out.println("HTTP2_FixedLengthHandler received request to " + t.getRequestURI()); 264 try (InputStream is = t.getRequestBody()) { 265 is.readAllBytes(); 266 } 267 t.sendResponseHeaders(200, 0); 268 t.getResponseBody().close(); 269 } 270 } 271 272 static class HTTP2_VariableHandler implements Http2Handler { 273 @Override 274 public void handle(Http2TestExchange t) throws IOException { 275 out.println("HTTP2_VariableHandler received request to " + t.getRequestURI()); 276 try (InputStream is = t.getRequestBody()) { 277 is.readAllBytes(); 278 } 279 t.sendResponseHeaders(200, -1); // variable 280 t.getResponseBody().close(); //no body 281 } 282 } 283 }