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