1 /*
   2  * Copyright (c) 1999, 2012, 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 /* @test
  25  * @bug 4203167
  26  *
  27  * @summary RMI blocks in HttpAwareServerSocket.accept() if you telnet to it
  28  * @author Adrian Colley
  29  *
  30  * @library ../../../../../java/rmi/testlibrary
  31  * @modules java.rmi/sun.rmi.transport.proxy
  32  * @build TestIface TestImpl TestImpl_Stub
  33  * @run main/othervm/policy=security.policy/timeout=60 BlockAcceptTest
  34  */
  35 
  36 /* This test attempts to stymie the RMI accept loop.  The accept loop in
  37  * RMI endlessly accepts a connection, spawns a thread for it, and repeats.
  38  * The accept() call can be replaced by a user-supplied library which
  39  * might foolishly block indefinitely in its accept() method, which would
  40  * prevent RMI from accepting other connections on that socket.
  41  *
  42  * Unfortunately, HttpAwareServerSocket (default server socket) is/was such
  43  * a foolish thing.  It reads 4 bytes to see if they're "POST" before
  44  * returning.  The bug fix is to move the HTTP stuff into the mainloop,
  45  * which has the side effect of enabling it for non-default socketfactories.
  46  *
  47  * This test:
  48  * 1. Creates an object and exports it.
  49  * 2. Connects to the listening RMI port and sends nothing, to hold it up.
  50  * 3. Makes a regular call, using HTTP tunnelling.
  51  * 4. Fails to deadlock, thereby passing the test.
  52  *
  53  * Some runtime dependencies I'm trying to eliminate:
  54  * 1. We don't know the port number until after exporting the object, but
  55  *    have to set it in http.proxyPort somehow.  Hopefully http.proxyPort
  56  *    isn't read too soon or this test will fail with a ConnectException.
  57  */
  58 
  59 import java.rmi.*;
  60 import java.rmi.server.RMISocketFactory;
  61 import java.io.*;
  62 import java.net.*;
  63 
  64 import sun.rmi.transport.proxy.RMIMasterSocketFactory;
  65 import sun.rmi.transport.proxy.RMIHttpToPortSocketFactory;
  66 
  67 public class BlockAcceptTest
  68 {
  69     public static void main(String[] args)
  70         throws Exception
  71     {
  72         // Make trouble for ourselves
  73         if (System.getSecurityManager() == null)
  74             System.setSecurityManager(new RMISecurityManager());
  75 
  76         // HTTP direct to the server port
  77         System.setProperty("http.proxyHost", "127.0.0.1");
  78 
  79         // Set the socket factory.
  80         System.err.println("(installing HTTP-out socket factory)");
  81         HttpOutFactory fac = new HttpOutFactory();
  82         RMISocketFactory.setSocketFactory(fac);
  83 
  84         // Create remote object
  85         TestImpl impl = new TestImpl();
  86 
  87         // Export and get which port.
  88         System.err.println("(exporting remote object)");
  89         TestIface stub = impl.export();
  90         try {
  91             int port = fac.whichPort();
  92 
  93             // Sanity
  94             if (port == 0)
  95                 throw new Error("TEST FAILED: export didn't reserve a port(?)");
  96 
  97             // Set the HTTP port, at last.
  98             System.setProperty("http.proxyPort", port+"");
  99 
 100             // Now, connect to that port
 101             //Thread.sleep(2000);
 102             System.err.println("(connecting to listening port on 127.0.0.1:" +
 103                                port + ")");
 104             Socket DoS = new Socket("127.0.0.1", port);
 105             // we hold the connection open until done with the test.
 106 
 107             // The test itself: make a remote call and see if it's blocked or
 108             // if it works
 109             //Thread.sleep(2000);
 110             System.err.println("(making RMI-through-HTTP call)");
 111             System.err.println("(typical test failure deadlocks here)");
 112             String result = stub.testCall("dummy load");
 113 
 114             System.err.println(" => " + result);
 115             if (!("OK".equals(result)))
 116                 throw new Error("TEST FAILED: result not OK");
 117             System.err.println("Test passed.");
 118 
 119             // Clean up, including writing a byte to that connection just in
 120             // case an optimizer thought of optimizing it out of existence
 121             try {
 122                 DoS.getOutputStream().write(0);
 123                 DoS.getOutputStream().close();
 124             } catch (Throwable apathy) {
 125             }
 126 
 127         } finally {
 128             try {
 129                 impl.unexport();
 130             } catch (Throwable unmatter) {
 131             }
 132         }
 133 
 134         // Should exit here
 135     }
 136 
 137     private static class HttpOutFactory
 138         extends RMISocketFactory
 139     {
 140         private int servport = 0;
 141 
 142         public Socket createSocket(String h, int p)
 143             throws IOException
 144         {
 145             return ((new RMIHttpToPortSocketFactory()).createSocket(h, p));
 146         }
 147 
 148         /** Create a server socket and remember which port it's on.
 149          * Aborts if createServerSocket(0) is called twice, because then
 150          * it doesn't know whether to remember the first or second port.
 151          */
 152         public ServerSocket createServerSocket(int p)
 153             throws IOException
 154         {
 155             ServerSocket ss;
 156             ss = (new RMIMasterSocketFactory()).createServerSocket(p);
 157             if (p == 0) {
 158                 if (servport != 0) {
 159                     System.err.println("TEST FAILED: " +
 160                                        "Duplicate createServerSocket(0)");
 161                     throw new Error("Test aborted (createServerSocket)");
 162                 }
 163                 servport = ss.getLocalPort();
 164             }
 165             return (ss);
 166         }
 167 
 168         /** Return which port was reserved by createServerSocket(0).
 169          * If the return value was 0, createServerSocket(0) wasn't called.
 170          */
 171         public int whichPort() {
 172             return (servport);
 173         }
 174     } // end class HttpOutFactory
 175 }