< prev index next >

src/java.httpclient/share/classes/java/net/http/Http2ClientImpl.java

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -21,13 +21,144 @@
  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  * or visit www.oracle.com if you need additional information or have any
  */
 package java.net.http;
 
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import static java.net.http.SettingsFrame.INITIAL_WINDOW_SIZE;
+import static java.net.http.SettingsFrame.ENABLE_PUSH;
+import static java.net.http.SettingsFrame.HEADER_TABLE_SIZE;
+import static java.net.http.SettingsFrame.MAX_CONCURRENT_STREAMS;
+import static java.net.http.SettingsFrame.MAX_FRAME_SIZE;
+import java.util.Base64;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ *  Http2 specific aspects of HttpClientImpl
+ */
 class Http2ClientImpl {
-    Http2ClientImpl(HttpClientImpl t) {}
-    String getSettingsString() {return "";}
-    void debugPrint() {}
-    Http2Connection getConnectionFor(HttpRequestImpl r) {
+
+    HttpClientImpl client;
+
+    Http2ClientImpl(HttpClientImpl client) {
+        this.client = client;
+    }
+
+    /* Map key is "scheme:host:port" */
+    final Map<String,Http2Connection> connections =
+            Collections.synchronizedMap(new HashMap<>());
+
+    final Set<String> opening = Collections.synchronizedSet(new HashSet<>());
+
+    synchronized boolean haveConnectionFor(URI uri, InetSocketAddress proxy) {
+        return connections.containsKey(Http2Connection.keyFor(uri,proxy));
+    }
+
+    /*
+     * Only one connection per destination is created. Blocks when opening
+     * connection, or when waiting for connection to be opened.
+     * First thread opens the connection and notifies the others when done.
+     *
+     * If the request is secure (https) then we open the connection here.
+     * If not, then the more complicated upgrade from 1.1 to 2 happens (not here)
+     * In latter case, when the Http2Connection is connected, putConnection() must
+     * be called to store it.
+     */
+    Http2Connection getConnectionFor(HttpRequestImpl req)
+        throws IOException, InterruptedException
+    {
+        URI uri = req.uri();
+        InetSocketAddress proxy = req.proxy();
+        String key = Http2Connection.keyFor(uri, proxy);
+        Http2Connection connection;
+        boolean createHere; // true if this thread is going to open connection
+        synchronized (opening) {
+            connection = connections.get(key);
+            if (connection != null) {
+                return connection;
+            }
+
+            if (!req.secure()) {
         return null;
     }
+
+            if (!opening.contains(key)) {
+                opening.add(key);
+                createHere = true;
+            } else {
+                createHere = false;
+                opening.wait();
+            }
+        }
+        if (createHere) {
+            connection = new Http2Connection(req);
+            synchronized(opening) {
+                connections.put(key, connection);
+                opening.remove(key);
+                opening.notifyAll();
+            }
+        }
+        return connection;
+    }
+
+    /*
+     * TODO: If there isn't a connection to the same destination, then
+     * store it. If there is already a connection, then close it
+     */
+    synchronized void putConnection(Http2Connection c) {
+        String key = c.key();
+        connections.put(key, c);
+    }
+
+    synchronized void deleteConnection(Http2Connection c) {
+        String key = c.key();
+        connections.remove(key);
+    }
+
+    /** Returns the client settings as a base64 (url) encoded string */
+    String getSettingsString() {
+        SettingsFrame sf = getClientSettings();
+        ByteBufferGenerator bg = new ByteBufferGenerator(client);
+        sf.writeOutgoing(bg);
+        byte[] settings = bg.asByteArray(9); // without the header
+        Base64.Encoder encoder = Base64.getUrlEncoder()
+                                       .withoutPadding();
+        return encoder.encodeToString(settings);
+    }
+
+    private static final int K = 1024;
+
+    SettingsFrame getClientSettings() {
+        SettingsFrame frame = new SettingsFrame();
+        frame.setParameter(HEADER_TABLE_SIZE, Utils.getIntegerNetProperty(
+            "sun.net.httpclient.hpack.maxheadertablesize", 16 * K));
+        frame.setParameter(ENABLE_PUSH, Utils.getIntegerNetProperty(
+            "sun.net.httpclient.enablepush", 1));
+        frame.setParameter(MAX_CONCURRENT_STREAMS, Utils.getIntegerNetProperty(
+            "sun.net.httpclient.maxstreams", 16));
+        frame.setParameter(INITIAL_WINDOW_SIZE, Utils.getIntegerNetProperty(
+            "sun.net.httpclient.windowsize", 32 * K));
+        frame.setParameter(MAX_FRAME_SIZE, Utils.getIntegerNetProperty(
+            "sun.net.httpclient.maxframesize", 16 * K));
+        frame.setLength();
+        return frame;
+    }
+
+    // these default settings only used until first Settings frame received
+    // from server.
+
+    SettingsFrame getServerSettings() {
+        SettingsFrame frame = new SettingsFrame();
+        frame.setParameter(HEADER_TABLE_SIZE, 4 * K);
+        frame.setParameter(ENABLE_PUSH, 0);
+        frame.setParameter(MAX_CONCURRENT_STREAMS, 8);
+        frame.setParameter(INITIAL_WINDOW_SIZE, 64 * K - 1);
+        frame.setParameter(MAX_FRAME_SIZE, 8 * K);
+        return frame;
+    }
 }
< prev index next >