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 @bug 8087112 26 * @modules jdk.incubator.httpclient 27 * java.logging 28 * jdk.httpserver 29 * @library /lib/testlibrary/ /test/lib 30 * @compile ../../../com/sun/net/httpserver/LogFilter.java 31 * @compile ../../../com/sun/net/httpserver/EchoHandler.java 32 * @compile ../../../com/sun/net/httpserver/FileServerHandler.java 33 * @build jdk.test.lib.Platform 34 * @build jdk.test.lib.util.FileUtils 35 * @build LightWeightHttpServer 36 * @build jdk.testlibrary.SimpleSSLContext 37 * @run testng/othervm RequestBodyTest 38 */ 39 40import java.io.*; 41import java.net.URI; 42import jdk.incubator.http.HttpClient; 43import jdk.incubator.http.HttpRequest; 44import jdk.incubator.http.HttpResponse; 45import java.nio.charset.Charset; 46import java.nio.charset.StandardCharsets; 47import java.nio.file.Files; 48import java.nio.file.Path; 49import java.nio.file.Paths; 50import java.util.ArrayList; 51import java.util.Arrays; 52import java.util.List; 53import java.util.Optional; 54import java.util.concurrent.ExecutorService; 55import java.util.concurrent.Executors; 56import java.util.function.Supplier; 57import javax.net.ssl.SSLContext; 58import jdk.test.lib.util.FileUtils; 59import static java.nio.charset.StandardCharsets.*; 60import static java.nio.file.StandardOpenOption.*; 61import static jdk.incubator.http.HttpRequest.BodyProcessor.*; 62import static jdk.incubator.http.HttpResponse.BodyHandler.*; 63 64import org.testng.annotations.AfterTest; 65import org.testng.annotations.BeforeTest; 66import org.testng.annotations.DataProvider; 67import org.testng.annotations.Test; 68import static org.testng.Assert.*; 69 70public class RequestBodyTest { 71 72 static final String fileroot = System.getProperty("test.src") + "/docs"; 73 static final String midSizedFilename = "/files/notsobigfile.txt"; 74 static final String smallFilename = "/files/smallfile.txt"; 75 76 HttpClient client; 77 ExecutorService exec = Executors.newCachedThreadPool(); 78 String httpURI; 79 String httpsURI; 80 81 enum RequestBody { 82 BYTE_ARRAY, 83 BYTE_ARRAY_OFFSET, 84 BYTE_ARRAYS, 85 FILE, 86 INPUTSTREAM, 87 STRING, 88 STRING_WITH_CHARSET 89 } 90 91 enum ResponseBody { 92 BYTE_ARRAY, 93 BYTE_ARRAY_CONSUMER, 94 DISCARD, 95 FILE, 96 FILE_WITH_OPTION, 97 STRING, 98 STRING_WITH_CHARSET, 99 } 100 101 @BeforeTest 102 public void setup() throws Exception { 103 LightWeightHttpServer.initServer(); 104 httpURI = LightWeightHttpServer.httproot + "echo/foo"; 105 httpsURI = LightWeightHttpServer.httpsroot + "echo/foo"; 106 107 SSLContext ctx = LightWeightHttpServer.ctx; 108 client = HttpClient.newBuilder() 109 .sslContext(ctx) 110 .version(HttpClient.Version.HTTP_1_1) 111 .followRedirects(HttpClient.Redirect.ALWAYS) 112 .executor(exec) 113 .build(); 114 } 115 116 @AfterTest 117 public void teardown() throws Exception { 118 exec.shutdownNow(); 119 LightWeightHttpServer.stop(); 120 } 121 122 @DataProvider 123 public Object[][] exchanges() throws Exception { 124 List<Object[]> values = new ArrayList<>(); 125 126 for (boolean async : new boolean[] { false, true }) 127 for (String uri : new String[] { httpURI, httpsURI }) 128 for (String file : new String[] { smallFilename, midSizedFilename }) 129 for (RequestBody requestBodyType : RequestBody.values()) 130 for (ResponseBody responseBodyType : ResponseBody.values()) 131 values.add(new Object[] 132 {uri, requestBodyType, responseBodyType, file, async}); 133 134 return values.stream().toArray(Object[][]::new); 135 } 136 137 @Test(dataProvider = "exchanges") 138 void exchange(String target, 139 RequestBody requestBodyType, 140 ResponseBody responseBodyType, 141 String file, 142 boolean async) 143 throws Exception 144 { 145 Path filePath = Paths.get(fileroot + file); 146 URI uri = new URI(target); 147 148 HttpRequest request = createRequest(uri, requestBodyType, filePath); 149 150 checkResponse(client, request, requestBodyType, responseBodyType, filePath, async); 151 } 152 153 static final int DEFAULT_OFFSET = 10; 154 static final int DEFAULT_LENGTH = 1000; 155 156 HttpRequest createRequest(URI uri, 157 RequestBody requestBodyType, 158 Path file) 159 throws IOException 160 { 161 HttpRequest.Builder rb = HttpRequest.newBuilder(uri); 162 163 String filename = file.toFile().getAbsolutePath(); 164 byte[] fileAsBytes = getFileBytes(filename); 165 String fileAsString = new String(fileAsBytes, UTF_8); 166 167 switch (requestBodyType) { 168 case BYTE_ARRAY: 169 rb.POST(fromByteArray(fileAsBytes)); 170 break; 171 case BYTE_ARRAY_OFFSET: 172 rb.POST(fromByteArray(fileAsBytes, DEFAULT_OFFSET, DEFAULT_LENGTH)); 173 break; 174 case BYTE_ARRAYS: 175 Iterable<byte[]> iterable = Arrays.asList(fileAsBytes); 176 rb.POST(fromByteArrays(iterable)); 177 break; 178 case FILE: 179 rb.POST(fromFile(file)); 180 break; 181 case INPUTSTREAM: 182 rb.POST(fromInputStream(fileInputStreamSupplier(file))); 183 break; 184 case STRING: 185 rb.POST(fromString(fileAsString)); 186 break; 187 case STRING_WITH_CHARSET: 188 rb.POST(fromString(new String(fileAsBytes), Charset.defaultCharset())); 189 break; 190 default: 191 throw new AssertionError("Unknown request body:" + requestBodyType); 192 } 193 return rb.build(); 194 } 195 196 void checkResponse(HttpClient client, 197 HttpRequest request, 198 RequestBody requestBodyType, 199 ResponseBody responseBodyType, 200 Path file, 201 boolean async) 202 throws InterruptedException, IOException 203 { 204 String filename = file.toFile().getAbsolutePath(); 205 byte[] fileAsBytes = getFileBytes(filename); 206 if (requestBodyType == RequestBody.BYTE_ARRAY_OFFSET) { 207 // Truncate the expected response body, if only a portion was sent 208 fileAsBytes = Arrays.copyOfRange(fileAsBytes, 209 DEFAULT_OFFSET, 210 DEFAULT_OFFSET + DEFAULT_LENGTH); 211 } 212 String fileAsString = new String(fileAsBytes, UTF_8); 213 Path tempFile = Paths.get("RequestBodyTest.tmp"); 214 FileUtils.deleteFileIfExistsWithRetry(tempFile); 215 216 switch (responseBodyType) { 217 case BYTE_ARRAY: 218 HttpResponse<byte[]> bar = getResponse(client, request, asByteArray(), async); 219 assertEquals(bar.statusCode(), 200); 220 assertEquals(bar.body(), fileAsBytes); 221 break; 222 case BYTE_ARRAY_CONSUMER: 223 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 224 HttpResponse<Void> v = getResponse(client, request, 225 asByteArrayConsumer(o -> consumerBytes(o, baos) ), async); 226 byte[] ba = baos.toByteArray(); 227 assertEquals(v.statusCode(), 200); 228 assertEquals(ba, fileAsBytes); 229 break; 230 case DISCARD: 231 Object o = new Object(); 232 HttpResponse<Object> or = getResponse(client, request, discard(o), async); 233 assertEquals(or.statusCode(), 200); 234 assertSame(or.body(), o); 235 break; 236 case FILE: 237 HttpResponse<Path> fr = getResponse(client, request, asFile(tempFile), async); 238 assertEquals(fr.statusCode(), 200); 239 assertEquals(Files.size(tempFile), fileAsString.length()); 240 assertEquals(Files.readAllBytes(tempFile), fileAsBytes); 241 break; 242 case FILE_WITH_OPTION: 243 fr = getResponse(client, request, asFile(tempFile, CREATE_NEW, WRITE), async); 244 assertEquals(fr.statusCode(), 200); 245 assertEquals(Files.size(tempFile), fileAsString.length()); 246 assertEquals(Files.readAllBytes(tempFile), fileAsBytes); 247 break; 248 case STRING: 249 HttpResponse<String> sr = getResponse(client, request, asString(), async); 250 assertEquals(sr.statusCode(), 200); 251 assertEquals(sr.body(), fileAsString); 252 break; 253 case STRING_WITH_CHARSET: 254 HttpResponse<String> r = getResponse(client, request, asString(StandardCharsets.UTF_8), async); 255 assertEquals(r.statusCode(), 200); 256 assertEquals(r.body(), fileAsString); 257 break; 258 default: 259 throw new AssertionError("Unknown response body:" + responseBodyType); 260 } 261 } 262 263 static <T> HttpResponse<T> getResponse(HttpClient client, 264 HttpRequest request, 265 HttpResponse.BodyHandler<T> handler, 266 boolean async) 267 throws InterruptedException, IOException 268 { 269 if (!async) 270 return client.send(request, handler); 271 else 272 return client.sendAsync(request, handler).join(); 273 } 274 275 static byte[] getFileBytes(String path) throws IOException { 276 try (FileInputStream fis = new FileInputStream(path); 277 BufferedInputStream bis = new BufferedInputStream(fis); 278 ByteArrayOutputStream baos = new ByteArrayOutputStream()) { 279 bis.transferTo(baos); 280 return baos.toByteArray(); 281 } 282 } 283 284 static Supplier<FileInputStream> fileInputStreamSupplier(Path f) { 285 return new Supplier<>() { 286 Path file = f; 287 @Override 288 public FileInputStream get() { 289 try { 290 return new FileInputStream(file.toFile()); 291 } catch (FileNotFoundException x) { 292 throw new UncheckedIOException(x); 293 } 294 } 295 }; 296 } 297 298 static void consumerBytes(Optional<byte[]> bytes, ByteArrayOutputStream baos) { 299 try { 300 if (bytes.isPresent()) 301 baos.write(bytes.get()); 302 } catch (IOException x) { 303 throw new UncheckedIOException(x); 304 } 305 } 306} 307