/**
* Copyright (c) 2002 by Phil Hanna
* All rights reserved.
*
* You may study, use, modify, and distribute this
* software for any purpose provided that this
* copyright notice appears in all copies.
*
* This software is provided without warranty
* either expressed or implied.
*/
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Mainline for the HTTP tracer tool
*/
public class MainHTTPTracerTool {
public static void main(String[] args) throws IOException {
String opt_host = null;
String opt_port = null;
String opt_tracerPort = null;
String opt_log = null;
try {
// Parse command line arguments
for (int i = 0, n = args.length; i < n; i++) {
String arg = args[i];
if (arg.equals("-h")) {
showUsage();
return;
}
if (arg.equals("-host") && (i + 1 < n))
opt_host = args[++i];
else if (arg.equals("-port") && (i + 1 < n))
opt_port = args[++i];
else if (arg.equals("-tracerPort") && (i + 1 < n))
opt_tracerPort = args[++i];
else if (arg.equals("-log") && (i + 1 < n))
opt_log = args[++i];
else
throw new IllegalArgumentException("Unrecognized option "
+ arg);
}
// Verify that there is no port conflict
int testTracerPort = (opt_tracerPort == null) ? Tracer.DEFAULT_PORT
: Integer.parseInt(opt_tracerPort);
int testHostPort = (opt_port == null) ? RequestHandler.DEFAULT_PORT
: Integer.parseInt(opt_port);
if (testTracerPort == testHostPort)
throw new IllegalArgumentException(
"Cannot assign port and tracerPort both to "
+ testHostPort);
} catch (IllegalArgumentException e) {
System.err.println(e.getMessage());
return;
}
// Create the tracer and set its properties
Tracer tracer = new Tracer();
if (opt_host != null)
tracer.setHost(opt_host);
if (opt_port != null)
tracer.setPort(Integer.parseInt(opt_port));
if (opt_tracerPort != null)
tracer.setTracerPort(Integer.parseInt(opt_tracerPort));
if (opt_log != null)
tracer.setLogWriter(new FileWriter(opt_log));
// Start it running
tracer.start();
}
public static final void showUsage() {
String[] text = { "", "usage: java -jar tracer.jar [options]", "",
"where options are:", "",
"-host <hostName> (default is localhost)",
"-port <hostPort> (default is 80)",
"-tracerPort <localPort> (default is 8601)",
"-log <fileName> (default is stdout)", };
for (int i = 0; i < text.length; i++)
System.out.println(text[i]);
}
}
/**
* Copyright (c) 2002 by Phil Hanna All rights reserved.
*
* You may study, use, modify, and distribute this software for any purpose
* provided that this copyright notice appears in all copies.
*
* This software is provided without warranty either expressed or implied.
*/
/**
* Acts as a proxy web server, capturing requests and responses and echoing the
* headers to a log stream.
*/
class Tracer extends Thread implements Logger {
public static final int DEFAULT_PORT = 8601;
private String host;
private int port;
private int tracerPort;
private PrintWriter logWriter;
public void run() {
// Set defaults if not otherwise specified
if (tracerPort == 0)
tracerPort = DEFAULT_PORT;
if (logWriter == null)
logWriter = new PrintWriter(System.out);
// Start proxy server
try {
log("M: Opening tracer server on tracerPort " + tracerPort);
ServerSocket server = new ServerSocket(tracerPort);
// Loop forever
while (true) {
// Wait for connection
log("M: Waiting for connections");
Socket client = server.accept();
log("M: Connection received from " + client);
// Dispatch it to a request handler thread
RequestHandler rh = new RequestHandler(client);
rh.setLogger(this);
if (host != null)
rh.setHost(host);
if (port != 0)
rh.setPort(port);
rh.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
// ===========================================
// Implementation of Logger
// ===========================================
/**
* Writes a message to the log
*
* @param message
* the message
*/
public synchronized void log(String message) {
logWriter.println(message);
logWriter.flush();
}
// ===========================================
// Property setters
// ===========================================
/**
* Sets the host.
*
* @param host
* the host.
*/
public void setHost(String host) {
this.host = host;
}
/**
* Sets the port.
*
* @param port
* the port.
*/
public void setPort(int port) {
this.port = port;
}
/**
* Sets the tracerPort.
*
* @param tracerPort
* the tracerPort.
*/
public void setTracerPort(int tracerPort) {
this.tracerPort = tracerPort;
}
/**
* Sets the logWriter.
*
* @param logWriter
* the logWriter.
*/
public void setLogWriter(Writer logWriter) throws IOException {
this.logWriter = new PrintWriter(logWriter);
}
}
/**
* Copyright (c) 2002 by Phil Hanna All rights reserved.
*
* You may study, use, modify, and distribute this software for any purpose
* provided that this copyright notice appears in all copies.
*
* This software is provided without warranty either expressed or implied.
*/
/**
* A proxy HTTP server that handles a single request
*/
class RequestHandler extends Thread {
public static final String DEFAULT_HOST = "localhost";
public static final int DEFAULT_PORT = 80;
private Socket client;
private Logger logger;
private String host;
private int port;
// ===========================================
// Constructors
// ===========================================
/**
* Creates a new <code>RequestHandler</code> for the specified client
*/
public RequestHandler(Socket client) {
this.client = client;
}
// ===========================================
// Instance methods
// ===========================================
/**
* Copies the request from the client to the server and copies the response
* back to the client.
*/
public void run() {
try {
// Open a socket to the web server
if (host == null)
host = DEFAULT_HOST;
if (port <= 0)
port = DEFAULT_PORT;
Socket server = new Socket(host, port);
// Open I/O streams to the client
InputStream cin = new BufferedInputStream(client.getInputStream());
OutputStream cout = new BufferedOutputStream(client
.getOutputStream());
// Open I/O streams to the server
InputStream sin = new BufferedInputStream(server.getInputStream());
OutputStream sout = new BufferedOutputStream(server
.getOutputStream());
// Copy request line and headers from client to server,
// echoing to logger if specified. Stop after the
// first empty line (end of headers)
int contentLength = 0;
StringBuffer sb = new StringBuffer();
for (;;) {
// Read a byte from client
// and copy it to server
int c = cin.read();
sout.write(c);
// Ignore CR at end of line
if (c == '\r')
continue;
// If LF, process the line
if (c == '\n') {
String line = sb.toString();
sb = new StringBuffer();
// Log the line
logger.log("C: " + line);
// If this is an empty line,
// there are no more headers
if (line.length() == 0)
break;
// If it is a content length header,
// save the content length
int p = line.indexOf(":");
if (p != -1) {
String key = line.substring(0, p).trim();
String value = line.substring(p + 1).trim();
if (key.equalsIgnoreCase("content-length"))
contentLength = Integer.parseInt(value);
}
}
// Otherwise, append char to string buffer
else
sb.append((char) c);
}
sout.flush();
// If content length was specified, read input stream
// and copy to server
if (contentLength > 0) {
for (int i = 0; i < contentLength; i++) {
int c = cin.read();
sout.write(c);
}
sout.flush();
}
// Echo the response back to the client
sb = new StringBuffer();
while (true) {
// Read a byte from server
// and copy it to client
int c = sin.read();
cout.write(c);
// Ignore CR at end of line
if (c == '\r')
continue;
// If LF, process the line
if (c == '\n') {
String line = sb.toString();
sb = new StringBuffer();
// Log the line
logger.log("S: " + line);
// If this is an empty line,
// there are no more headers
if (line.length() == 0)
break;
}
// Otherwise, append char to string buffer
else
sb.append((char) c);
}
cout.flush();
// Copy remaining bytes to client
int bytesCopied = 0;
while (true) {
int c = sin.read();
if (c == -1)
break;
cout.write(c);
bytesCopied++;
}
if (bytesCopied > 0)
cout.flush();
// Close streams and sockets
cin.close();
cout.close();
client.close();
sin.close();
sout.close();
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// ===========================================
// Property setters
// ===========================================
/**
* Sets the logger.
*
* @param logger
* the logger.
*/
public void setLogger(Logger logger) {
this.logger = logger;
}
/**
* Sets the host.
*
* @param host
* the host.
*/
public void setHost(String host) {
this.host = host;
}
/**
* Sets the port.
*
* @param port
* the port.
*/
public void setPort(int port) {
this.port = port;
}
}
/**
* Copyright (c) 2002 by Phil Hanna All rights reserved.
*
* You may study, use, modify, and distribute this software for any purpose
* provided that this copyright notice appears in all copies.
*
* This software is provided without warranty either expressed or implied.
*/
/**
* The set of methods that must be implemented by a class that logs message
*/
interface Logger {
/**
* Logs a message
*/
public void log(String s);
}
|