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 }