1 /* 2 * Copyright (c) 2015, 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 * @bug 8087112 27 * @modules java.net.http 28 * java.logging 29 * jdk.httpserver 30 * @library /test/lib 31 * @compile ../../../com/sun/net/httpserver/LogFilter.java 32 * @compile ../../../com/sun/net/httpserver/EchoHandler.java 33 * @compile ../../../com/sun/net/httpserver/FileServerHandler.java 34 * @build jdk.test.lib.net.SimpleSSLContext 35 * @build LightWeightHttpServer 36 * @build jdk.test.lib.Platform 37 * @build jdk.test.lib.util.FileUtils 38 * @run testng/othervm RequestBodyTest 39 * @run testng/othervm/java.security.policy=RequestBodyTest.policy RequestBodyTest 40 */ 41 42 import java.io.*; 43 import java.net.URI; 44 import java.net.http.HttpClient; 45 import java.net.http.HttpRequest; 46 import java.net.http.HttpResponse; 47 import java.net.http.HttpRequest.BodyPublishers; 48 import java.net.http.HttpResponse.BodyHandler; 49 import java.net.http.HttpResponse.BodyHandlers; 50 import java.nio.charset.Charset; 51 import java.nio.charset.StandardCharsets; 52 import java.nio.file.Files; 53 import java.nio.file.Path; 54 import java.nio.file.Paths; 55 import java.security.AccessController; 56 import java.security.PrivilegedActionException; 57 import java.security.PrivilegedExceptionAction; 58 import java.util.ArrayList; 59 import java.util.Arrays; 60 import java.util.List; 61 import java.util.Optional; 62 import java.util.concurrent.ConcurrentHashMap; 63 import java.util.function.Consumer; 64 import java.util.function.Supplier; 65 import javax.net.ssl.SSLContext; 66 import jdk.test.lib.util.FileUtils; 67 import static java.lang.System.out; 68 import static java.nio.charset.StandardCharsets.*; 69 import static java.nio.file.StandardOpenOption.*; 70 71 import org.testng.annotations.AfterTest; 72 import org.testng.annotations.BeforeTest; 73 import org.testng.annotations.DataProvider; 74 import org.testng.annotations.Test; 75 import static org.testng.Assert.*; 76 77 public class RequestBodyTest { 78 79 static final String fileroot = System.getProperty("test.src", ".") + "/docs"; 80 static final String midSizedFilename = "/files/notsobigfile.txt"; 81 static final String smallFilename = "/files/smallfile.txt"; 82 final ConcurrentHashMap<String,Throwable> failures = new ConcurrentHashMap<>(); 83 84 HttpClient client; 85 String httpURI; 86 String httpsURI; 87 88 enum RequestBody { 89 BYTE_ARRAY, 90 BYTE_ARRAY_OFFSET, 91 BYTE_ARRAYS, 92 FILE, 93 INPUTSTREAM, 94 STRING, 95 STRING_WITH_CHARSET 96 } 97 98 enum ResponseBody { 99 BYTE_ARRAY, 100 BYTE_ARRAY_CONSUMER, 101 DISCARD, 102 FILE, 103 FILE_WITH_OPTION, 104 STRING, 105 STRING_WITH_CHARSET, 106 } 107 108 @BeforeTest setup()109 public void setup() throws Exception { 110 LightWeightHttpServer.initServer(); 111 httpURI = LightWeightHttpServer.httproot + "echo/foo"; 112 httpsURI = LightWeightHttpServer.httpsroot + "echo/foo"; 113 114 SSLContext ctx = LightWeightHttpServer.ctx; 115 client = HttpClient.newBuilder() 116 .sslContext(ctx) 117 .version(HttpClient.Version.HTTP_1_1) 118 .followRedirects(HttpClient.Redirect.ALWAYS) 119 .build(); 120 } 121 122 @AfterTest teardown()123 public void teardown() throws Exception { 124 try { 125 LightWeightHttpServer.stop(); 126 } finally { 127 System.out.println("RequestBodyTest: " + failures.size() + " failures"); 128 int i = 0; 129 for (String key: failures.keySet()) { 130 System.out.println("test" + key + " failed: " + failures.get(key)); 131 failures.get(key).printStackTrace(System.out); 132 if (i++ > 3) { 133 System.out.println("..... other failures not printed ...."); 134 break; 135 } 136 } 137 } 138 } 139 140 @DataProvider exchanges()141 public Object[][] exchanges() throws Exception { 142 List<Object[]> values = new ArrayList<>(); 143 144 for (boolean async : new boolean[] { false, true }) 145 for (String uri : new String[] { httpURI, httpsURI }) 146 for (String file : new String[] { smallFilename, midSizedFilename }) 147 for (RequestBody requestBodyType : RequestBody.values()) 148 for (ResponseBody responseBodyType : ResponseBody.values()) 149 for (boolean bufferResponseBody : new boolean[] { false, true }) 150 values.add(new Object[] 151 {uri, requestBodyType, responseBodyType, file, async, bufferResponseBody}); 152 153 return values.stream().toArray(Object[][]::new); 154 } 155 156 @Test(dataProvider = "exchanges") exchange(String target, RequestBody requestBodyType, ResponseBody responseBodyType, String file, boolean async, boolean bufferResponseBody)157 void exchange(String target, 158 RequestBody requestBodyType, 159 ResponseBody responseBodyType, 160 String file, 161 boolean async, 162 boolean bufferResponseBody) 163 throws Exception 164 { 165 try { 166 Path filePath = Paths.get(fileroot + file); 167 URI uri = new URI(target); 168 169 HttpRequest request = createRequest(uri, requestBodyType, filePath); 170 171 checkResponse(client, request, requestBodyType, responseBodyType, filePath, async, bufferResponseBody); 172 } catch (Exception | Error x) { 173 Object[] params = new Object[] { 174 target, requestBodyType, responseBodyType, 175 file, "async=" + async, "buffer=" + bufferResponseBody 176 }; 177 failures.put(java.util.Arrays.toString(params), x); 178 throw x; 179 } 180 } 181 182 static final int DEFAULT_OFFSET = 10; 183 static final int DEFAULT_LENGTH_FACTOR = 4/5; 184 createRequest(URI uri, RequestBody requestBodyType, Path file)185 HttpRequest createRequest(URI uri, 186 RequestBody requestBodyType, 187 Path file) 188 throws IOException 189 { 190 HttpRequest.Builder rb = HttpRequest.newBuilder(uri); 191 192 String filename = file.toFile().getAbsolutePath(); 193 byte[] fileAsBytes = getFileBytes(filename); 194 String fileAsString = new String(fileAsBytes, UTF_8); 195 196 switch (requestBodyType) { 197 case BYTE_ARRAY: 198 rb.POST(BodyPublishers.ofByteArray(fileAsBytes)); 199 break; 200 case BYTE_ARRAY_OFFSET: 201 rb.POST(BodyPublishers.ofByteArray(fileAsBytes, 202 DEFAULT_OFFSET, 203 fileAsBytes.length * DEFAULT_LENGTH_FACTOR)); 204 break; 205 case BYTE_ARRAYS: 206 Iterable<byte[]> iterable = Arrays.asList(fileAsBytes); 207 rb.POST(BodyPublishers.ofByteArrays(iterable)); 208 break; 209 case FILE: 210 rb.POST(BodyPublishers.ofFile(file)); 211 break; 212 case INPUTSTREAM: 213 rb.POST(BodyPublishers.ofInputStream(fileInputStreamSupplier(file))); 214 break; 215 case STRING: 216 rb.POST(BodyPublishers.ofString(fileAsString)); 217 break; 218 case STRING_WITH_CHARSET: 219 rb.POST(BodyPublishers.ofString(new String(fileAsBytes), Charset.defaultCharset())); 220 break; 221 default: 222 throw new AssertionError("Unknown request body:" + requestBodyType); 223 } 224 return rb.build(); 225 } 226 checkResponse(HttpClient client, HttpRequest request, RequestBody requestBodyType, ResponseBody responseBodyType, Path file, boolean async, boolean bufferResponseBody)227 void checkResponse(HttpClient client, 228 HttpRequest request, 229 RequestBody requestBodyType, 230 ResponseBody responseBodyType, 231 Path file, 232 boolean async, 233 boolean bufferResponseBody) 234 throws InterruptedException, IOException 235 { 236 String filename = file.toFile().getAbsolutePath(); 237 byte[] fileAsBytes = getFileBytes(filename); 238 if (requestBodyType == RequestBody.BYTE_ARRAY_OFFSET) { 239 // Truncate the expected response body, if only a portion was sent 240 int length = DEFAULT_OFFSET + (fileAsBytes.length * DEFAULT_LENGTH_FACTOR); 241 fileAsBytes = Arrays.copyOfRange(fileAsBytes, DEFAULT_OFFSET, length); 242 } 243 String fileAsString = new String(fileAsBytes, UTF_8); 244 Path tempFile = Paths.get("RequestBodyTest.tmp"); 245 FileUtils.deleteFileIfExistsWithRetry(tempFile); 246 247 switch (responseBodyType) { 248 case BYTE_ARRAY: 249 BodyHandler<byte[]> bh = BodyHandlers.ofByteArray(); 250 if (bufferResponseBody) bh = BodyHandlers.buffering(bh, 50); 251 HttpResponse<byte[]> bar = getResponse(client, request, bh, async); 252 assertEquals(bar.statusCode(), 200); 253 assertEquals(bar.body(), fileAsBytes); 254 break; 255 case BYTE_ARRAY_CONSUMER: 256 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 257 Consumer<Optional<byte[]>> consumer = o -> consumerBytes(o, baos); 258 BodyHandler<Void> bh1 = BodyHandlers.ofByteArrayConsumer(consumer); 259 if (bufferResponseBody) bh1 = BodyHandlers.buffering(bh1, 49); 260 HttpResponse<Void> v = getResponse(client, request, bh1, async); 261 byte[] ba = baos.toByteArray(); 262 assertEquals(v.statusCode(), 200); 263 assertEquals(ba, fileAsBytes); 264 break; 265 case DISCARD: 266 Object o = new Object(); 267 BodyHandler<Object> bh2 = BodyHandlers.replacing(o); 268 if (bufferResponseBody) bh2 = BodyHandlers.buffering(bh2, 51); 269 HttpResponse<Object> or = getResponse(client, request, bh2, async); 270 assertEquals(or.statusCode(), 200); 271 assertSame(or.body(), o); 272 break; 273 case FILE: 274 BodyHandler<Path> bh3 = BodyHandlers.ofFile(tempFile); 275 if (bufferResponseBody) bh3 = BodyHandlers.buffering(bh3, 48); 276 HttpResponse<Path> fr = getResponse(client, request, bh3, async); 277 assertEquals(fr.statusCode(), 200); 278 assertEquals(Files.size(tempFile), fileAsString.length()); 279 assertEquals(Files.readAllBytes(tempFile), fileAsBytes); 280 break; 281 case FILE_WITH_OPTION: 282 BodyHandler<Path> bh4 = BodyHandlers.ofFile(tempFile, CREATE_NEW, WRITE); 283 if (bufferResponseBody) bh4 = BodyHandlers.buffering(bh4, 52); 284 fr = getResponse(client, request, bh4, async); 285 assertEquals(fr.statusCode(), 200); 286 assertEquals(Files.size(tempFile), fileAsString.length()); 287 assertEquals(Files.readAllBytes(tempFile), fileAsBytes); 288 break; 289 case STRING: 290 BodyHandler<String> bh5 = BodyHandlers.ofString(); 291 if(bufferResponseBody) bh5 = BodyHandlers.buffering(bh5, 47); 292 HttpResponse<String> sr = getResponse(client, request, bh5, async); 293 assertEquals(sr.statusCode(), 200); 294 assertEquals(sr.body(), fileAsString); 295 break; 296 case STRING_WITH_CHARSET: 297 BodyHandler<String> bh6 = BodyHandlers.ofString(StandardCharsets.UTF_8); 298 if (bufferResponseBody) bh6 = BodyHandlers.buffering(bh6, 53); 299 HttpResponse<String> r = getResponse(client, request, bh6, async); 300 assertEquals(r.statusCode(), 200); 301 assertEquals(r.body(), fileAsString); 302 break; 303 default: 304 throw new AssertionError("Unknown response body:" + responseBodyType); 305 } 306 } 307 getResponse(HttpClient client, HttpRequest request, HttpResponse.BodyHandler<T> handler, boolean async)308 static <T> HttpResponse<T> getResponse(HttpClient client, 309 HttpRequest request, 310 HttpResponse.BodyHandler<T> handler, 311 boolean async) 312 throws InterruptedException, IOException 313 { 314 if (!async) 315 return client.send(request, handler); 316 else 317 return client.sendAsync(request, handler).join(); 318 } 319 getFileBytes(String path)320 static byte[] getFileBytes(String path) throws IOException { 321 try (FileInputStream fis = new FileInputStream(path); 322 BufferedInputStream bis = new BufferedInputStream(fis); 323 ByteArrayOutputStream baos = new ByteArrayOutputStream()) { 324 bis.transferTo(baos); 325 return baos.toByteArray(); 326 } 327 } 328 fileInputStreamSupplier(Path f)329 static Supplier<FileInputStream> fileInputStreamSupplier(Path f) { 330 return new Supplier<>() { 331 Path file = f; 332 @Override 333 public FileInputStream get() { 334 try { 335 PrivilegedExceptionAction<FileInputStream> pa = 336 () -> new FileInputStream(file.toFile()); 337 return AccessController.doPrivileged(pa); 338 } catch (PrivilegedActionException x) { 339 throw new UncheckedIOException((IOException)x.getCause()); 340 } 341 } 342 }; 343 } 344 consumerBytes(Optional<byte[]> bytes, ByteArrayOutputStream baos)345 static void consumerBytes(Optional<byte[]> bytes, ByteArrayOutputStream baos) { 346 try { 347 if (bytes.isPresent()) 348 baos.write(bytes.get()); 349 } catch (IOException x) { 350 throw new UncheckedIOException(x); 351 } 352 } 353 354 // --- 355 356 /* Main entry point for standalone testing of the main functional test. */ main(String... args)357 public static void main(String... args) throws Exception { 358 RequestBodyTest t = new RequestBodyTest(); 359 t.setup(); 360 int count = 0; 361 try { 362 for (Object[] objs : t.exchanges()) { 363 count++; 364 out.printf("********* iteration: %d %s %s %s %s %s %s *********%n", 365 count, objs[0], objs[1], objs[2], objs[3], objs[4], objs[5]); 366 t.exchange((String) objs[0], 367 (RequestBody) objs[1], 368 (ResponseBody) objs[2], 369 (String) objs[3], 370 (boolean) objs[4], 371 (boolean) objs[5]); 372 } 373 } finally { 374 t.teardown(); 375 } 376 } 377 } 378