6811297: Add more logging to HTTP protocol handler
authorjccollet
Thu, 25 Jun 2009 18:56:30 +0200
changeset 3059 2975ff687a6b
parent 3058 3849bdd1e8da
child 3060 4477a1762fa5
6811297: Add more logging to HTTP protocol handler Summary: Added extra logging to HttpURLConnection and HttpClient. Added a capture tool. Reviewed-by: chegar
jdk/make/sun/net/FILES_java.gmk
jdk/src/share/classes/sun/net/www/http/HttpCapture.java
jdk/src/share/classes/sun/net/www/http/HttpCaptureInputStream.java
jdk/src/share/classes/sun/net/www/http/HttpCaptureOutputStream.java
jdk/src/share/classes/sun/net/www/http/HttpClient.java
jdk/src/share/classes/sun/net/www/protocol/http/HttpLogFormatter.java
jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java
--- a/jdk/make/sun/net/FILES_java.gmk	Tue Jun 23 22:07:58 2009 -0700
+++ b/jdk/make/sun/net/FILES_java.gmk	Thu Jun 25 18:56:30 2009 +0200
@@ -66,6 +66,9 @@
 	sun/net/www/protocol/file/Handler.java \
 	sun/net/www/protocol/file/FileURLConnection.java \
 	sun/net/www/http/HttpClient.java \
+	sun/net/www/http/HttpCapture.java \
+	sun/net/www/http/HttpCaptureInputStream.java \
+	sun/net/www/http/HttpCaptureOutputStream.java \
 	sun/net/www/http/PosterOutputStream.java \
 	sun/net/www/http/ChunkedInputStream.java \
 	sun/net/www/http/ChunkedOutputStream.java \
@@ -75,6 +78,7 @@
 	sun/net/www/http/Hurryable.java \
 	sun/net/www/protocol/http/Handler.java \
 	sun/net/www/protocol/http/HttpURLConnection.java \
