Index: trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadSessionTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadSessionTask.java	(revision 9170)
+++ trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadSessionTask.java	(revision 9171)
@@ -13,5 +13,5 @@
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-import org.openstreetmap.josm.tools.Utils;
+import org.openstreetmap.josm.tools.HttpClient;
 
 /**
@@ -45,5 +45,5 @@
             try {
                 URL u = new URL(url);
-                loader = new Loader(Utils.openURL(u), u.toURI(), url.endsWith(".joz"));
+                loader = new Loader(HttpClient.create(u).connect().getContent(), u.toURI(), url.endsWith(".joz"));
                 return Main.worker.submit(loader);
             } catch (URISyntaxException | IOException e) {
Index: trunk/src/org/openstreetmap/josm/data/imagery/CachedAttributionBingAerialTileSource.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/imagery/CachedAttributionBingAerialTileSource.java	(revision 9170)
+++ trunk/src/org/openstreetmap/josm/data/imagery/CachedAttributionBingAerialTileSource.java	(revision 9171)
@@ -6,5 +6,4 @@
 import java.net.URL;
 import java.util.List;
-import java.util.Scanner;
 import java.util.concurrent.Callable;
 
@@ -14,6 +13,5 @@
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.io.CacheCustomContent;
-import org.openstreetmap.josm.io.UTFInputStreamReader;
-import org.openstreetmap.josm.tools.Utils;
+import org.openstreetmap.josm.tools.HttpClient;
 import org.xml.sax.InputSource;
 
@@ -55,9 +53,7 @@
         protected byte[] updateData() throws IOException {
             URL u = getAttributionUrl();
-            try (Scanner scanner = new Scanner(UTFInputStreamReader.create(Utils.openURL(u)))) {
-                String r = scanner.useDelimiter("\\A").next();
-                Main.info("Successfully loaded Bing attribution data.");
-                return r.getBytes("UTF-8");
-            }
+            final String r = HttpClient.create(u).connect().fetchContent();
+            Main.info("Successfully loaded Bing attribution data.");
+            return r.getBytes("UTF-8");
         }
     }
Index: trunk/src/org/openstreetmap/josm/gui/MainApplication.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 9170)
+++ trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 9171)
@@ -65,4 +65,5 @@
 import org.openstreetmap.josm.tools.BugReportExceptionHandler;
 import org.openstreetmap.josm.tools.FontsManager;
+import org.openstreetmap.josm.tools.HttpClient;
 import org.openstreetmap.josm.tools.I18n;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -392,5 +393,5 @@
             for (String i : args.get(Option.LOAD_PREFERENCES)) {
                 info("Reading preferences from " + i);
-                try (InputStream is = Utils.openURL(new URL(i))) {
+                try (InputStream is = HttpClient.create(new URL(i)).connect().getContent()) {
                     config.openAndReadXML(is);
                 } catch (Exception ex) {
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java	(revision 9170)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java	(revision 9171)
@@ -16,5 +16,4 @@
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
-import java.net.HttpURLConnection;
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -101,4 +100,5 @@
 import org.openstreetmap.josm.tools.AlphanumComparator;
 import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.HttpClient;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.InputMapUtils;
@@ -1171,22 +1171,20 @@
                         try {
                             // find a page that actually exists in the wiki
-                            HttpURLConnection conn;
+                            HttpClient.Response conn;
                             for (URI u : uris) {
-                                conn = Utils.openHttpConnection(u.toURL());
-                                conn.setConnectTimeout(Main.pref.getInteger("socket.timeout.connect", 15)*1000);
+                                conn = HttpClient.create(u.toURL(), "HEAD").connect();
 
                                 if (conn.getResponseCode() != 200) {
-                                    Main.info("{0} does not exist", u);
                                     conn.disconnect();
                                 } else {
-                                    int osize = conn.getContentLength();
+                                    long osize = conn.getContentLength();
                                     if (osize > -1) {
                                         conn.disconnect();
 
-                                        conn = Utils.openHttpConnection(new URI(u.toString()
+                                        final URI newURI = new URI(u.toString()
                                                 .replace("=", "%3D") /* do not URLencode whole string! */
                                                 .replaceFirst("/wiki/", "/w/index.php?redirect=no&title=")
