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