+	sun/net/www/protocol/http/HttpLogFormatter.java \
 	sun/net/www/protocol/http/HttpAuthenticator.java \
 	sun/net/www/protocol/http/AuthenticationHeader.java \
 	sun/net/www/protocol/http/AuthenticationInfo.java \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/net/www/http/HttpCapture.java	Thu Jun 25 18:56:30 2009 +0200
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.net.www.http;
+import java.io.*;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import sun.net.NetProperties;
+import java.util.regex.*;
+
+/**
+ * Main class of the HTTP traffic capture tool.
+ * Captures are triggered by the sun.net.http.captureRules system property.
+ * If set, it should point to a file containing the capture rules.
+ * Format for the file is simple:
+ * - 1 rule per line
+ * - Lines starting with a # are considered comments and ignored
+ * - a rule is a pair of a regular expression and file pattern, separated by a comma
+ * - The regular expression is applied to URLs, if it matches, the traffic for
+ *   that URL will be captured in the associated file.
+ * - if the file name contains a '%d', then that sequence will be replaced by a
+ *   unique random number for each URL. This allow for multi-threaded captures
+ *   of URLs matching the same pattern.
+ * - Rules are checked in sequence, in the same order as in the file, until a
+ *   match is found or the end of the list is reached.
+ *
+ * Examples of rules:
+ * www\.sun\.com , sun%d.log
+ * yahoo\.com\/.*asf , yahoo.log
+ *
+ * @author jccollet
+ */
+public class HttpCapture {
+    private File file = null;
+    private boolean incoming = true;
+    private BufferedWriter out = null;
+    private static boolean initialized = false;
+    private static volatile ArrayList<Pattern> patterns = null;
+    private static volatile ArrayList<String> capFiles = null;
+
+    private static synchronized void init() {
+        initialized = true;
+        String rulesFile = java.security.AccessController.doPrivileged(
+            new java.security.PrivilegedAction<String>() {
+                public String run() {
+                    return NetProperties.get("sun.net.http.captureRules");
+                }
+            });
+        if (rulesFile != null && !rulesFile.isEmpty()) {
+            BufferedReader in;
+            try {
+                in = new BufferedReader(new FileReader(rulesFile));
+            } catch (FileNotFoundException ex) {
+                return;
+            }
+            try {
+                String line = in.readLine();
+                while (line != null) {
+                    line = line.trim();
+                    if (!line.startsWith("#")) {
+                        // skip line if it's a comment
+                        String[] s = line.split(",");
+                        if (s.length == 2) {
+                            if (patterns == null) {
+                                patterns = new ArrayList<Pattern>();
+                                capFiles = new ArrayList<String>();
+                            }
+                            patterns.add(Pattern.compile(s[0].trim()));
+                            capFiles.add(s[1].trim());
+                        }
+                    }
+                    line = in.readLine();
+                }
+            } catch (IOException ioe) {
+
+            } finally {
+                try {
+                    in.close();
+                } catch (IOException ex) {
+                }
+            }
+        }
+    }
+
+    private static synchronized boolean isInitialized() {
+        return initialized;
+    }
+
+    private HttpCapture(File f, java.net.URL url) {
+        file = f;
+        try {
+            out = new BufferedWriter(new FileWriter(file, true));
+            out.write("URL: " + url + "\n");
+        } catch (IOException ex) {
+            Logger.getLogger(HttpCapture.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+
+    public synchronized void sent(int c) throws IOException {
+        if (incoming) {
+            out.write("\n------>\n");
+            incoming = false;
+            out.flush();
+        }
+        out.write(c);
+    }
+
+    public synchronized void received(int c) throws IOException {
+        if (!incoming) {
+            out.write("\n<------\n");
+            incoming = true;
+            out.flush();
+        }
+        out.write(c);
+    }
+
+    public synchronized void flush() throws IOException {
+        out.flush();
+    }
+
+    public static HttpCapture getCapture(java.net.URL url) {
+        if (!isInitialized()) {
+            init();
+        }
+        if (patterns == null || patterns.isEmpty()) {
+            return null;
+        }
+        String s = url.toString();
+        for (int i = 0; i < patterns.size(); i++) {
+            Pattern p = patterns.get(i);
+            if (p.matcher(s).find()) {
+                String f = capFiles.get(i);
+                File fi;
+                if (f.indexOf("%d") >= 0) {
+                    java.util.Random rand = new java.util.Random();
+                    do {
+                        String f2 = f.replace("%d", Integer.toString(rand.nextInt()));
+                        fi = new File(f2);
+                    } while (fi.exists());
+                } else {
+                    fi = new File(f);
+                }
+                return new HttpCapture(fi, url);
+            }
+        }
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/net/www/http/HttpCaptureInputStream.java	Thu Jun 25 18:56:30 2009 +0200
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.net.www.http;
+import java.io.*;
+
+/**
+ * A Simple FilterInputStream subclass to capture HTTP traffic.
+ * Every byte read is also passed to the HttpCapture class.
+ *
+ * @author jccollet
+ */
+public class HttpCaptureInputStream extends FilterInputStream {
+    private HttpCapture capture = null;
+
+    public HttpCaptureInputStream(InputStream in, HttpCapture cap) {
+        super(in);
+        capture = cap;
+    }
+
+    @Override
+    public int read() throws IOException {
+        int i = super.read();
+        capture.received(i);
+        return i;
+    }
+
+    @Override
+    public void close() throws IOException {
+        try {
+            capture.flush();
+        } catch (IOException iOException) {
+        }
+        super.close();
+    }
+
+    @Override
+    public int read(byte[] b) throws IOException {
+        int ret = super.read(b);
+        for (int i = 0; i < ret; i++) {
+            capture.received(b[i]);
+        }
+        return ret;
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        int ret = super.read(b, off, len);
+        for (int i = 0; i < ret; i++) {
+            capture.received(b[off+i]);
+        }
+        return ret;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/net/www/http/HttpCaptureOutputStream.java	Thu Jun 25 18:56:30 2009 +0200
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.net.www.http;
+import java.io.*;
+
+/**
+ * A Simple FilterOutputStream subclass to capture HTTP traffic.
+ * Every byte written is also passed to the HttpCapture class.
+ *
+ * @author jccollet
+ */
+public class HttpCaptureOutputStream extends FilterOutputStream {
+    private HttpCapture capture = null;
+
+    public HttpCaptureOutputStream(OutputStream out, HttpCapture cap) {
+        super(out);
+        capture = cap;
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+        capture.sent(b);
+        out.write(b);
+    }
+
+    @Override
+    public void write(byte[] ba) throws IOException {
+        for (byte b : ba) {
+            capture.sent(b);
+        }
+        out.write(ba);
+    }
+
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException {
+        for (int i = off; i < len; i++) {
+            capture.sent(b[i]);
+        }
+        out.write(b, off, len);
+    }
+
+    @Override
+    public void flush() throws IOException {
+        try {
+            capture.flush();
+        } catch (IOException iOException) {
+        }
+        super.flush();
+    }
+}
--- a/jdk/src/share/classes/sun/net/www/http/HttpClient.java	Tue Jun 23 22:07:58 2009 -0700
+++ b/jdk/src/share/classes/sun/net/www/http/HttpClient.java	Thu Jun 25 18:56:30 2009 +0200
@@ -27,6 +27,9 @@
 
 import java.io.*;
 import java.net.*;
+import java.util.Locale;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 import sun.net.NetworkClient;
 import sun.net.ProgressSource;
 import sun.net.www.MessageHeader;
@@ -34,7 +37,6 @@
 import sun.net.www.MeteredStream;
 import sun.net.www.ParseUtil;
 import sun.net.www.protocol.http.HttpURLConnection;
-import sun.misc.RegexpPool;
 
 /**
  * @author Herb Jellinek
@@ -64,6 +66,10 @@
     /** Default port number for http daemons. REMIND: make these private */
     static final int    httpPortNumber = 80;
 
+    // Use same logger as HttpURLConnection since we want to combine both event
+    // streams into one single HTTP log
+    private static Logger logger = Logger.getLogger("sun.net.www.protocol.http.HttpURLConnection");
+
     /** return default port number (subclasses may override) */
     protected int getDefaultPort () { return httpPortNumber; }
 
@@ -75,30 +81,6 @@
         return -1;
     }
 
-    /* The following three data members are left in for binary */
-    /* backwards-compatibility.  Unfortunately, HotJava sets them directly */
-    /* when it wants to change the settings.  The new design has us not */
-    /* cache these, so this is unnecessary, but eliminating the data members */
-    /* would break HJB 1.1 under JDK 1.2. */
-    /* */
-    /* These data members are not used, and their values are meaningless. */
-    /* REMIND:  Take them out for JDK 2.0! */
-    /**
-     * @deprecated
-     */
-    //    public static String proxyHost = null;
-    /**
-     * @deprecated
-     */
-    //    public static int proxyPort = 80;
-
-    /* instance-specific proxy fields override the static fields if set.
-     * Used by FTP.  These are set to the true proxy host/port if
-     * usingProxy is true.
-     */
-    //    private String instProxy = null;
-    //    private int instProxyPort = -1;
-
     /* All proxying (generic as well as instance-specific) may be
      * disabled through use of this flag
      */
@@ -141,6 +123,9 @@
     /* if set, the client will be reused and must not be put in cache */
     public boolean reuse = false;
 
+    // Traffic capture tool, if configured. See HttpCapture class for info
+     private HttpCapture capture = null;
+
     /**
      * A NOP method kept for backwards binary compatibility
      * @deprecated -- system properties are no longer cached.
@@ -226,6 +211,7 @@
                 }
             });
 
+        capture = HttpCapture.getCapture(url);
         openServer();
     }
 
@@ -300,8 +286,10 @@
                     // KeepAliveTimeout will get reset. We simply close the connection.
                     // This should be fine as it is very rare that a connection
                     // to the same host will not use the same proxy.
-                    ret.inCache = false;
-                    ret.closeServer();
+                    synchronized(ret) {
+                        ret.inCache = false;
+                        ret.closeServer();
+                    }
                     ret = null;
                 }
             }
@@ -369,7 +357,7 @@
         kac.put(url, null, this);
     }
 
-    protected boolean isInKeepAliveCache() {
+    protected synchronized boolean isInKeepAliveCache() {
         return inCache;
     }
 
@@ -389,11 +377,16 @@
      * method parseHTTP().  That's why this method is overidden from the
      * superclass.
      */
+    @Override
     public void openServer(String server, int port) throws IOException {
         serverSocket = doConnect(server, port);
         try {
+            OutputStream out = serverSocket.getOutputStream();
+            if (capture != null) {
+                out = new HttpCaptureOutputStream(out, capture);
+            }
             serverOutput = new PrintStream(
-                new BufferedOutputStream(serverSocket.getOutputStream()),
+                new BufferedOutputStream(out),
                                          false, encoding);
         } catch (UnsupportedEncodingException e) {
             throw new InternalError(encoding+" encoding not found");
@@ -412,7 +405,7 @@
     /*
      * Returns true if this httpclient is from cache
      */
-    public boolean isCachedConnection() {
+    public synchronized boolean isCachedConnection() {
         return cachedHttpClient;
     }
 
@@ -458,26 +451,6 @@
     }
 
     /*
-     * call super.openServer in a privileged block
-     */
-    private synchronized void privilegedSuperOpenServer(final String proxyHost,
-                                                        final int proxyPort)
-        throws IOException
-    {
-        try {
-            java.security.AccessController.doPrivileged(
-                new java.security.PrivilegedExceptionAction<Void>() {
-                    public Void run() throws IOException {
-                    superOpenServer(proxyHost, proxyPort);
-                    return null;
-                }
-            });
-        } catch (java.security.PrivilegedActionException pae) {
-            throw (IOException) pae.getException();
-        }
-    }
-
-    /*
      */
     protected synchronized void openServer() throws IOException {
 
@@ -490,8 +463,6 @@
             return;
         }
 
-        String urlHost = url.getHost().toLowerCase();
-
         if (url.getProtocol().equals("http") ||
             url.getProtocol().equals("https") ) {
 
@@ -595,6 +566,9 @@
 
         try {
             serverInput = serverSocket.getInputStream();
+            if (capture != null) {
+                serverInput = new HttpCaptureInputStream(serverInput, capture);
+            }
             serverInput = new BufferedInputStream(serverInput);
             return (parseHTTPHeader(responses, pi, httpuc));
         } catch (SocketTimeoutException stex) {
@@ -686,7 +660,7 @@
                 if (keep == null) {
                     keep = responses.findValue("Connection");
                 }
-                if (keep != null && keep.toLowerCase().equals("keep-alive")) {
+                if (keep != null && keep.toLowerCase(Locale.US).equals("keep-alive")) {
                     /* some servers, notably Apache1.1, send something like:
                      * "Keep-Alive: timeout=15, max=1" which we should respect.
                      */
@@ -767,10 +741,7 @@
          * the HTTP method and response code indicate there will be
          * no entity body to parse.
          */
-        String te = null;
-        try {
-            te = responses.findValue("Transfer-Encoding");
-        } catch (Exception e) {}
+        String te = responses.findValue("Transfer-Encoding");
         if (te != null && te.equalsIgnoreCase("chunked")) {
             serverInput = new ChunkedInputStream(serverInput, this, responses);
 
@@ -794,10 +765,14 @@
              * 2. "Not-Modified" or "No-Content" responses - RFC 2616 states that
              *    204 or 304 response must not include a message body.
              */
-            try {
-                cl = Long.parseLong(responses.findValue("content-length"));
-            } catch (Exception e) {}
-
+            String cls = responses.findValue("content-length");
+            if (cls != null) {
+                try {
+                    cl = Long.parseLong(cls);
+                } catch (NumberFormatException e) {
+                    cl = -1;
+                }
+            }
             String requestLine = requests.getKey(0);
 
             if ((requestLine != null &&
@@ -835,6 +810,9 @@
 
             if (isKeepingAlive())   {
                 // Wrap KeepAliveStream if keep alive is enabled.
+                if (logger.isLoggable(Level.FINEST)) {
+                    logger.finest("KeepAlive stream used: " + url);
+                }
                 serverInput = new KeepAliveStream(serverInput, pi, cl, this);
                 failedOnce = false;
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/net/www/protocol/http/HttpLogFormatter.java	Thu Jun 25 18:56:30 2009 +0200
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.net.www.protocol.http;
+
+import java.util.logging.LogRecord;
+import java.util.regex.*;
+
+/**
+ * A Formatter to make the HTTP logs a bit more palatable to the developer
+ * looking at them. The idea is to present the HTTP events in such a way that
+ * commands and headers are easily spotted (i.e. on separate lines).
+ * @author jccollet
+ */
+public class HttpLogFormatter extends java.util.logging.SimpleFormatter {
+    // Pattern for MessageHeader data. Mostly pairs within curly brackets
+    private static volatile Pattern pattern = null;
+    // Pattern for Cookies
+    private static volatile Pattern cpattern = null;
+
+    public HttpLogFormatter() {
+        if (pattern == null) {
+            pattern = Pattern.compile("\\{[^\\}]*\\}");
+            cpattern = Pattern.compile("[^,\\] ]{2,}");
+        }
+    }
+
+    @Override
+    public String format(LogRecord record) {
+        if (!"sun.net.www.protocol.http.HttpURLConnection".equalsIgnoreCase(record.getSourceClassName())
+                && !"sun.net.www.http.HttpClient".equalsIgnoreCase(record.getSourceClassName())) {
+            // Don't change format for stuff that doesn't concern us
+            return super.format(record);
+        }
+        String src = record.getMessage();
+        StringBuilder buf = new StringBuilder("HTTP: ");
+        if (src.startsWith("sun.net.www.MessageHeader@")) {
+            // MessageHeader logs are composed of pairs within curly brackets
+            // Let's extract them to make it more readable. That way we get one
+            // header pair (name, value) per line. A lot easier to read.
+            Matcher match = pattern.matcher(src);
+            while (match.find()) {
+                int i = match.start();
+                int j = match.end();
+                String s = src.substring(i + 1, j - 1);
+                if (s.startsWith("null: ")) {
+                    s = s.substring(6);
+                }
+                if (s.endsWith(": null")) {
+                    s = s.substring(0, s.length() - 6);
+                }
+                buf.append("\t").append(s).append("\n");
+            }
+        } else if (src.startsWith("Cookies retrieved: {")) {
+            // This comes from the Cookie handler, let's clean up the format a bit
+            String s = src.substring(20);
+            buf.append("Cookies from handler:\n");
+            while (s.length() >= 7) {
+                if (s.startsWith("Cookie=[")) {
+                    String s2 = s.substring(8);
+                    int c = s2.indexOf("Cookie2=[");
+                    if (c > 0) {
+                        s2 = s2.substring(0, c-1);
+                        s = s2.substring(c);
+                    } else {
+                        s = "";
+                    }
+                    if (s2.length() < 4) {
+                        continue;
+                    }
+                    Matcher m = cpattern.matcher(s2);
+                    while (m.find()) {
+                        int i = m.start();
+                        int j = m.end();
+                        if (i >= 0) {
+                            String cookie = s2.substring(i + 1, j > 0 ? j - 1 : s2.length() - 1);
+                            buf.append("\t").append(cookie).append("\n");
+                        }
+                    }
+                }
+                if (s.startsWith("Cookie2=[")) {
+                    String s2 = s.substring(9);
+                    int c = s2.indexOf("Cookie=[");
+                    if (c > 0) {
+                        s2 = s2.substring(0, c-1);
+                        s = s2.substring(c);
+                    } else {
+                        s = "";
+                    }
+                    Matcher m = cpattern.matcher(s2);
+                    while (m.find()) {
+                        int i = m.start();
+                        int j = m.end();
+                        if (i >= 0) {
+                            String cookie = s2.substring(i+1, j > 0 ? j-1 : s2.length() - 1);
+                            buf.append("\t").append(cookie).append("\n");
+                        }
+                    }
+                }
+            }
+        } else {
+            // Anything else we let as is.
+            buf.append(src).append("\n");
+        }
+        return buf.toString();
+    }
+
+}
--- a/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java	Tue Jun 23 22:07:58 2009 -0700
+++ b/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java	Thu Jun 25 18:56:30 2009 +0200
@@ -237,7 +237,6 @@
     /* try auth without calling Authenticator */
     private boolean tryTransparentNTLMServer = NTLMAuthentication.supportsTransparentAuth();
     private boolean tryTransparentNTLMProxy = NTLMAuthentication.supportsTransparentAuth();
-    Object authObj;
 
     /* Set if the user is manually setting the Authorization or Proxy-Authorization headers */
     boolean isUserServerAuth;
@@ -303,9 +302,16 @@
         return java.security.AccessController.doPrivileged(
             new java.security.PrivilegedAction<PasswordAuthentication>() {
                 public PasswordAuthentication run() {
-                    return Authenticator.requestPasswordAuthentication(
+                    if (logger.isLoggable(Level.FINEST)) {
+                        logger.finest("Requesting Authentication: host =" + host + " url = " + url);
+                    }
+                    PasswordAuthentication pass = Authenticator.requestPasswordAuthentication(
                         host, addr, port, protocol,
                         prompt, scheme, url, authType);
+                    if (pass != null && logger.isLoggable(Level.FINEST)) {
+                        logger.finest("Authentication returned: " + pass.toString());
+                    }
+                    return pass;
                 }
             });
     }
@@ -458,7 +464,7 @@
 
             setRequests=true;
         }
-        if(logger.isLoggable(Level.FINEST)) {
+        if (logger.isLoggable(Level.FINE)) {
             logger.fine(requests.toString());
         }
         http.writeRequests(requests, poster);
@@ -602,7 +608,7 @@
     {
         boolean redir;
         int redirects = 0;
-        InputStream in = null;
+        InputStream in;
 
         do {
             if (c instanceof HttpURLConnection) {
@@ -715,6 +721,12 @@
                         && !(cachedResponse instanceof SecureCacheResponse)) {
                         cachedResponse = null;
                     }
+                    if (logger.isLoggable(Level.FINEST)) {
+                        logger.finest("Cache Request for " + uri + " / " + getRequestMethod());
+                        if (cachedResponse != null) {
+                            logger.finest("From cache: "+cachedResponse.toString());
+                        }
+                    }
                     if (cachedResponse != null) {
                         cachedHeaders = mapToMessageHeader(cachedResponse.getHeaders());
                         cachedInputStream = cachedResponse.getBody();
@@ -750,10 +762,13 @@
                                      return ProxySelector.getDefault();
                                  }
                              });
-                Proxy p = null;
                 if (sel != null) {
                     URI uri = sun.net.www.ParseUtil.toURI(url);
+                    if (logger.isLoggable(Level.FINEST)) {
+                        logger.finest("ProxySelector Request for " + uri);
+                    }
                     Iterator<Proxy> it = sel.select(uri).iterator();
+                    Proxy p;
                     while (it.hasNext()) {
                         p = it.next();
                         try {
@@ -766,6 +781,11 @@
                                 http = getNewHttpClient(url, p, connectTimeout, false);
                                 http.setReadTimeout(readTimeout);
                             }
+                            if (logger.isLoggable(Level.FINEST)) {
+                                if (p != null) {
+                                    logger.finest("Proxy used: " + p.toString());
+                                }
+                            }
                             break;
                         } catch (IOException ioex) {
                             if (p != Proxy.NO_PROXY) {
@@ -993,10 +1013,16 @@
 
             URI uri = ParseUtil.toURI(url);
             if (uri != null) {
+                if (logger.isLoggable(Level.FINEST)) {
+                    logger.finest("CookieHandler request for " + uri);
+                }
                 Map<String, List<String>> cookies
                     = cookieHandler.get(
                         uri, requests.getHeaders(EXCLUDE_HEADERS));
                 if (!cookies.isEmpty()) {
+                    if (logger.isLoggable(Level.FINEST)) {
+                        logger.finest("Cookies retrieved: " + cookies.toString());
+                    }
                     for (Map.Entry<String, List<String>> entry :
                              cookies.entrySet()) {
                         String key = entry.getKey();
@@ -1126,7 +1152,7 @@
                     writeRequests();
                 }
                 http.parseHTTP(responses, pi, this);
-                if(logger.isLoggable(Level.FINEST)) {
+                if (logger.isLoggable(Level.FINE)) {
                     logger.fine(responses.toString());
                 }
                 inputStream = http.getInputStream();
@@ -1193,7 +1219,6 @@
                             disconnectInternal ();
                             throw new IOException ("Authentication failure");
                         }
-                        authObj = null;
                         doingNTLMp2ndStage = false;
                         continue;
                     }
@@ -1270,7 +1295,6 @@
                             throw new IOException ("Authentication failure");
                         }
                         doingNTLM2ndStage = false;
-                        authObj = null;
                         setCookieHeader();
                         continue;
                     }
@@ -1571,7 +1595,9 @@
                 http.parseHTTP(responses, null, this);
 
                 /* Log the response to the CONNECT */
-                logger.fine(responses.toString());
+                if (logger.isLoggable(Level.FINE)) {
+                    logger.fine(responses.toString());
+                }
 
                 statusLine = responses.getValue(0);
                 StringTokenizer st = new StringTokenizer(statusLine);
@@ -1617,12 +1643,9 @@
                         reset ();
                         if (!proxyAuthentication.setHeaders(this,
                                                 authhdr.headerParser(), raw)) {
-                            proxyHost = http.getProxyHostUsed();
-                            proxyPort = http.getProxyPortUsed();
                             disconnectInternal();
                             throw new IOException ("Authentication failure");
                         }
-                        authObj = null;
                         doingNTLMp2ndStage = false;
                         continue;
                     }
@@ -1699,7 +1722,9 @@
         setPreemptiveProxyAuthentication(requests);
 
          /* Log the CONNECT request */
-        logger.fine(requests.toString());
+        if (logger.isLoggable(Level.FINE)) {
+            logger.fine(requests.toString());
+        }
 
         http.writeRequests(requests, null);
         // remove CONNECT header
@@ -1842,6 +1867,9 @@
                 }
             }
         }
+        if (logger.isLoggable(Level.FINER)) {
+            logger.finer("Proxy Authentication for " + authhdr.toString() +" returned " + ret.toString());
+        }
         return ret;
     }
 
@@ -1896,21 +1924,9 @@
             }
             if (ret == null) {
                 if (schemeID == NegotiateAuthentication.KERBEROS_AUTH) {
-                    URL url1;
-                    try {
-                        url1 = new URL (url, "/"); /* truncate the path */
-                    } catch (Exception e) {
-                        url1 = url;
-                    }
                     ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Kerberos"));
                 }
                 if (schemeID == NegotiateAuthentication.NEGOTIATE_AUTH) {
-                    URL url1;
-                    try {
-                        url1 = new URL (url, "/"); /* truncate the path */
-                    } catch (Exception e) {
-                        url1 = url;
-                    }
                     ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Negotiate"));
                 }
                 if (schemeID == BasicAuthentication.BASIC_AUTH) {
@@ -1981,6 +1997,9 @@
                 }
             }
         }
+        if (logger.isLoggable(Level.FINER)) {
+            logger.finer("Server Authentication for " + authhdr.toString() +" returned " + ret.toString());
+        }
         return ret;
     }
 
@@ -2054,6 +2073,9 @@
         if (streaming()) {
             throw new HttpRetryException (RETRY_MSG3, stat, loc);
         }
+        if (logger.isLoggable(Level.FINE)) {
+            logger.fine("Redirected from " + url + " to " + locUrl);
+        }
 
         // clear out old response headers!!!!
         responses = new MessageHeader();
@@ -2158,11 +2180,17 @@
                     /* raw stream, which will block on read, so only read
                      * the expected number of bytes, probably 0
                      */
-                    int cl = 0, n=0;
-                    try {
-                        cl = Integer.parseInt (responses.findValue ("Content-Length"));
-                    } catch (Exception e) {}
-                    for (int i=0; i<cl; ) {
+                    long cl = 0;
+                    int n = 0;
+                    String cls = responses.findValue ("Content-Length");
+                    if (cls != null) {
+                        try {
+                            cl = Long.parseLong (cls);
+                        } catch (NumberFormatException e) {
+                            cl = 0;
+                        }
+                    }
+                    for (long i=0; i<cl; ) {
                         if ((n = is.read (cdata)) == -1) {
                             break;
                         } else {
@@ -2509,12 +2537,6 @@
         return readTimeout < 0 ? 0 : readTimeout;
     }
 
-    @Override
-    protected void finalize() {
-        // this should do nothing.  The stream finalizer will close
-        // the fd
-    }
-
     String getMethod() {
         return method;
     }