1 /*
  2  * Copyright (c) 2015, 2023, 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  * library /test/lib /
 26  * build jdk.test.lib.net.SimpleSSLContext ProxyServer
 27  * compile ../../../com/sun/net/httpserver/LogFilter.java
 28  * compile ../../../com/sun/net/httpserver/EchoHandler.java
 29  * compile ../../../com/sun/net/httpserver/FileServerHandler.java
 30  */
 31 import com.sun.net.httpserver.Headers;
 32 import com.sun.net.httpserver.HttpContext;
 33 import com.sun.net.httpserver.HttpExchange;
 34 import com.sun.net.httpserver.HttpHandler;
 35 import com.sun.net.httpserver.HttpServer;
 36 import com.sun.net.httpserver.HttpsConfigurator;
 37 import com.sun.net.httpserver.HttpsServer;
 38 import java.io.IOException;
 39 import java.io.InputStream;
 40 import java.io.OutputStream;
 41 import java.net.InetAddress;
 42 import java.net.InetSocketAddress;
 43 import java.nio.file.Path;
 44 import java.util.HashSet;
 45 import java.util.concurrent.BrokenBarrierException;
 46 import java.util.concurrent.CyclicBarrier;
 47 import java.util.concurrent.ExecutorService;
 48 import java.util.concurrent.Executors;
 49 import java.util.logging.ConsoleHandler;
 50 import java.util.logging.Level;
 51 import java.util.logging.Logger;
 52 import javax.net.ssl.SSLContext;
 53 import jdk.test.lib.net.SimpleSSLContext;
 54 
 55 public class LightWeightHttpServer {
 56 
 57     static SSLContext ctx;
 58     static HttpServer httpServer;
 59     static HttpsServer httpsServer;
 60     static ExecutorService executor;
 61     static int port;
 62     static int httpsport;
 63     static String httproot;
 64     static String httpsroot;
 65     static ProxyServer proxy;
 66     static int proxyPort;
 67     static RedirectErrorHandler redirectErrorHandler, redirectErrorHandlerSecure;
 68     static RedirectHandler redirectHandler, redirectHandlerSecure;
 69     static DelayHandler delayHandler;
 70     static final String midSizedFilename = "/files/notsobigfile.txt";
 71     static final String smallFilename = "/files/smallfile.txt";
 72     static Path midSizedFile;
 73     static Path smallFile;
 74     static String fileroot;
 75 
 76     public static void initServer() throws IOException {
 77 
 78         Logger logger = Logger.getLogger("com.sun.net.httpserver");
 79         ConsoleHandler ch = new ConsoleHandler();
 80         logger.setLevel(Level.ALL);
 81         ch.setLevel(Level.ALL);
 82         logger.addHandler(ch);
 83 
 84         String root = System.getProperty("test.src", ".") + "/docs";
 85         InetSocketAddress addr = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
 86         httpServer = HttpServer.create(addr, 0);
 87         if (httpServer instanceof HttpsServer) {
 88             throw new RuntimeException("should not be httpsserver");
 89         }
 90         httpsServer = HttpsServer.create(addr, 0);
 91         HttpHandler h = new FileServerHandler(root);
 92 
 93         HttpContext c1 = httpServer.createContext("/files", h);
 94         HttpContext c2 = httpsServer.createContext("/files", h);
 95         HttpContext c3 = httpServer.createContext("/echo", new EchoHandler());
 96         redirectHandler = new RedirectHandler("/redirect");
 97         redirectHandlerSecure = new RedirectHandler("/redirect");
 98         HttpContext c4 = httpServer.createContext("/redirect", redirectHandler);
 99         HttpContext c41 = httpsServer.createContext("/redirect", redirectHandlerSecure);
100         HttpContext c5 = httpsServer.createContext("/echo", new EchoHandler());
101         HttpContext c6 = httpServer.createContext("/keepalive", new KeepAliveHandler());
102         redirectErrorHandler = new RedirectErrorHandler("/redirecterror");
103         redirectErrorHandlerSecure = new RedirectErrorHandler("/redirecterror");
104         HttpContext c7 = httpServer.createContext("/redirecterror", redirectErrorHandler);
105         HttpContext c71 = httpsServer.createContext("/redirecterror", redirectErrorHandlerSecure);
106         delayHandler = new DelayHandler();
107         HttpContext c8 = httpServer.createContext("/delay", delayHandler);
108         HttpContext c81 = httpsServer.createContext("/delay", delayHandler);
109 
110         executor = Executors.newCachedThreadPool();
111         httpServer.setExecutor(executor);
112         httpsServer.setExecutor(executor);
113         ctx = new SimpleSSLContext().get();
114         httpsServer.setHttpsConfigurator(new HttpsConfigurator(ctx));
115         httpServer.start();
116         httpsServer.start();
117 
118         port = httpServer.getAddress().getPort();
119         System.out.println("HTTP server port = " + port);
120         httpsport = httpsServer.getAddress().getPort();
121         System.out.println("HTTPS server port = " + httpsport);
122         httproot = "http://" + makeServerAuthority(httpServer.getAddress()) + "/";
123         httpsroot = "https://" + makeServerAuthority(httpsServer.getAddress()) + "/";
124 
125         proxy = new ProxyServer(0, false);
126         proxyPort = proxy.getPort();
127         System.out.println("Proxy port = " + proxyPort);
128     }
129 
130     private static String makeServerAuthority(final InetSocketAddress addr) {
131         final String hostIP = addr.getAddress().getHostAddress();
132         // escape for ipv6
133         final String h = hostIP.contains(":") ? "[" + hostIP + "]" : hostIP;
134         return h + ":" + addr.getPort();
135     }
136 
137     public static void stop() throws IOException {
138         if (httpServer != null) {
139             httpServer.stop(0);
140         }
141         if (httpsServer != null) {
142             httpsServer.stop(0);
143         }
144         if (proxy != null) {
145             proxy.close();
146         }
147         if (executor != null) {
148             executor.shutdownNow();
149         }
150     }
151 
152     static class RedirectErrorHandler implements HttpHandler {
153 
154         String root;
155         volatile int count = 1;
156 
157         RedirectErrorHandler(String root) {
158             this.root = root;
159         }
160 
161         synchronized int count() {
162             return count;
163         }
164 
165         synchronized void increment() {
166             count++;
167         }
168 
169         @Override
170         public synchronized void handle(HttpExchange t)
171                 throws IOException {
172             byte[] buf = new byte[2048];
173             try (InputStream is = t.getRequestBody()) {
174                 while (is.read(buf) != -1) ;
175             }
176 
177             Headers map = t.getResponseHeaders();
178             String redirect = root + "/foo/" + Integer.toString(count);
179             increment();
180             map.add("Location", redirect);
181             t.sendResponseHeaders(301, -1);
182             t.close();
183         }
184     }
185 
186     static class RedirectHandler implements HttpHandler {
187 
188         String root;
189         volatile int count = 0;
190 
191         RedirectHandler(String root) {
192             this.root = root;
193         }
194 
195         @Override
196         public synchronized void handle(HttpExchange t)
197                 throws IOException {
198             byte[] buf = new byte[2048];
199             try (InputStream is = t.getRequestBody()) {
200                 while (is.read(buf) != -1) ;
201             }
202 
203             Headers map = t.getResponseHeaders();
204 
205             if (count++ < 1) {
206                 map.add("Location", root + "/foo/" + count);
207             } else {
208                 map.add("Location", SmokeTest.midSizedFilename);
209             }
210             t.sendResponseHeaders(301, -1);
211             t.close();
212         }
213 
214         int count() {
215             return count;
216         }
217 
218         void reset() {
219             count = 0;
220         }
221     }
222 
223     static class KeepAliveHandler implements HttpHandler {
224 
225         volatile int counter = 0;
226         HashSet<Integer> portSet = new HashSet<>();
227         volatile int[] ports = new int[4];
228 
229         void sleep(int n) {
230             try {
231                 Thread.sleep(n);
232             } catch (InterruptedException e) {
233             }
234         }
235 
236         @Override
237         public synchronized void handle(HttpExchange t)
238                 throws IOException {
239             int remotePort = t.getRemoteAddress().getPort();
240             String result = "OK";
241 
242             int n = counter++;
243             /// First test
244             if (n < 4) {
245                 ports[n] = remotePort;
246             }
247             if (n == 3) {
248                 // check all values in ports[] are the same
249                 if (ports[0] != ports[1] || ports[2] != ports[3]
250                         || ports[0] != ports[2]) {
251                     result = "Error " + Integer.toString(n);
252                     System.out.println(result);
253                 }
254             }
255             // Second test
256             if (n >= 4 && n < 8) {
257                 // delay to ensure ports are different
258                 sleep(500);
259                 ports[n - 4] = remotePort;
260             }
261             if (n == 7) {
262                 // should be all different
263                 if (ports[0] == ports[1] || ports[2] == ports[3]
264                         || ports[0] == ports[2]) {
265                     result = "Error " + Integer.toString(n);
266                     System.out.println(result);
267                     System.out.printf("Ports: %d, %d, %d, %d\n",
268                                       ports[0], ports[1], ports[2], ports[3]);
269                 }
270                 // setup for third test
271                 for (int i = 0; i < 4; i++) {
272                     portSet.add(ports[i]);
273                 }
274             }
275             // Third test
276             if (n > 7) {
277                 // just check that port is one of the ones in portSet
278                 if (!portSet.contains(remotePort)) {
279                     System.out.println("UNEXPECTED REMOTE PORT " + remotePort);
280                     result = "Error " + Integer.toString(n);
281                     System.out.println(result);
282                 }
283             }
284             byte[] buf = new byte[2048];
285 
286             try (InputStream is = t.getRequestBody()) {
287                 while (is.read(buf) != -1) ;
288             }
289             t.sendResponseHeaders(200, result.length());
290             OutputStream o = t.getResponseBody();
291             o.write(result.getBytes("US-ASCII"));
292             t.close();
293         }
294     }
295 
296     static class DelayHandler implements HttpHandler {
297 
298         CyclicBarrier bar1 = new CyclicBarrier(2);
299         CyclicBarrier bar2 = new CyclicBarrier(2);
300         CyclicBarrier bar3 = new CyclicBarrier(2);
301 
302         CyclicBarrier barrier1() {
303             return bar1;
304         }
305 
306         CyclicBarrier barrier2() {
307             return bar2;
308         }
309 
310         @Override
311         public synchronized void handle(HttpExchange he) throws IOException {
312             try(InputStream is = he.getRequestBody()) {
313                 is.readAllBytes();
314                 bar1.await();
315                 bar2.await();
316             } catch (InterruptedException | BrokenBarrierException e) {
317                 throw new IOException(e);
318             }
319             he.sendResponseHeaders(200, -1); // will probably fail
320             he.close();
321         }
322     }
323 }