1 /*
2  * Copyright (c) 2002, 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 
24 /**
25  * @test
26  * @bug 4636331
27  * @library /test/lib
28  * @summary Check that URLClassLoader doesn't create excessive http
29  *          connections
30  */
31 import java.net.*;
32 import java.io.*;
33 import java.util.*;
34 
35 import jdk.test.lib.net.URIBuilder;
36 
37 public class HttpTest {
38 
39     /*
40      * Simple http server to service http requests. Auto shutdown
41      * if "idle" (no requests) for 10 seconds. Forks worker thread
42      * to service persistent connections. Work threads shutdown if
43      * "idle" for 5 seconds.
44      */
45     static class HttpServer implements Runnable {
46 
47         private static HttpServer svr = null;
48         private static Counters cnts = null;
49         private static ServerSocket ss;
50 
51         private static Object counterLock = new Object();
52         private static int getCount = 0;
53         private static int headCount = 0;
54 
55         class Worker extends Thread {
56             Socket s;
Worker(Socket s)57             Worker(Socket s) {
58                 this.s = s;
59             }
60 
run()61             public void run() {
62                 InputStream in = null;
63                 try {
64                     in = s.getInputStream();
65                     for (;;) {
66 
67                         // read entire request from client
68                         byte b[] = new byte[1024];
69                         int n, total=0;
70 
71                         // max 5 seconds to wait for new request
72                         s.setSoTimeout(5000);
73                         try {
74                             do {
75                                 n = in.read(b, total, b.length-total);
76                                 // max 0.5 seconds between each segment
77                                 // of request.
78                                 s.setSoTimeout(500);
79                                 if (n > 0) total += n;
80                             } while (n > 0);
81                         } catch (SocketTimeoutException e) { }
82 
83                         if (total == 0) {
84                             s.close();
85                             return;
86                         }
87 
88                         boolean getRequest = false;
89                         if (b[0] == 'G' && b[1] == 'E' && b[2] == 'T')
90                             getRequest = true;
91 
92                         synchronized (counterLock) {
93                             if (getRequest)
94                                 getCount++;
95                             else
96                                 headCount++;
97                         }
98 
99                         // response to client
100                         PrintStream out = new PrintStream(
101                                 new BufferedOutputStream(
102                                         s.getOutputStream() ));
103                         out.print("HTTP/1.1 200 OK\r\n");
104 
105                         out.print("Content-Length: 75000\r\n");
106                         out.print("\r\n");
107                         if (getRequest) {
108                             for (int i=0; i<75*1000; i++) {
109                                 out.write( (byte)'.' );
110                             }
111                         }
112                         out.flush();
113 
114                     } // for
115 
116                 } catch (Exception e) {
117                     unexpected(e);
118                 } finally {
119                     if (in != null) { try {in.close(); } catch(IOException e) {unexpected(e);} }
120                 }
121             }
122         }
123 
HttpServer()124         HttpServer() throws Exception {
125             ss = new ServerSocket();
126             ss.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
127         }
128 
run()129         public void run() {
130             try {
131                 // shutdown if no request in 10 seconds.
132                 ss.setSoTimeout(10000);
133                 for (;;) {
134                     Socket s = ss.accept();
135                     (new Worker(s)).start();
136                 }
137             } catch (Exception e) {
138             }
139         }
140 
unexpected(Exception e)141         void unexpected(Exception e) {
142             System.out.println(e);
143             e.printStackTrace();
144         }
145 
create()146         public static HttpServer create() throws Exception {
147             if (svr != null)
148                 return svr;
149             cnts = new Counters();
150             svr = new HttpServer();
151             (new Thread(svr)).start();
152             return svr;
153         }
154 
shutdown()155         public static void shutdown() throws Exception {
156             if (svr != null) {
157                 ss.close();
158                 svr = null;
159             }
160         }
161 
port()162         public int port() {
163             return ss.getLocalPort();
164         }
165 
166         public static class Counters {
reset()167             public void reset() {
168                 synchronized (counterLock) {
169                     getCount = 0;
170                     headCount = 0;
171                 }
172             }
173 
getCount()174             public int getCount() {
175                 synchronized (counterLock) {
176                     return getCount;
177                 }
178             }
179 
headCount()180             public int headCount() {
181                 synchronized (counterLock) {
182                     return headCount;
183                 }
184             }
185 
toString()186             public String toString() {
187                 synchronized (counterLock) {
188                     return "GET count: " + getCount + "; " +
189                        "HEAD count: " + headCount;
190                 }
191             }
192         }
193 
counters()194         public Counters counters() {
195             return cnts;
196         }
197 
198     }
199 
main(String args[])200     public static void main(String args[]) throws Exception {
201         boolean failed = false;
202 
203         // create http server
204         HttpServer svr = HttpServer.create();
205 
206         // create class loader
207         URL urls[] = {
208                 URIBuilder.newBuilder().scheme("http").loopback().port(svr.port())
209                         .path("/dir1/").toURL(),
210                 URIBuilder.newBuilder().scheme("http").loopback().port(svr.port())
211                         .path("/dir2/").toURL(),
212         };
213         URLClassLoader cl = new URLClassLoader(urls);
214 
215         // Test 1 - check that getResource does single HEAD request
216         svr.counters().reset();
217         URL url = cl.getResource("foo.gif");
218         System.out.println(svr.counters());
219 
220         if (svr.counters().getCount() > 0 ||
221             svr.counters().headCount() > 1) {
222             failed = true;
223         }
224 
225         // Test 2 - check that getResourceAsStream does at most
226         //          one GET request
227         svr.counters().reset();
228         InputStream in = cl.getResourceAsStream("foo2.gif");
229         in.close();
230         System.out.println(svr.counters());
231         if (svr.counters().getCount() > 1) {
232             failed = true;
233         }
234 
235         // Test 3 - check that getResources only does HEAD requests
236         svr.counters().reset();
237         Enumeration e = cl.getResources("foos.gif");
238         try {
239             for (;;) {
240                 e.nextElement();
241             }
242         } catch (NoSuchElementException exc) { }
243         System.out.println(svr.counters());
244         if (svr.counters().getCount() > 1) {
245             failed = true;
246         }
247 
248         // shutdown http server
249         svr.shutdown();
250 
251         if (failed) {
252             throw new Exception("Excessive http connections established - Test failed");
253         }
254     }
255 
256 }
257