-                                                ).toURL());
-                                        conn.setConnectTimeout(Main.pref.getInteger("socket.timeout.connect", 15)*1000);
+                                        );
+                                        conn = HttpClient.create(newURI.toURL(), "HEAD").connect();
                                     }
 
@@ -1199,5 +1197,4 @@
                                         conn.disconnect();
                                     } else {
-                                        Main.info("browsing to {0}", u);
                                         conn.disconnect();
 
Index: trunk/src/org/openstreetmap/josm/gui/download/PlaceSelection.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/download/PlaceSelection.java	(revision 9170)
+++ trunk/src/org/openstreetmap/josm/gui/download/PlaceSelection.java	(revision 9171)
@@ -13,10 +13,6 @@
 import java.awt.event.MouseEvent;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.io.Reader;
-import java.net.HttpURLConnection;
 import java.net.URL;
-import java.nio.charset.StandardCharsets;
 import java.text.DecimalFormat;
 import java.util.ArrayList;
@@ -57,4 +53,5 @@
 import org.openstreetmap.josm.io.OsmTransferException;
 import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.HttpClient;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.OsmUrlToBounds;
@@ -328,5 +325,5 @@
 
         private final String searchExpression;
-        private HttpURLConnection connection;
+        private HttpClient.Response connection;
         private List<SearchResult> data;
         private boolean canceled;
@@ -371,11 +368,7 @@
                 URL url = new URL(urlString);
                 synchronized (this) {
-                    connection = Utils.openHttpConnection(url);
-                }
-                connection.setConnectTimeout(Main.pref.getInteger("socket.timeout.connect", 15)*1000);
-                try (
-                    InputStream inputStream = connection.getInputStream();
-                    Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
-                ) {
+                    connection = HttpClient.create(url).connect();
+                }
+                try (Reader reader = connection.getContentReader()) {
                     InputSource inputSource = new InputSource(reader);
                     NameFinderResultParser parser = new NameFinderResultParser();
Index: trunk/src/org/openstreetmap/josm/gui/io/DownloadFileTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/DownloadFileTask.java	(revision 9170)
+++ trunk/src/org/openstreetmap/josm/gui/io/DownloadFileTask.java	(revision 9171)
@@ -11,5 +11,4 @@
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -22,5 +21,5 @@
 import org.openstreetmap.josm.gui.PleaseWaitDialog;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
-import org.openstreetmap.josm.tools.Utils;
+import org.openstreetmap.josm.tools.HttpClient;
 import org.xml.sax.SAXException;
 
@@ -68,5 +67,5 @@
 
     private boolean canceled;
-    private HttpURLConnection downloadConnection;
+    private HttpClient.Response downloadConnection;
 
     private synchronized void closeConnectionIfNeeded() {
@@ -100,9 +99,7 @@
 
             URL url = new URL(address);
-            int size;
+            long size;
             synchronized (this) {
-                downloadConnection = Utils.openHttpConnection(url);
-                downloadConnection.setRequestProperty("Cache-Control", "no-cache");
-                downloadConnection.connect();
+                downloadConnection = HttpClient.create(url).useCache(false).connect();
                 size = downloadConnection.getContentLength();
             }
@@ -112,10 +109,10 @@
 
             try (
-                InputStream in = downloadConnection.getInputStream();
+                InputStream in = downloadConnection.getContent();
                 OutputStream out = new FileOutputStream(file)
             ) {
                 byte[] buffer = new byte[32768];
                 int count = 0;
-                int p1 = 0, p2 = 0;
+                long p1 = 0, p2 = 0;
                 for (int read = in.read(buffer); read != -1; read = in.read(buffer)) {
                     out.write(buffer, 0, read);
@@ -124,5 +121,5 @@
                     p2 = 100 * count / size;
                     if (p2 != p1) {
-                        progressMonitor.setTicks(p2);
+                        progressMonitor.setTicks((int) p2);
                         p1 = p2;
                     }
Index: trunk/src/org/openstreetmap/josm/io/imagery/WMSImagery.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/imagery/WMSImagery.java	(revision 9170)
+++ trunk/src/org/openstreetmap/josm/io/imagery/WMSImagery.java	(revision 9171)
@@ -3,11 +3,8 @@
 
 import java.awt.HeadlessException;
-import java.io.BufferedReader;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.StringReader;
 import java.net.MalformedURLException;
 import java.net.URL;
-import java.net.URLConnection;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -27,5 +24,5 @@
 import org.openstreetmap.josm.data.imagery.ImageryInfo;
 import org.openstreetmap.josm.data.projection.Projections;
-import org.openstreetmap.josm.io.UTFInputStreamReader;
+import org.openstreetmap.josm.tools.HttpClient;
 import org.openstreetmap.josm.tools.Predicate;
 import org.openstreetmap.josm.tools.Utils;
@@ -137,18 +134,5 @@
 
         Main.info("GET " + getCapabilitiesUrl);
-        URLConnection openConnection = Utils.openHttpConnection(getCapabilitiesUrl, false, true);
-        StringBuilder ba = new StringBuilder();
-
-        try (
-            InputStream inputStream = openConnection.getInputStream();
-            BufferedReader br = new BufferedReader(UTFInputStreamReader.create(inputStream))
-        ) {
-            String line;
-            while ((line = br.readLine()) != null) {
-                ba.append(line);
-                ba.append('\n');
-            }
-        }
-        String incomingData = ba.toString();
+        final String incomingData = HttpClient.create(getCapabilitiesUrl).connect().fetchContent();
         Main.debug("Server response to Capabilities request:");
         Main.debug(incomingData);
Index: trunk/src/org/openstreetmap/josm/plugins/ReadRemotePluginInformationTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/plugins/ReadRemotePluginInformationTask.java	(revision 9170)
+++ trunk/src/org/openstreetmap/josm/plugins/ReadRemotePluginInformationTask.java	(revision 9171)
@@ -6,5 +6,4 @@
 import java.awt.Dimension;
 import java.awt.GridBagLayout;
-import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.File;
@@ -13,8 +12,6 @@
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
-import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -42,4 +39,5 @@
 import org.openstreetmap.josm.io.OsmTransferException;
 import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.HttpClient;
 import org.openstreetmap.josm.tools.Utils;
 import org.xml.sax.SAXException;
@@ -53,5 +51,5 @@
     private Collection<String> sites;
     private boolean canceled;
-    private HttpURLConnection connection;
+    private HttpClient.Response connection;
     private List<PluginInformation> availablePlugins;
     private boolean displayErrMsg;
@@ -153,4 +151,5 @@
         }
 
+        String content = null;
         try {
             monitor.beginTask("");
@@ -158,17 +157,10 @@
 
             URL url = new URL(site);
-            synchronized (this) {
-                connection = Utils.openHttpConnection(url);
-                connection.setRequestProperty("Cache-Control", "no-cache");
-                connection.setRequestProperty("Accept-Charset", "utf-8");
-            }
-            try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
-                StringBuilder sb = new StringBuilder();
-                String line;
-                while ((line = in.readLine()) != null) {
-                    sb.append(line).append('\n');
-                }
-                return sb.toString();
-            }
+            connection = HttpClient.create(url).useCache(false).connect();
+            content = connection.fetchContent();
+            if (connection.getResponseCode() != 200) {
+                throw new IOException(tr("Unsuccessful HTTP request"));
+            }
+            return content;
         } catch (MalformedURLException e) {
             if (canceled) return null;
@@ -177,6 +169,5 @@
         } catch (IOException e) {
             if (canceled) return null;
-            Main.addNetworkError(site, e);
-            handleIOException(monitor, e, tr("Plugin list download error"), tr("JOSM failed to download plugin list:"), displayErrMsg);
+            handleIOException(monitor, e, content);
             return null;
         } finally {
@@ -191,24 +182,6 @@
     }
 
-    private void handleIOException(final ProgressMonitor monitor, IOException e, final String title, final String firstMessage,
-            boolean displayMsg) {
-        StringBuilder sb = new StringBuilder();
-        try (InputStream errStream = connection.getErrorStream()) {
-            if (errStream != null) {
-                try (BufferedReader err = new BufferedReader(new InputStreamReader(errStream, StandardCharsets.UTF_8))) {
-                    String line;
-                    while ((line = err.readLine()) != null) {
-                        sb.append(line).append('\n');
-                    }
-                } catch (Exception ex) {
-                    Main.error(e);
-                    Main.error(ex);
-                }
-            }
-        } catch (IOException ex) {
-            Main.warn(ex);
-        }
+    private void handleIOException(final ProgressMonitor monitor, IOException e, String details) {
         final String msg = e.getMessage();
-        final String details = sb.toString();
         if (details.isEmpty()) {
             Main.error(e.getClass().getSimpleName()+": " + msg);
@@ -217,6 +190,6 @@
         }
 
-        if (displayMsg) {
-            displayErrorMessage(monitor, msg, details, title, firstMessage);
+        if (displayErrMsg) {
+            displayErrorMessage(monitor, msg, details, tr("Plugin list download error"), tr("JOSM failed to download plugin list:"));
         }
     }
Index: trunk/src/org/openstreetmap/josm/tools/HttpClient.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/HttpClient.java	(revision 9170)
+++ trunk/src/org/openstreetmap/josm/tools/HttpClient.java	(revision 9171)
@@ -8,10 +8,9 @@
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.net.HttpURLConnection;
 import java.net.URL;
-import java.nio.charset.StandardCharsets;
 import java.util.Map;
+import java.util.Scanner;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.zip.GZIPInputStream;
@@ -20,4 +19,5 @@
 import org.openstreetmap.josm.data.Version;
 import org.openstreetmap.josm.io.Compression;
+import org.openstreetmap.josm.io.UTFInputStreamReader;
 
 /**
@@ -38,4 +38,6 @@
     private final Map<String, String> headers = new ConcurrentHashMap<>();
     private int maxRedirects = Main.pref.getInteger("socket.maxredirects", 5);
+    private boolean useCache;
+    private boolean keepAlive;
 
     private HttpClient(URL url, String requestMethod) {
@@ -70,4 +72,11 @@
             connection.setIfModifiedSince(ifModifiedSince);
         }
+        connection.setUseCaches(useCache);
+        if (!useCache) {
+            connection.setRequestProperty("Cache-Control", "no-cache");
+        }
+        if (!keepAlive) {
+            connection.setRequestProperty("Connection", "close");
+        }
         for (Map.Entry<String, String> header : headers.entrySet()) {
             connection.setRequestProperty(header.getKey(), header.getValue());
@@ -78,4 +87,5 @@
             try {
                 connection.connect();
+                Main.info("{0} {1} => {2}", requestMethod, url, connection.getResponseCode());
             } catch (IOException e) {
                 //noinspection ThrowableResultOfMethodCallIgnored
@@ -157,8 +167,23 @@
 
         /**
-         * Returns {@link #getContent()} wrapped in a buffered reader
+         * Returns {@link #getContent()} wrapped in a buffered reader.
+         *
+         * Detects Unicode charset in use utilizing {@link UTFInputStreamReader}.
          */
         public BufferedReader getContentReader() throws IOException {
-            return new BufferedReader(new InputStreamReader(getContent(), StandardCharsets.UTF_8));
+            return new BufferedReader(
+                    UTFInputStreamReader.create(getContent())
+            );
+        }
+
+        /**
+         * Fetches the HTTP response as String.
+         * @return the response
+         * @throws IOException
+         */
+        public String fetchContent() throws IOException {
+            try (Scanner scanner = new Scanner(getContentReader())) {
+                return scanner.useDelimiter("\\A").next();
+            }
         }
 
@@ -187,4 +212,11 @@
 
         /**
+         * Returns the {@code Content-Length} header.
+         */
+        public long getContentLength() {
+            return connection.getContentLengthLong();
+        }
+
+        /**
          * @see HttpURLConnection#disconnect()
          */
@@ -213,4 +245,29 @@
     public static HttpClient create(URL url, String requestMethod) {
         return new HttpClient(url, requestMethod);
+    }
+
+    /**
+     * Sets whether not to set header {@code Cache-Control=no-cache}
+     *
+     * @param useCache whether not to set header {@code Cache-Control=no-cache}
+     * @return {@code this}
+     * @see HttpURLConnection#setUseCaches(boolean)
+     */
+    public HttpClient useCache(boolean useCache) {
+        this.useCache = useCache;
+        return this;
+    }
+
+    /**
+     * Sets whether not to set header {@code Connection=close}
+     * <p/>
+     * This might fix #7640, see <a href='https://web.archive.org/web/20140118201501/http://www.tikalk.com/java/forums/httpurlconnection-disable-keep-alive'>here</a>.
+     *
+     * @param keepAlive whether not to set header {@code Connection=close}
+     * @return {@code this}
+     */
+    public HttpClient keepAlive(boolean keepAlive) {
+        this.keepAlive = keepAlive;
+        return this;
     }
 
