6811297: Add more logging to HTTP protocol handler
Added extra logging to HttpURLConnection and HttpClient. Added a capture tool. Reviewed-by: chegar
This commit is contained in:
parent
b9554f42b8
commit
1ece67558e
@ -66,6 +66,9 @@ FILES_java = \
|
||||
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 @@ FILES_java = \
|
||||
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 \
|
||||
|
171
jdk/src/share/classes/sun/net/www/http/HttpCapture.java
Normal file
171
jdk/src/share/classes/sun/net/www/http/HttpCapture.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -27,6 +27,9 @@ package sun.net.www.http;
|
||||
|
||||
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.HeaderParser;
|
||||
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 @@ public class HttpClient extends NetworkClient {
|
||||
/** 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 @@ public class HttpClient extends NetworkClient {
|
||||
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 @@ public class HttpClient extends NetworkClient {
|
||||
/* 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 @@ public class HttpClient extends NetworkClient {
|
||||
}
|
||||
});
|
||||
|
||||
capture = HttpCapture.getCapture(url);
|
||||
openServer();
|
||||
}
|
||||
|
||||
@ -300,8 +286,10 @@ public class HttpClient extends NetworkClient {
|
||||
// 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 @@ public class HttpClient extends NetworkClient {
|
||||
kac.put(url, null, this);
|
||||
}
|
||||
|
||||
protected boolean isInKeepAliveCache() {
|
||||
protected synchronized boolean isInKeepAliveCache() {
|
||||
return inCache;
|
||||
}
|
||||
|
||||
@ -389,11 +377,16 @@ public class HttpClient extends NetworkClient {
|
||||
* 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 @@ public class HttpClient extends NetworkClient {
|
||||
/*
|
||||
* Returns true if this httpclient is from cache
|
||||
*/
|
||||
public boolean isCachedConnection() {
|
||||
public synchronized boolean isCachedConnection() {
|
||||
return cachedHttpClient;
|
||||
}
|
||||
|
||||
@ -457,26 +450,6 @@ public class HttpClient extends NetworkClient {
|
||||
super.openServer(proxyHost, proxyPort);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 @@ public class HttpClient extends NetworkClient {
|
||||
return;
|
||||
}
|
||||
|
||||
String urlHost = url.getHost().toLowerCase();
|
||||
|
||||
if (url.getProtocol().equals("http") ||
|
||||
url.getProtocol().equals("https") ) {
|
||||
|
||||
@ -595,6 +566,9 @@ public class HttpClient extends NetworkClient {
|
||||
|
||||
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 @@ public class HttpClient extends NetworkClient {
|
||||
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 @@ public class HttpClient extends NetworkClient {
|
||||
* 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 @@ public class HttpClient extends NetworkClient {
|
||||
* 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 @@ public class HttpClient extends NetworkClient {
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -237,7 +237,6 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
/* 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 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
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 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
|
||||
setRequests=true;
|
||||
}
|
||||
if(logger.isLoggable(Level.FINEST)) {
|
||||
if (logger.isLoggable(Level.FINE)) {
|
||||
logger.fine(requests.toString());
|
||||
}
|
||||
http.writeRequests(requests, poster);
|
||||
@ -602,7 +608,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
{
|
||||
boolean redir;
|
||||
int redirects = 0;
|
||||
InputStream in = null;
|
||||
InputStream in;
|
||||
|
||||
do {
|
||||
if (c instanceof HttpURLConnection) {
|
||||
@ -715,6 +721,12 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
&& !(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 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
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 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
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 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
|
||||
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 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
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 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
disconnectInternal ();
|
||||
throw new IOException ("Authentication failure");
|
||||
}
|
||||
authObj = null;
|
||||
doingNTLMp2ndStage = false;
|
||||
continue;
|
||||
}
|
||||
@ -1270,7 +1295,6 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
throw new IOException ("Authentication failure");
|
||||
}
|
||||
doingNTLM2ndStage = false;
|
||||
authObj = null;
|
||||
setCookieHeader();
|
||||
continue;
|
||||
}
|
||||
@ -1571,7 +1595,9 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
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 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
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 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
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 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (logger.isLoggable(Level.FINER)) {
|
||||
logger.finer("Proxy Authentication for " + authhdr.toString() +" returned " + ret.toString());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1896,21 +1924,9 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
}
|
||||
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 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (logger.isLoggable(Level.FINER)) {
|
||||
logger.finer("Server Authentication for " + authhdr.toString() +" returned " + ret.toString());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -2054,6 +2073,9 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
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 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
/* 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 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
||||
return readTimeout < 0 ? 0 : readTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
// this should do nothing. The stream finalizer will close
|
||||
// the fd
|
||||
}
|
||||
|
||||
String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user