1 /*
2  * Copyright (c) 2018, 2019, 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 import java.io.InputStream;
24 import java.io.OutputStream;
25 import java.net.InetAddress;
26 import java.net.InetSocketAddress;
27 import java.net.ServerSocket;
28 import java.net.Socket;
29 import java.net.URI;
30 import java.net.SocketTimeoutException;
31 import java.time.Duration;
32 import javax.net.ssl.SSLServerSocketFactory;
33 import javax.net.ServerSocketFactory;
34 import javax.net.ssl.SSLContext;
35 import javax.net.ssl.SSLParameters;
36 import java.net.http.HttpClient;
37 import java.net.http.HttpRequest;
38 import java.net.http.HttpResponse;
39 import java.util.List;
40 import java.util.concurrent.CopyOnWriteArrayList;
41 
42 import jdk.test.lib.net.SimpleSSLContext;
43 
44 /**
45  * @test
46  * @bug 8207966
47  * @library /test/lib
48  * @build jdk.test.lib.net.SimpleSSLContext
49  * @run main/othervm -Djdk.httpclient.enableAllMethodRetry
50  *                   -Djdk.tls.acknowledgeCloseNotify=true UnknownBodyLengthTest plain false
51  * @run main/othervm -Djdk.httpclient.enableAllMethodRetry
52  *                   -Djdk.tls.acknowledgeCloseNotify=true UnknownBodyLengthTest SSL false
53  * @run main/othervm -Djdk.httpclient.enableAllMethodRetry
54  *                   -Djdk.tls.acknowledgeCloseNotify=true UnknownBodyLengthTest plain true
55  * @run main/othervm -Djdk.httpclient.enableAllMethodRetry
56  *                   -Djdk.tls.acknowledgeCloseNotify=true UnknownBodyLengthTest SSL true
57  */
58 
59 public class UnknownBodyLengthTest {
60     static final byte[] BUF = new byte[32 * 10234 + 2];
61 
62     volatile SSLContext ctx;
63     volatile ServerSocketFactory factory;
64     volatile String clientURL;
65     volatile int port;
66     final ServerSocket ss;
67     final List<Socket> acceptedList = new CopyOnWriteArrayList<>();
68 
UnknownBodyLengthTest(boolean useSSL)69     UnknownBodyLengthTest(boolean useSSL) throws Exception {
70         ctx = new SimpleSSLContext().get();
71         SSLContext.setDefault(ctx);
72         factory = useSSL ? SSLServerSocketFactory.getDefault()
73                          : ServerSocketFactory.getDefault();
74         ss = factory.createServerSocket();
75         ss.setReuseAddress(true);
76         ss.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
77         System.out.println("ServerSocket = " + ss.getClass() + " " + ss);
78         port = ss.getLocalPort();
79         clientURL = (useSSL ? "https" : "http") + "://localhost:"
80             + Integer.toString(port) + "/test";
81     }
82 
fillBuf(byte[] buf)83     static void fillBuf(byte[] buf) {
84         for (int i=0; i<buf.length; i++)
85             buf[i] = (byte)i;
86     }
87 
checkBuf(byte[] buf)88     static void checkBuf(byte[] buf) {
89         if (buf.length != BUF.length)
90             throw new RuntimeException("buffer lengths not the same");
91         for (int i=0; i<buf.length; i++)
92             if (buf[i] != BUF[i])
93                 throw new RuntimeException("error at position " + i);
94     }
95 
96     volatile boolean stopped;
97 
server(final boolean withContentLength)98     void server(final boolean withContentLength) {
99         fillBuf(BUF);
100         try {
101             while (!stopped) {
102                 try {
103                     Socket s = ss.accept();
104                     acceptedList.add(s);
105                     s.setTcpNoDelay(true);
106                     // if we use linger=1 we still see some
107                     // intermittent failures caused by IOException
108                     // "Connection reset by peer".
109                     // The client side is expecting EOF, but gets reset instead.
110                     // 30 is a 'magic' value that may need to be adjusted again.
111                     s.setSoLinger(true, 30);
112                     System.out.println("Accepted: " + s.getRemoteSocketAddress());
113                     System.out.println("Accepted: " + s);
114                     OutputStream os = s.getOutputStream();
115                     InputStream is = s.getInputStream();
116                     boolean done = false;
117                     byte[] buf = new byte[1024];
118                     String rsp = "";
119                     while (!done) {
120                         int c = is.read(buf);
121                         if (c < 0) break;
122                         String s1 = new String(buf, 0, c, "ISO-8859-1");
123                         rsp += s1;
124                         done = rsp.endsWith("!#!#");
125                     }
126                     String r = "HTTP/1.0 200 OK\r\nConnection: close\r\nContent-Type:" +
127                             " text/xml; charset=UTF-8\r\n";
128                     os.write(r.getBytes());
129                     String chdr = "Content-Length: " + Integer.toString(BUF.length) +
130                             "\r\n";
131                     System.out.println(chdr);
132                     if (withContentLength)
133                         os.write(chdr.getBytes());
134                     os.write("\r\n".getBytes());
135                     os.write(BUF);
136                     if (is.available() > 0) {
137                         System.out.println("Draining input: " + s);
138                         is.read(buf);
139                     }
140                     os.flush();
141                     s.shutdownOutput();
142                     System.out.println("Closed output: " + s);
143                 } catch(Exception e) {
144                     if (!stopped) {
145                         System.out.println("Unexpected server exception: " + e);
146                         e.printStackTrace();
147                     }
148                 }
149             }
150         } catch(final Throwable t) {
151             if (!stopped) t.printStackTrace();
152         } finally {
153             stop();
154         }
155     }
156 
client(boolean useSSL)157     void client(boolean useSSL) throws Exception {
158         SSLContext ctx = SSLContext.getDefault();
159         HttpClient.Builder clientB = HttpClient.newBuilder()
160             .version(HttpClient.Version.HTTP_2);
161         if (useSSL) {
162             clientB = clientB.sslContext(ctx)
163                 .sslParameters(ctx.getSupportedSSLParameters());
164         }
165         final HttpClient client = clientB.build();
166 
167         System.out.println("URL: " + clientURL);
168         final HttpResponse<byte[]> response = client
169             .send(HttpRequest
170                 .newBuilder(new URI(clientURL))
171                 .timeout(Duration.ofMillis(120_000))
172                 .POST(HttpRequest.BodyPublishers.ofString("body!#!#"))
173                 .build(), HttpResponse.BodyHandlers.ofByteArray());
174 
175         System.out.println("Received reply: " + response.statusCode());
176         byte[] bb = response.body();
177         checkBuf(bb);
178     }
179 
main(final String[] args)180     public static void main(final String[] args) throws Exception {
181         boolean ssl = args[0].equals("SSL");
182         boolean fixedlen = args[1].equals("true");
183         UnknownBodyLengthTest test = new UnknownBodyLengthTest(ssl);
184         try {
185             test.run(ssl, fixedlen);
186         } finally {
187             test.stop();
188         }
189     }
190 
run(boolean ssl, boolean fixedlen)191     public void run(boolean ssl, boolean fixedlen) throws Exception {
192         new Thread(()->server(fixedlen)).start();
193         client(ssl);
194     }
195 
stop()196     public void stop() {
197         stopped = true;
198         try { ss.close(); } catch (Throwable t) { }
199         for (Socket s : acceptedList) {
200             try { s.close(); } catch (Throwable t) { }
201         }
202     }
203 }
204