1 /*
2  * Copyright (c) 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  * @summary Tests response body subscribers's onNext's Lists are unmodifiable,
27  *          and that the buffers are read-only
28  * @library /test/lib http2/server
29  * @build jdk.test.lib.net.SimpleSSLContext
30  * @modules java.base/sun.net.www.http
31  *          java.net.http/jdk.internal.net.http.common
32  *          java.net.http/jdk.internal.net.http.frame
33  *          java.net.http/jdk.internal.net.http.hpack
34  * @run testng/othervm ImmutableFlowItems
35  */
36 
37 import java.io.IOException;
38 import java.io.InputStream;
39 import java.io.OutputStream;
40 import java.net.InetAddress;
41 import java.net.InetSocketAddress;
42 import java.net.URI;
43 import java.nio.ByteBuffer;
44 import java.util.List;
45 import java.util.concurrent.CompletionStage;
46 import java.util.concurrent.Flow;
47 import com.sun.net.httpserver.HttpExchange;
48 import com.sun.net.httpserver.HttpHandler;
49 import com.sun.net.httpserver.HttpServer;
50 import com.sun.net.httpserver.HttpsConfigurator;
51 import com.sun.net.httpserver.HttpsServer;
52 import java.net.http.HttpClient;
53 import java.net.http.HttpHeaders;
54 import java.net.http.HttpRequest;
55 import java.net.http.HttpResponse;
56 import java.net.http.HttpResponse.BodyHandler;
57 import java.net.http.HttpResponse.BodySubscriber;
58 import java.net.http.HttpResponse.BodySubscribers;
59 import javax.net.ssl.SSLContext;
60 import jdk.test.lib.net.SimpleSSLContext;
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 java.lang.System.out;
66 import static java.nio.charset.StandardCharsets.UTF_8;
67 import static org.testng.Assert.*;
68 
69 public class ImmutableFlowItems {
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     @DataProvider(name = "variants")
variants()86     public Object[][] variants() {
87         return new Object[][]{
88                 { httpURI_fixed   },
89                 { httpURI_chunk   },
90                 { httpsURI_fixed  },
91                 { httpsURI_chunk  },
92                 { http2URI_fixed  },
93                 { http2URI_chunk  },
94                 { https2URI_fixed },
95                 { https2URI_chunk },
96         };
97     }
98 
99     static final String BODY = "You'll never plough a field by turning it over in your mind.";
100 
newHttpClient()101     HttpClient newHttpClient() {
102         return HttpClient.newBuilder()
103                 .sslContext(sslContext)
104                 .build();
105     }
106 
107     @Test(dataProvider = "variants")
testAsString(String uri)108     public void testAsString(String uri) throws Exception {
109         HttpClient client = newHttpClient();
110 
111         HttpRequest req = HttpRequest.newBuilder(URI.create(uri))
112                 .build();
113 
114         BodyHandler<String> handler = new CRSBodyHandler();
115         client.sendAsync(req, handler)
116                 .thenApply(HttpResponse::body)
117                 .thenAccept(body -> assertEquals(body, BODY))
118                 .join();
119     }
120 
121     static class CRSBodyHandler implements BodyHandler<String> {
122         @Override
apply(HttpResponse.ResponseInfo rinfo)123         public BodySubscriber<String> apply(HttpResponse.ResponseInfo rinfo) {
124             assertEquals(rinfo.statusCode(), 200);
125             return new CRSBodySubscriber();
126         }
127     }
128 
129     static class CRSBodySubscriber implements BodySubscriber<String> {
130         private final BodySubscriber<String> ofString = BodySubscribers.ofString(UTF_8);
131 
132         @Override
onSubscribe(Flow.Subscription subscription)133         public void onSubscribe(Flow.Subscription subscription) {
134             ofString.onSubscribe(subscription);
135         }
136 
137         @Override
onNext(List<ByteBuffer> item)138         public void onNext(List<ByteBuffer> item) {
139             assertUnmodifiableList(item);
140             long c = item.stream().filter(ByteBuffer::isReadOnly).count();
141             assertEquals(c, item.size(), "Unexpected writable buffer in: " +item);
142             ofString.onNext(item);
143         }
144 
145         @Override
onError(Throwable throwable)146         public void onError(Throwable throwable) {
147             ofString.onError(throwable);
148         }
149 
150         @Override
onComplete()151         public void onComplete() {
152             ofString.onComplete();
153         }
154 
155         @Override
getBody()156         public CompletionStage<String> getBody() {
157             return ofString.getBody();
158         }
159     }
160 
161     static final Class<UnsupportedOperationException> UOE = UnsupportedOperationException.class;
162 
assertUnmodifiableList(List<ByteBuffer> list)163     static void assertUnmodifiableList(List<ByteBuffer> list) {
164         assertNotNull(list);
165         ByteBuffer b = list.get(0);
166         assertNotNull(b);
167         assertThrows(UOE, () -> list.add(ByteBuffer.wrap(new byte[0])));
168         assertThrows(UOE, () -> list.remove(b));
169     }
170 
serverAuthority(HttpServer server)171     static String serverAuthority(HttpServer server) {
172         return InetAddress.getLoopbackAddress().getHostName() + ":"
173                 + server.getAddress().getPort();
174     }
175 
176     @BeforeTest
setup()177     public void setup() throws Exception {
178         sslContext = new SimpleSSLContext().get();
179         if (sslContext == null)
180             throw new AssertionError("Unexpected null sslContext");
181 
182         // HTTP/1.1
183         HttpHandler h1_fixedLengthHandler = new HTTP1_FixedLengthHandler();
184         HttpHandler h1_chunkHandler = new HTTP1_ChunkedHandler();
185         InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
186         httpTestServer = HttpServer.create(sa, 0);
187         httpTestServer.createContext("/http1/fixed", h1_fixedLengthHandler);
188         httpTestServer.createContext("/http1/chunk", h1_chunkHandler);
189         httpURI_fixed = "http://" + serverAuthority(httpTestServer) + "/http1/fixed";
190         httpURI_chunk = "http://" + serverAuthority(httpTestServer) + "/http1/chunk";
191 
192         httpsTestServer = HttpsServer.create(sa, 0);
193         httpsTestServer.setHttpsConfigurator(new HttpsConfigurator(sslContext));
194         httpsTestServer.createContext("/https1/fixed", h1_fixedLengthHandler);
195         httpsTestServer.createContext("/https1/chunk", h1_chunkHandler);
196         httpsURI_fixed = "https://" + serverAuthority(httpsTestServer) + "/https1/fixed";
197         httpsURI_chunk = "https://" + serverAuthority(httpsTestServer) + "/https1/chunk";
198 
199         // HTTP/2
200         Http2Handler h2_fixedLengthHandler = new HTTP2_FixedLengthHandler();
201         Http2Handler h2_chunkedHandler = new HTTP2_VariableHandler();
202 
203         http2TestServer = new Http2TestServer("localhost", false, 0);
204         http2TestServer.addHandler(h2_fixedLengthHandler, "/http2/fixed");
205         http2TestServer.addHandler(h2_chunkedHandler, "/http2/chunk");
206         http2URI_fixed = "http://" + http2TestServer.serverAuthority() + "/http2/fixed";
207         http2URI_chunk = "http://" + http2TestServer.serverAuthority() + "/http2/chunk";
208 
209         https2TestServer = new Http2TestServer("localhost", true, sslContext);
210         https2TestServer.addHandler(h2_fixedLengthHandler, "/https2/fixed");
211         https2TestServer.addHandler(h2_chunkedHandler, "/https2/chunk");
212         https2URI_fixed = "https://" + https2TestServer.serverAuthority() + "/https2/fixed";
213         https2URI_chunk = "https://" + https2TestServer.serverAuthority() + "/https2/chunk";
214 
215         httpTestServer.start();
216         httpsTestServer.start();
217         http2TestServer.start();
218         https2TestServer.start();
219     }
220 
221     @AfterTest
teardown()222     public void teardown() throws Exception {
223         httpTestServer.stop(0);
224         httpsTestServer.stop(0);
225         http2TestServer.stop();
226         https2TestServer.stop();
227     }
228 
229     static class HTTP1_FixedLengthHandler implements HttpHandler {
230         @Override
handle(HttpExchange t)231         public void handle(HttpExchange t) throws IOException {
232             out.println("HTTP1_FixedLengthHandler received request to " + t.getRequestURI());
233             try (InputStream is = t.getRequestBody();
234                  OutputStream os = t.getResponseBody()) {
235                 is.readAllBytes();
236                 byte[] bytes = BODY.getBytes(UTF_8);
237                 t.sendResponseHeaders(200, bytes.length);
238                 os.write(bytes);
239             }
240         }
241     }
242 
243     static class HTTP1_ChunkedHandler implements HttpHandler {
244         @Override
handle(HttpExchange t)245         public void handle(HttpExchange t) throws IOException {
246             out.println("HTTP1_ChunkedHandler received request to " + t.getRequestURI());
247             try (InputStream is = t.getRequestBody();
248                  OutputStream os = t.getResponseBody()) {
249                 is.readAllBytes();
250                 byte[] bytes = BODY.getBytes(UTF_8);
251                 t.sendResponseHeaders(200, 0);
252                 os.write(bytes);
253             }
254         }
255     }
256 
257     static class HTTP2_FixedLengthHandler implements Http2Handler {
258         @Override
handle(Http2TestExchange t)259         public void handle(Http2TestExchange t) throws IOException {
260             out.println("HTTP2_FixedLengthHandler received request to " + t.getRequestURI());
261             try (InputStream is = t.getRequestBody();
262                  OutputStream os = t.getResponseBody()) {
263                 is.readAllBytes();
264                 byte[] bytes = BODY.getBytes(UTF_8);
265                 t.sendResponseHeaders(200, bytes.length);
266                 os.write(bytes);
267             }
268         }
269     }
270 
271     static class HTTP2_VariableHandler implements Http2Handler {
272         @Override
handle(Http2TestExchange t)273         public void handle(Http2TestExchange t) throws IOException {
274             out.println("HTTP2_VariableHandler received request to " + t.getRequestURI());
275             try (InputStream is = t.getRequestBody();
276                  OutputStream os = t.getResponseBody()) {
277                 is.readAllBytes();
278                 byte[] bytes = BODY.getBytes(UTF_8);
279                 t.sendResponseHeaders(200, 0); // variable
280                 os.write(bytes);
281             }
282         }
283     }
284 }
285