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 * questions. 24 */ 25 26 package java.net.http; 27 28 import java.io.IOException; 29 import java.net.InetSocketAddress; 30 import java.net.URI; 31 import static java.net.http.SettingsFrame.INITIAL_WINDOW_SIZE; 32 import static java.net.http.SettingsFrame.ENABLE_PUSH; 33 import static java.net.http.SettingsFrame.HEADER_TABLE_SIZE; 34 import static java.net.http.SettingsFrame.MAX_CONCURRENT_STREAMS; 35 import static java.net.http.SettingsFrame.MAX_FRAME_SIZE; 36 import java.util.Base64; 37 import java.util.Collections; 38 import java.util.HashMap; 39 import java.util.HashSet; 40 import java.util.Map; 41 import java.util.Set; 42 43 /** 44 * Http2 specific aspects of HttpClientImpl 45 */ 46 class Http2ClientImpl { 47 48 final private HttpClientImpl client; 49 50 Http2ClientImpl(HttpClientImpl client) { 51 this.client = client; 52 } 53 54 /* Map key is "scheme:host:port" */ 55 final private Map<String,Http2Connection> connections = 56 Collections.synchronizedMap(new HashMap<>()); 57 58 final private Set<String> opening = Collections.synchronizedSet(new HashSet<>()); 59 60 synchronized boolean haveConnectionFor(URI uri, InetSocketAddress proxy) { 61 return connections.containsKey(Http2Connection.keyFor(uri,proxy)); 62 } 63 64 /** 65 * If a https request then blocks and waits until a connection is opened. 66 * Returns null if the request is 'http' as a different (upgrade) 67 * mechanism is used. 68 * 69 * Only one connection per destination is created. Blocks when opening 70 * connection, or when waiting for connection to be opened. 71 * First thread opens the connection and notifies the others when done. 72 * 73 * If the request is secure (https) then we open the connection here. 74 * If not, then the more complicated upgrade from 1.1 to 2 happens (not here) 75 * In latter case, when the Http2Connection is connected, putConnection() must 76 * be called to store it. 77 */ 78 Http2Connection getConnectionFor(HttpRequestImpl req) 79 throws IOException, InterruptedException { 80 URI uri = req.uri(); 81 InetSocketAddress proxy = req.proxy(); 82 String key = Http2Connection.keyFor(uri, proxy); 83 Http2Connection connection; 84 synchronized (opening) { 85 while ((connection = connections.get(key)) == null) { 86 if (!req.secure()) { 87 return null; 88 } 89 if (!opening.contains(key)) { 90 opening.add(key); 91 break; 92 } else { 93 opening.wait(); 94 } 95 } 96 } 97 if (connection != null) { 98 return connection; 99 } 100 // we are opening the connection here blocking until it is done. 101 connection = new Http2Connection(req); 102 synchronized (opening) { 103 connections.put(key, connection); 104 opening.remove(key); 105 opening.notifyAll(); 106 } 107 return connection; 108 } 109 110 111 /* 112 * TODO: If there isn't a connection to the same destination, then 113 * store it. If there is already a connection, then close it 114 */ 115 synchronized void putConnection(Http2Connection c) { 116 String key = c.key(); 117 connections.put(key, c); 118 } 119 120 synchronized void deleteConnection(Http2Connection c) { 121 String key = c.key(); 122 connections.remove(key); 123 } 124 125 HttpClientImpl client() { 126 return client; 127 } 128 129 /** Returns the client settings as a base64 (url) encoded string */ 130 String getSettingsString() { 131 SettingsFrame sf = getClientSettings(); 132 ByteBufferGenerator bg = new ByteBufferGenerator(client); 133 sf.writeOutgoing(bg); 134 byte[] settings = bg.asByteArray(9); // without the header 135 Base64.Encoder encoder = Base64.getUrlEncoder() 136 .withoutPadding(); 137 return encoder.encodeToString(settings); 138 } 139 140 private static final int K = 1024; 141 142 SettingsFrame getClientSettings() { 143 SettingsFrame frame = new SettingsFrame(); 144 frame.setParameter(HEADER_TABLE_SIZE, Utils.getIntegerNetProperty( 145 "java.net.httpclient.hpack.maxheadertablesize", 16 * K)); 146 frame.setParameter(ENABLE_PUSH, Utils.getIntegerNetProperty( 147 "java.net.httpclient.enablepush", 1)); 148 frame.setParameter(MAX_CONCURRENT_STREAMS, Utils.getIntegerNetProperty( 149 "java.net.httpclient.maxstreams", 16)); 150 frame.setParameter(INITIAL_WINDOW_SIZE, Utils.getIntegerNetProperty( 151 "java.net.httpclient.windowsize", 32 * K)); 152 frame.setParameter(MAX_FRAME_SIZE, Utils.getIntegerNetProperty( 153 "java.net.httpclient.maxframesize", 16 * K)); 154 frame.computeLength(); 155 return frame; 156 } 157 }