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