jdk/src/java.httpclient/share/classes/java/net/http/HttpConnection.java
changeset 36131 379db4b2f95d
child 37720 45cd7cc65382
equal deleted inserted replaced
36130:5423259ef9ea 36131:379db4b2f95d
       
     1 /*
       
     2  * Copyright (c) 2015, 2016, 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  */
       
    24 package java.net.http;
       
    25 
       
    26 import java.io.FileOutputStream;
       
    27 import java.io.IOException;
       
    28 import java.io.PrintStream;
       
    29 import java.net.InetSocketAddress;
       
    30 import java.nio.ByteBuffer;
       
    31 import java.nio.channels.SocketChannel;
       
    32 import java.util.concurrent.CompletableFuture;
       
    33 import javax.net.ssl.SSLParameters;
       
    34 
       
    35 /**
       
    36  * Wraps socket channel layer and takes care of SSL also.
       
    37  *
       
    38  * Subtypes are:
       
    39  *      PlainHttpConnection: regular direct TCP connection to server
       
    40  *      PlainProxyConnection: plain text proxy connection
       
    41  *      PlainTunnelingConnection: opens plain text (CONNECT) tunnel to server
       
    42  *      SSLConnection: TLS channel direct to server
       
    43  *      SSLTunnelConnection: TLS channel via (CONNECT) proxy tunnel
       
    44  */
       
    45 abstract class HttpConnection implements BufferHandler {
       
    46 
       
    47     // address we are connected to. Could be a server or a proxy
       
    48     final InetSocketAddress address;
       
    49     final HttpClientImpl client;
       
    50     protected volatile ByteBuffer buffer;
       
    51 
       
    52     HttpConnection(InetSocketAddress address, HttpClientImpl client) {
       
    53         this.address = address;
       
    54         this.client = client;
       
    55     }
       
    56 
       
    57     /**
       
    58      * Public API to this class. addr is the ultimate destination. Any proxies
       
    59      * etc are figured out from the request. Returns an instance of one of the
       
    60      * following
       
    61      *      PlainHttpConnection
       
    62      *      PlainTunnelingConnection
       
    63      *      SSLConnection
       
    64      *      SSLTunnelConnection
       
    65      *
       
    66      * When object returned, connect() or connectAsync() must be called, which
       
    67      * when it returns/completes, the connection is usable for requests.
       
    68      */
       
    69     public static HttpConnection getConnection(InetSocketAddress addr,
       
    70                                                HttpRequestImpl request) {
       
    71         return getConnectionImpl(addr, request);
       
    72     }
       
    73 
       
    74     public abstract void connect() throws IOException, InterruptedException;
       
    75 
       
    76     public abstract CompletableFuture<Void> connectAsync();
       
    77 
       
    78     /**
       
    79      * Returns whether this connection is connected to its destination
       
    80      */
       
    81     abstract boolean connected();
       
    82 
       
    83     abstract boolean isSecure();
       
    84 
       
    85     abstract boolean isProxied();
       
    86 
       
    87     /**
       
    88      * Completes when the first byte of the response is available to be read.
       
    89      */
       
    90     abstract CompletableFuture<Void> whenReceivingResponse();
       
    91 
       
    92     // must be called before reading any data off connection
       
    93     // at beginning of response.
       
    94     ByteBuffer getRemaining() {
       
    95         ByteBuffer b = buffer;
       
    96         buffer = null;
       
    97         return b;
       
    98     }
       
    99 
       
   100     final boolean isOpen() {
       
   101         return channel().isOpen();
       
   102     }
       
   103 
       
   104     /* Returns either a plain HTTP connection or a plain tunnelling connection
       
   105      * for proxied websockets */
       
   106     private static HttpConnection getPlainConnection(InetSocketAddress addr,
       
   107                                                      InetSocketAddress proxy,
       
   108                                                      HttpRequestImpl request) {
       
   109         HttpClientImpl client = request.client();
       
   110 
       
   111         if (request.isWebSocket() && proxy != null) {
       
   112             return new PlainTunnelingConnection(addr,
       
   113                                                 proxy,
       
   114                                                 client,
       
   115                                                 request.getAccessControlContext());
       
   116         } else {
       
   117             if (proxy == null) {
       
   118                 return new PlainHttpConnection(addr, client);
       
   119             } else {
       
   120                 return new PlainProxyConnection(proxy, client);
       
   121             }
       
   122         }
       
   123     }
       
   124 
       
   125     private static HttpConnection getSSLConnection(InetSocketAddress addr,
       
   126                                                    InetSocketAddress proxy,
       
   127                                                    HttpRequestImpl request,
       
   128                                                    String[] alpn) {
       
   129         HttpClientImpl client = request.client();
       
   130         if (proxy != null) {
       
   131             return new SSLTunnelConnection(addr,
       
   132                                            client,
       
   133                                            proxy,
       
   134                                            request.getAccessControlContext());
       
   135         } else {
       
   136             return new SSLConnection(addr, client, alpn);
       
   137         }
       
   138     }
       
   139 
       
   140     /**
       
   141      * Main factory method.   Gets a HttpConnection, either cached or new if
       
   142      * none available.
       
   143      */
       
   144     private static HttpConnection getConnectionImpl(InetSocketAddress addr,
       
   145                                                     HttpRequestImpl request) {
       
   146         HttpConnection c;
       
   147         HttpClientImpl client = request.client();
       
   148         InetSocketAddress proxy = request.proxy();
       
   149         boolean secure = request.secure();
       
   150         ConnectionPool pool = client.connectionPool();
       
   151         String[] alpn =  null;
       
   152 
       
   153         if (secure && request.requestHttp2()) {
       
   154             alpn = new String[1];
       
   155             alpn[0] = "h2";
       
   156         }
       
   157 
       
   158         if (!secure) {
       
   159             c = pool.getConnection(false, addr, proxy);
       
   160             if (c != null) {
       
   161                 return c;
       
   162             } else {
       
   163                 return getPlainConnection(addr, proxy, request);
       
   164             }
       
   165         } else {
       
   166             c = pool.getConnection(true, addr, proxy);
       
   167             if (c != null) {
       
   168                 return c;
       
   169             } else {
       
   170                 return getSSLConnection(addr, proxy, request, alpn);
       
   171             }
       
   172         }
       
   173     }
       
   174 
       
   175     void returnToCache(HttpHeaders hdrs) {
       
   176         if (hdrs == null) {
       
   177             // the connection was closed by server
       
   178             close();
       
   179             return;
       
   180         }
       
   181         if (!isOpen()) {
       
   182             return;
       
   183         }
       
   184         ConnectionPool pool = client.connectionPool();
       
   185         boolean keepAlive = hdrs.firstValue("Connection")
       
   186                 .map((s) -> !s.equalsIgnoreCase("close"))
       
   187                 .orElse(true);
       
   188 
       
   189         if (keepAlive) {
       
   190             pool.returnToPool(this);
       
   191         } else {
       
   192             close();
       
   193         }
       
   194     }
       
   195 
       
   196     /**
       
   197      * Also check that the number of bytes written is what was expected. This
       
   198      * could be different if the buffer is user-supplied and its internal
       
   199      * pointers were manipulated in a race condition.
       
   200      */
       
   201     final void checkWrite(long expected, ByteBuffer buffer) throws IOException {
       
   202         long written = write(buffer);
       
   203         if (written != expected) {
       
   204             throw new IOException("incorrect number of bytes written");
       
   205         }
       
   206     }
       
   207 
       
   208     final void checkWrite(long expected,
       
   209                           ByteBuffer[] buffers,
       
   210                           int start,
       
   211                           int length)
       
   212         throws IOException
       
   213     {
       
   214         long written = write(buffers, start, length);
       
   215         if (written != expected) {
       
   216             throw new IOException("incorrect number of bytes written");
       
   217         }
       
   218     }
       
   219 
       
   220     abstract SocketChannel channel();
       
   221 
       
   222     final InetSocketAddress address() {
       
   223         return address;
       
   224     }
       
   225 
       
   226     void configureBlocking(boolean mode) throws IOException {
       
   227         channel().configureBlocking(mode);
       
   228     }
       
   229 
       
   230     abstract ConnectionPool.CacheKey cacheKey();
       
   231 
       
   232     /*
       
   233     static PrintStream ps;
       
   234 
       
   235     static {
       
   236         try {
       
   237             String propval = Utils.getNetProperty("java.net.httpclient.showData");
       
   238             if (propval != null && propval.equalsIgnoreCase("true")) {
       
   239                 ps = new PrintStream(new FileOutputStream("/tmp/httplog.txt"), false);
       
   240             }
       
   241         } catch (IOException e) {
       
   242             e.printStackTrace();
       
   243         }
       
   244     }
       
   245 
       
   246     synchronized final void debugPrint(String s, ByteBuffer b) {
       
   247         ByteBuffer[] bufs = new ByteBuffer[1];
       
   248         bufs[0] = b;
       
   249         debugPrint(s, bufs, 0, 1);
       
   250     }
       
   251 
       
   252     synchronized final void debugPrint(String s,
       
   253                                        ByteBuffer[] bufs,
       
   254                                        int start,
       
   255                                        int number) {
       
   256         if (ps == null) {
       
   257             return;
       
   258         }
       
   259 
       
   260         ps.printf("\n%s:\n", s);
       
   261 
       
   262         for (int i=start; i<start+number; i++) {
       
   263             ByteBuffer b = bufs[i].duplicate();
       
   264             while (b.hasRemaining()) {
       
   265                 int c = b.get();
       
   266                 if (c == 10) {
       
   267                     ps.printf("LF \n");
       
   268                 } else if (c == 13) {
       
   269                     ps.printf(" CR ");
       
   270                 } else if (c == 0x20) {
       
   271                     ps.printf("_");
       
   272                 } else if (c > 0x20 && c <= 0x7F) {
       
   273                     ps.printf("%c", (char)c);
       
   274                 } else {
       
   275                     ps.printf("0x%02x ", c);
       
   276                 }
       
   277             }
       
   278         }
       
   279         ps.printf("\n---------------------\n");
       
   280     }
       
   281 
       
   282     */
       
   283 
       
   284     // overridden in SSL only
       
   285     SSLParameters sslParameters() {
       
   286         return null;
       
   287     }
       
   288 
       
   289     // Methods to be implemented for Plain TCP and SSL
       
   290 
       
   291     abstract long write(ByteBuffer[] buffers, int start, int number)
       
   292         throws IOException;
       
   293 
       
   294     abstract long write(ByteBuffer buffer) throws IOException;
       
   295 
       
   296     /**
       
   297      * Closes this connection, by returning the socket to its connection pool.
       
   298      */
       
   299     abstract void close();
       
   300 
       
   301     /**
       
   302      * Returns a ByteBuffer with data, or null if EOF.
       
   303      */
       
   304     final ByteBuffer read() throws IOException {
       
   305         return read(-1);
       
   306     }
       
   307 
       
   308     /**
       
   309      * Puts position to limit and limit to capacity so we can resume reading
       
   310      * into this buffer, but if required > 0 then limit may be reduced so that
       
   311      * no more than required bytes are read next time.
       
   312      */
       
   313     static void resumeChannelRead(ByteBuffer buf, int required) {
       
   314         int limit = buf.limit();
       
   315         buf.position(limit);
       
   316         int capacity = buf.capacity() - limit;
       
   317         if (required > 0 && required < capacity) {
       
   318             buf.limit(limit + required);
       
   319         } else {
       
   320             buf.limit(buf.capacity());
       
   321         }
       
   322     }
       
   323 
       
   324     /**
       
   325      * Blocks ands return requested amount.
       
   326      */
       
   327     final ByteBuffer read(int length) throws IOException {
       
   328         if (length <= 0) {
       
   329             buffer = readImpl(length);
       
   330             return buffer;
       
   331         }
       
   332         buffer = readImpl(length);
       
   333         int required = length - buffer.remaining();
       
   334         while (buffer.remaining() < length) {
       
   335             resumeChannelRead(buffer, required);
       
   336             int n = readImpl(buffer);
       
   337             required -= n;
       
   338         }
       
   339         return buffer;
       
   340     }
       
   341 
       
   342     final int read(ByteBuffer buffer) throws IOException {
       
   343         int n = readImpl(buffer);
       
   344         return n;
       
   345     }
       
   346 
       
   347     /** Reads up to length bytes. */
       
   348     protected abstract ByteBuffer readImpl(int length) throws IOException;
       
   349 
       
   350     /** Reads as much as possible into given buffer and returns amount read. */
       
   351     protected abstract int readImpl(ByteBuffer buffer) throws IOException;
       
   352 
       
   353     @Override
       
   354     public String toString() {
       
   355         return "HttpConnection: " + channel().toString();
       
   356     }
       
   357 
       
   358     @Override
       
   359     public final ByteBuffer getBuffer() {
       
   360         return client.getBuffer();
       
   361     }
       
   362 
       
   363     @Override
       
   364     public final void returnBuffer(ByteBuffer buffer) {
       
   365         client.returnBuffer(buffer);
       
   366     }
       
   367 }