1 /* 2 * Copyright (c) 2011, 2014, 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 com.sun.webkit.network; 27 28 import static com.sun.webkit.network.URLs.newURL; 29 30 import java.net.MalformedURLException; 31 import java.security.AccessController; 32 import java.security.PrivilegedAction; 33 import java.util.Arrays; 34 import java.util.concurrent.LinkedBlockingQueue; 35 import java.util.concurrent.ThreadFactory; 36 import java.util.concurrent.ThreadPoolExecutor; 37 import java.util.concurrent.TimeUnit; 38 import java.util.concurrent.atomic.AtomicInteger; 39 import java.util.logging.Level; 40 import java.util.logging.Logger; 41 42 import com.sun.webkit.WebPage; 43 import java.security.Permission; 44 45 final class NetworkContext { 46 47 private static final Logger logger = 48 Logger.getLogger(NetworkContext.class.getName()); 49 50 /** 51 * The size of the thread pool for asynchronous loaders. 52 */ 53 private static final int THREAD_POOL_SIZE = 20; 54 55 /** 56 * The thread pool keep alive time. 57 */ 58 private static final long THREAD_POOL_KEEP_ALIVE_TIME = 10000L; 59 60 /** 61 * The default value of the "http.maxConnections" system property. 62 */ 63 private static final int DEFAULT_HTTP_MAX_CONNECTIONS = 5; 64 65 /** 66 * The buffer size for the shared pool of byte buffers. 67 */ 68 private static final int BYTE_BUFFER_SIZE = 1024 * 40; 69 70 /** 71 * The thread pool used to execute asynchronous loaders. 72 */ 73 private static final ThreadPoolExecutor threadPool; 74 static { 75 threadPool = new ThreadPoolExecutor( 76 THREAD_POOL_SIZE, 77 THREAD_POOL_SIZE, 78 THREAD_POOL_KEEP_ALIVE_TIME, 79 TimeUnit.MILLISECONDS, 80 new LinkedBlockingQueue<Runnable>(), 81 new URLLoaderThreadFactory()); 82 threadPool.allowCoreThreadTimeOut(true); 83 } 84 85 /** 86 * The shared pool of byte buffers. 87 */ 88 private static final ByteBufferPool byteBufferPool = 89 ByteBufferPool.newInstance(BYTE_BUFFER_SIZE); 90 91 92 /** 93 * Non-invocable constructor. 94 */ 95 private NetworkContext() { 96 throw new AssertionError(); 97 } 98 99 100 /** 101 * Checks whether a URL is valid or not. I.E. if we do have a protocol 102 * handler to deal with it. 103 * 104 * @param url the <code>String</code> containing the url to check. 105 * @return <code>true</code> if we can handle the url. <code>false</code> 106 * otherwise. 107 */ 108 private static boolean canHandleURL(String url) { 109 java.net.URL u = null; 110 try { 111 u = newURL(url); 112 } catch (MalformedURLException malformedURLException) { 113 } 114 return u != null; 115 } 116 117 /** 118 * Starts an asynchronous load or executes a synchronous one. 119 */ 120 private static URLLoader fwkLoad(WebPage webPage, 121 boolean asynchronous, 122 String url, 123 String method, 124 String headers, 125 FormDataElement[] formDataElements, 126 long data) 127 { 128 if (logger.isLoggable(Level.FINEST)) { 129 logger.log(Level.FINEST, String.format( 130 "webPage: [%s], " + 131 "asynchronous: [%s], " + 132 "url: [%s], " + 133 "method: [%s], " + 134 "formDataElements: %s, " + 135 "data: [0x%016X], " + 136 "headers:%n%s", 137 webPage, 138 asynchronous, 139 url, 140 method, 141 formDataElements != null 142 ? Arrays.asList(formDataElements) : "[null]", 143 data, 144 Util.formatHeaders(headers))); 145 } 146 URLLoader loader = new URLLoader( 147 webPage, 148 byteBufferPool, 149 asynchronous, 150 url, 151 method, 152 headers, 153 formDataElements, 154 data); 155 if (asynchronous) { 156 threadPool.submit(loader); 157 if (logger.isLoggable(Level.FINEST)) { 158 logger.log(Level.FINEST, 159 "active count: [{0}], " + 160 "pool size: [{1}], " + 161 "max pool size: [{2}], " + 162 "task count: [{3}], " + 163 "completed task count: [{4}]", 164 new Object[] { 165 threadPool.getActiveCount(), 166 threadPool.getPoolSize(), 167 threadPool.getMaximumPoolSize(), 168 threadPool.getTaskCount(), 169 threadPool.getCompletedTaskCount()}); 170 } 171 return loader; 172 } else { 173 loader.run(); 174 return null; 175 } 176 } 177 178 /** 179 * Returns the maximum allowed number of connections per host. 180 */ 181 private static int fwkGetMaximumHTTPConnectionCountPerHost() { 182 // Our implementation employs HttpURLConnection for all 183 // HTTP exchanges, so return the value of the "http.maxConnections" 184 // system property. 185 int propValue = AccessController.doPrivileged( 186 (PrivilegedAction<Integer>) () -> Integer.getInteger("http.maxConnections", -1)); 187 return propValue >= 0 ? propValue : DEFAULT_HTTP_MAX_CONNECTIONS; 188 } 189 190 /** 191 * Thread factory for URL loader threads. 192 */ 193 private static final class URLLoaderThreadFactory implements ThreadFactory { 194 private final ThreadGroup group; 195 private final AtomicInteger index = new AtomicInteger(1); 196 197 // Need to assert the modifyThread and modifyThreadGroup permission when 198 // creating the thread from the URLLoaderThreadFactory, so we can 199 // create the thread with the desired ThreadGroup. 200 // Note that this is needed when running as an applet or a web start app. 201 private static final Permission modifyThreadGroupPerm = new RuntimePermission("modifyThreadGroup"); 202 private static final Permission modifyThreadPerm = new RuntimePermission("modifyThread"); 203 204 private URLLoaderThreadFactory() { 205 SecurityManager sm = System.getSecurityManager(); 206 group = (sm != null) ? sm.getThreadGroup() 207 : Thread.currentThread().getThreadGroup(); 208 } 209 210 @Override 211 public Thread newThread(Runnable r) { 212 // Assert the modifyThread and modifyThreadGroup permissions 213 return 214 AccessController.doPrivileged((PrivilegedAction<Thread>) () -> { 215 Thread t = new Thread(group, r, 216 "URL-Loader-" + index.getAndIncrement()); 217 t.setDaemon(true); 218 if (t.getPriority() != Thread.NORM_PRIORITY) { 219 t.setPriority(Thread.NORM_PRIORITY); 220 } 221 return t; 222 }, 223 null, 224 modifyThreadGroupPerm, modifyThreadPerm); 225 } 226 } 227 }