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