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 /lib/testlibrary/ /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.testlibrary.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