jdk/src/java.rmi/share/classes/sun/rmi/transport/proxy/HttpSendSocket.java
changeset 37947 f69560487686
parent 37946 e420b9f05aaf
parent 37936 428ebc487445
child 37948 caf97b37ebec
equal deleted inserted replaced
37946:e420b9f05aaf 37947:f69560487686
     1 /*
       
     2  * Copyright (c) 1996, 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.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 package sun.rmi.transport.proxy;
       
    26 
       
    27 import java.io.*;
       
    28 import java.net.*;
       
    29 import java.security.PrivilegedAction;
       
    30 
       
    31 import sun.rmi.runtime.Log;
       
    32 
       
    33 /**
       
    34  * The HttpSendSocket class extends the java.net.Socket class
       
    35  * by enclosing the data output stream in, then extracting the input
       
    36  * stream from, an HTTP protocol transmission.
       
    37  *
       
    38  * NOTES:
       
    39  *
       
    40  * Since the length of the output request must be known before the
       
    41  * HTTP header can be completed, all of the output is buffered by
       
    42  * an HttpOutputStream object until either an attempt is made to
       
    43  * read from this socket, or the socket is explicitly closed.
       
    44  *
       
    45  * On the first read attempt to read from this socket, the buffered
       
    46  * output is sent to the destination as the body of an HTTP POST
       
    47  * request.  All reads will then acquire data from the body of
       
    48  * the response.  A subsequent attempt to write to this socket will
       
    49  * throw an IOException.
       
    50  */
       
    51 class HttpSendSocket extends Socket implements RMISocketInfo {
       
    52 
       
    53     /** the host to connect to */
       
    54     protected String host;
       
    55 
       
    56     /** the port to connect to */
       
    57     protected int port;
       
    58 
       
    59     /** the URL to forward through */
       
    60     protected URL url;
       
    61 
       
    62     /** the object managing this connection through the URL */
       
    63     protected URLConnection conn = null;
       
    64 
       
    65     /** internal input stream for this socket */
       
    66     protected InputStream in = null;
       
    67 
       
    68     /** internal output stream for this socket */
       
    69     protected OutputStream out = null;
       
    70 
       
    71     /** the notifying input stream returned to users */
       
    72     protected HttpSendInputStream inNotifier;
       
    73 
       
    74     /** the notifying output stream returned to users */
       
    75     protected HttpSendOutputStream outNotifier;
       
    76 
       
    77     /**
       
    78      * Line separator string.  This is the value of the line.separator
       
    79      * property at the moment that the socket was created.
       
    80      */
       
    81     private String lineSeparator =
       
    82         java.security.AccessController.doPrivileged(
       
    83             (PrivilegedAction<String>) () -> System.getProperty("line.separator"));
       
    84 
       
    85     /**
       
    86      * Create a stream socket and connect it to the specified port on
       
    87      * the specified host.
       
    88      * @param host the host
       
    89      * @param port the port
       
    90      */
       
    91     public HttpSendSocket(String host, int port, URL url) throws IOException
       
    92     {
       
    93         super((SocketImpl)null);        // no underlying SocketImpl for this object
       
    94 
       
    95         if (RMIMasterSocketFactory.proxyLog.isLoggable(Log.VERBOSE)) {
       
    96             RMIMasterSocketFactory.proxyLog.log(Log.VERBOSE,
       
    97                 "host = " + host + ", port = " + port + ", url = " + url);
       
    98         }
       
    99 
       
   100         this.host = host;
       
   101         this.port = port;
       
   102         this.url = url;
       
   103 
       
   104         inNotifier = new HttpSendInputStream(null, this);
       
   105         outNotifier = new HttpSendOutputStream(writeNotify(), this);
       
   106     }
       
   107 
       
   108     /**
       
   109      * Create a stream socket and connect it to the specified port on
       
   110      * the specified host.
       
   111      * @param host the host
       
   112      * @param port the port
       
   113      */
       
   114     public HttpSendSocket(String host, int port) throws IOException
       
   115     {
       
   116         this(host, port, new URL("http", host, port, "/"));
       
   117     }
       
   118 
       
   119     /**
       
   120      * Create a stream socket and connect it to the specified address on
       
   121      * the specified port.
       
   122      * @param address the address
       
   123      * @param port the port
       
   124      */
       
   125     public HttpSendSocket(InetAddress address, int port) throws IOException
       
   126     {
       
   127         this(address.getHostName(), port);
       
   128     }
       
   129 
       
   130     /**
       
   131      * Indicate that this socket is not reusable.
       
   132      */
       
   133     public boolean isReusable()
       
   134     {
       
   135         return false;
       
   136     }
       
   137 
       
   138     /**
       
   139      * Create a new socket connection to host (or proxy), and prepare to
       
   140      * send HTTP transmission.
       
   141      */
       
   142     public synchronized OutputStream writeNotify() throws IOException
       
   143     {
       
   144         if (conn != null) {
       
   145             throw new IOException("attempt to write on HttpSendSocket after " +
       
   146                                   "request has been sent");
       
   147         }
       
   148 
       
   149         conn = url.openConnection();
       
   150         conn.setDoOutput(true);
       
   151         conn.setUseCaches(false);
       
   152         conn.setRequestProperty("Content-type", "application/octet-stream");
       
   153 
       
   154         inNotifier.deactivate();
       
   155         in = null;
       
   156 
       
   157         return out = conn.getOutputStream();
       
   158     }
       
   159 
       
   160     /**
       
   161      * Send HTTP output transmission and prepare to receive response.
       
   162      */
       
   163     public synchronized InputStream readNotify() throws IOException
       
   164     {
       
   165         RMIMasterSocketFactory.proxyLog.log(Log.VERBOSE,
       
   166             "sending request and activating input stream");
       
   167 
       
   168         outNotifier.deactivate();
       
   169         out.close();
       
   170         out = null;
       
   171 
       
   172         try {
       
   173             in = conn.getInputStream();
       
   174         } catch (IOException e) {
       
   175             RMIMasterSocketFactory.proxyLog.log(Log.BRIEF,
       
   176                 "failed to get input stream, exception: ", e);
       
   177 
       
   178             throw new IOException("HTTP request failed");
       
   179         }
       
   180 
       
   181         /*
       
   182          * If an HTTP error response is returned, sometimes an IOException
       
   183          * is thrown, which is handled above, and other times it isn't, and
       
   184          * the error response body will be available for reading.
       
   185          * As a safety net to catch any such unexpected HTTP behavior, we
       
   186          * verify that the content type of the response is what the
       
   187          * HttpOutputStream generates: "application/octet-stream".
       
   188          * (Servers' error responses will generally be "text/html".)
       
   189          * Any error response body is printed to the log.
       
   190          */
       
   191         String contentType = conn.getContentType();
       
   192         if (contentType == null ||
       
   193             !conn.getContentType().equals("application/octet-stream"))
       
   194         {
       
   195             if (RMIMasterSocketFactory.proxyLog.isLoggable(Log.BRIEF)) {
       
   196                 String message;
       
   197                 if (contentType == null) {
       
   198                     message = "missing content type in response" +
       
   199                         lineSeparator;
       
   200                 } else {
       
   201                     message = "invalid content type in response: " +
       
   202                         contentType + lineSeparator;
       
   203                 }
       
   204 
       
   205                 message += "HttpSendSocket.readNotify: response body: ";
       
   206                 try {
       
   207                     BufferedReader din = new BufferedReader(new InputStreamReader(in));
       
   208                     String line;
       
   209                     while ((line = din.readLine()) != null)
       
   210                         message += line + lineSeparator;
       
   211                 } catch (IOException e) {
       
   212                 }
       
   213                 RMIMasterSocketFactory.proxyLog.log(Log.BRIEF, message);
       
   214             }
       
   215 
       
   216             throw new IOException("HTTP request failed");
       
   217         }
       
   218 
       
   219         return in;
       
   220     }
       
   221 
       
   222     /**
       
   223      * Get the address to which the socket is connected.
       
   224      */
       
   225     public InetAddress getInetAddress()
       
   226     {
       
   227         try {
       
   228             return InetAddress.getByName(host);
       
   229         } catch (UnknownHostException e) {
       
   230             return null;        // null if couldn't resolve destination host
       
   231         }
       
   232     }
       
   233 
       
   234     /**
       
   235      * Get the local address to which the socket is bound.
       
   236      */
       
   237     public InetAddress getLocalAddress()
       
   238     {
       
   239         try {
       
   240             return InetAddress.getLocalHost();
       
   241         } catch (UnknownHostException e) {
       
   242             return null;        // null if couldn't determine local host
       
   243         }
       
   244     }
       
   245 
       
   246     /**
       
   247      * Get the remote port to which the socket is connected.
       
   248      */
       
   249     public int getPort()
       
   250     {
       
   251         return port;
       
   252     }
       
   253 
       
   254     /**
       
   255      * Get the local port to which the socket is connected.
       
   256      */
       
   257     public int getLocalPort()
       
   258     {
       
   259         return -1;      // request not applicable to this socket type
       
   260     }
       
   261 
       
   262     /**
       
   263      * Get an InputStream for this socket.
       
   264      */
       
   265     public InputStream getInputStream() throws IOException
       
   266     {
       
   267         return inNotifier;
       
   268     }
       
   269 
       
   270     /**
       
   271      * Get an OutputStream for this socket.
       
   272      */
       
   273     public OutputStream getOutputStream() throws IOException
       
   274     {
       
   275         return outNotifier;
       
   276     }
       
   277 
       
   278     /**
       
   279      * Enable/disable TCP_NODELAY.
       
   280      * This operation has no effect for an HttpSendSocket.
       
   281      */
       
   282     public void setTcpNoDelay(boolean on) throws SocketException
       
   283     {
       
   284     }
       
   285 
       
   286     /**
       
   287      * Retrieve whether TCP_NODELAY is enabled.
       
   288      */
       
   289     public boolean getTcpNoDelay() throws SocketException
       
   290     {
       
   291         return false;   // imply option is disabled
       
   292     }
       
   293 
       
   294     /**
       
   295      * Enable/disable SO_LINGER with the specified linger time.
       
   296      * This operation has no effect for an HttpSendSocket.
       
   297      */
       
   298     public void setSoLinger(boolean on, int val) throws SocketException
       
   299     {
       
   300     }
       
   301 
       
   302     /**
       
   303      * Retrive setting for SO_LINGER.
       
   304      */
       
   305     public int getSoLinger() throws SocketException
       
   306     {
       
   307         return -1;      // imply option is disabled
       
   308     }
       
   309 
       
   310     /**
       
   311      * Enable/disable SO_TIMEOUT with the specified timeout
       
   312      * This operation has no effect for an HttpSendSocket.
       
   313      */
       
   314     public synchronized void setSoTimeout(int timeout) throws SocketException
       
   315     {
       
   316     }
       
   317 
       
   318     /**
       
   319      * Retrive setting for SO_TIMEOUT.
       
   320      */
       
   321     public synchronized int getSoTimeout() throws SocketException
       
   322     {
       
   323         return 0;       // imply option is disabled
       
   324     }
       
   325 
       
   326     /**
       
   327      * Close the socket.
       
   328      */
       
   329     public synchronized void close() throws IOException
       
   330     {
       
   331         if (out != null) // push out transmission if not done
       
   332             out.close();
       
   333     }
       
   334 
       
   335     /**
       
   336      * Return string representation of this pseudo-socket.
       
   337      */
       
   338     public String toString()
       
   339     {
       
   340         return "HttpSendSocket[host=" + host +
       
   341                ",port=" + port +
       
   342                ",url=" + url + "]";
       
   343     }
       
   344 }