blob: f03cb35610dbd15dbabeb8107bf89110400026ac [file] [log] [blame]
/*
* Copyright 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.dev.util;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* A blind byte-by-byte bi-directional proxy.
*/
public class NetProxy {
private class ClientToServerProxyConnection extends ProxyConnection {
public ClientToServerProxyConnection(Socket clientSideSocket,
Socket serverSideSocket) {
super(clientSideSocket, serverSideSocket);
setName(fromPort + " => " + toPort + " #" + connections);
}
@Override
protected void recordBytesTransferred(byte[] bytes, int avail) {
addBytesSent(avail, bytes);
}
}
private abstract class ProxyConnection extends Thread {
private Socket fromSocket;
private Socket toSocket;
public ProxyConnection(Socket fromSocket, Socket toSocket) {
this.fromSocket = fromSocket;
this.toSocket = toSocket;
}
@Override
public void run() {
try {
InputStream fromSideInput = fromSocket.getInputStream();
OutputStream toSideOutput = toSocket.getOutputStream();
/*
* Spin and pass data in one direction.
*/
int avail;
byte[] bytes = new byte[32768];
while (true) {
// Read 'from' side
avail = fromSideInput.read(bytes);
if (avail > 0) {
// Forward to 'to' side
toSideOutput.write(bytes, 0, avail);
// Accumulate bytes received
recordBytesTransferred(bytes, avail);
} else if (avail == -1) {
break;
}
}
} catch (Throwable e) {
} finally {
try {
fromSocket.close();
toSocket.close();
} catch (Throwable e) {
}
}
}
protected abstract void recordBytesTransferred(byte[] bytes, int avail);
}
private class ServerToClientProxyConnection extends ProxyConnection {
public ServerToClientProxyConnection(Socket clientSideSocket,
Socket serverSideSocket) {
super(serverSideSocket, clientSideSocket);
setName(fromPort + " <= " + toPort + " #" + connections);
}
@Override
protected void recordBytesTransferred(byte[] bytes, int avail) {
addBytesReceived(avail, bytes);
}
}
public static void main(String[] args) {
if (args.length < 2) {
System.out.println("Usage: NetProxy <local-port> <remote-port> [<remote-host>]");
return;
}
int localPort = Integer.parseInt(args[0]);
int remotePort = Integer.parseInt(args[1]);
String remoteHost = args.length < 3 ? "localhost" : args[2];
NetProxy netProxy = new NetProxy(localPort, remoteHost, remotePort, true);
netProxy.run();
}
private boolean dumpChars;
private int fromPort;
private String toName;
private int toPort;
private int bytesSent;
private int bytesReceived;
private int connections;
private boolean end;
private long startTime = System.currentTimeMillis();
public NetProxy(int fromPort, String toName, int toPort, boolean dumpChars) {
this.fromPort = fromPort;
this.toName = toName;
this.toPort = toPort;
this.dumpChars = dumpChars;
}
public void end() {
end = true;
}
public void run() {
try {
System.out.println("Time\tBytes Sent\tBytes Received\tTotal Bytes\tHTTP Data");
ServerSocket serverSocket = new ServerSocket(fromPort);
while (!end) {
try {
++connections;
/*
* Listen for and accept the client-initiated connection. Connect to
* the real server. Spawn a thread to handle the data shuffling for
* newly-created connection.
*/
Socket clientSideSocket = serverSocket.accept();
clientSideSocket.setTcpNoDelay(true);
Socket serverSideSocket = new Socket(toName, toPort);
serverSideSocket.setTcpNoDelay(true);
new ClientToServerProxyConnection(clientSideSocket, serverSideSocket).start();
new ServerToClientProxyConnection(clientSideSocket, serverSideSocket).start();
} catch (Throwable e) {
e.printStackTrace();
}
}
} catch (Throwable e) {
// Failed to even be able to listen.
e.printStackTrace();
}
}
protected int getBytesReceived() {
return bytesReceived;
}
protected int getBytesSent() {
return bytesSent;
}
private synchronized void addBytesReceived(int byteCount, byte[] bytes) {
bytesReceived += byteCount;
log(0, byteCount, bytes);
}
private synchronized void addBytesSent(int byteCount, byte[] bytes) {
this.bytesSent += byteCount;
log(byteCount, 0, bytes);
}
private synchronized void log(int bytesSent, int bytesReceived,
byte[] dataBuffer) {
System.out.print(System.currentTimeMillis() - startTime);
System.out.print("\t");
System.out.print(bytesSent);
System.out.print("\t");
System.out.print(bytesReceived);
System.out.print("\t");
System.out.print(this.bytesSent + this.bytesReceived);
System.out.print("\t");
char ch;
int avail = (bytesSent != 0 ? bytesSent : bytesReceived);
if (dumpChars) {
int limit = (avail < 1024 ? avail : 1024);
for (int i = 0; i < limit; ++i) {
ch = (char) dataBuffer[i];
if (ch >= 32 && ch < 128) {
System.out.print(ch);
} else if (ch == '\n') {
System.out.print("\\n");
} else if (ch == '\r') {
System.out.print("\\r");
} else {
System.out.print('.');
}
}
} else {
// Just read up to the \r\n\r\n.
//
try {
String http = new String(dataBuffer, "UTF-8");
int endOfHeaders = http.indexOf("\r\n\r\n");
if (endOfHeaders != -1 && http.indexOf("HTTP") != -1) {
http = http.replaceAll("\\r", "");
http = http.replaceAll("\\n", "\n\t\t\t\t");
System.out.print(http.substring(0, endOfHeaders));
} else {
System.out.print("(data)");
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
System.out.println();
}
}