为了说明http代理的工作机制,我特意写了一个最简单的代理,去掉很多繁杂的功能,并尽可能的使代码量小,源码如下,不清楚http代理机制的朋友参考一下基本就明白了。
[
java]
/*
* $RCSfile: SimpleHttpProxy.java,v $$
* $Revision: 1.1 $
* $Date: 2013-1-9 $
*
* Copyright (C) 2008 Skin, Inc. All rights reserved.
*
* This software is the proprietary information of Skin, Inc.
* Use is subject to license terms.
*/
package test.com.skin.http.proxy;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.URL;
/**
* <p>Title: SimpleHttpProxy</p>
* <p>Description: </p>
* <p>Copyright: Copyright (c) 2006</p>
* @author xuesong.net
* @version 1.0
*/
public class SimpleHttpProxy
{
public static final int PORT = 6666;
public static final byte[] CRLF = new byte[]{0x0D, 0x0A};
/**
* @param args
*/
public static void main(String[] args)
{
ServerSocket socketServer = null;
try
{
socketServer = new ServerSocket(PORT);
while(true)
{
try
{
final Socket socket = socketServer.accept();
(new Thread(){
public void run(){
SimpleHttpProxy.service(socket);
}
}).start();
}
catch(SocketTimeoutException e)
{
e.printStackTrace();
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
if(socketServer != null)
{
try
{
socketServer.close();
}
catch(IOException e)
{
}
}
}
}
private static void service(Socket socket)
{
Socket remote = null;
try
{
socket.setSoTimeout(2000);
socket.setKeepAlive(false);
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
/**
* 读取协议头的第一行
* 格式: GET
http://www.mytest.com HTTP/1.1
*/
byte[] buffer = readLine(inputStream);
if(buffer.length < 1)
{
return;
}
String header = new String(buffer, "UTF-8");
String[] action = header.split(" ");
if(action.length < 3)
{
return;
}
String address = action[1];
/**
* 目标地址是从http协议的第一行取
* 目标主机应该从协议的Host头里面取,如果Host取不到, 从地址里面取
* 此处为了简化逻辑只从地址里面取host, 因此如果路径不是绝对路径就忽略
*/
if(address.startsWith("http://") == false)
{
return;
}
System.out.print(header);
URL url = new URL(address);
String host = url.getHost();
int port = (url.getPort() > -1 ? url.getPort() : 80);
remote = new Socket(host, port);
InputStream remoteInputStream = remote.getInputStream();
OutputStream remoteOutputStream = remote.getOutputStream();
/**
* 某些服务器对协议头必须一次性读完, 例如
qq空间
* 因此此处先读出协议头, 并且一次写入, 写入之后必须flush
* 否则就跳转到QQ首页了
*/
long contentLength = -1L;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write(buffer, 0, buffer.length);
/**
* 读取协议头
* 也可以不读取协议头, 而是直接把inputStream写入到remoteOutputStream
* 为了兼容某些服务器, 此处简单的读取一下协议头
*/
while((buffer = readLine(inputStream)).length > 0)
{
header = new String(buffer, "UTF-8").trim();
if(header.length() < 1)
{
break;
}
if(header.startsWith("Content-Length:"))
{
try
{
contentLength = Long.parseLong(header.substring(15).trim());
}
catch(NumberFormatException e){}
}
bos.write(buffer, 0, buffer.length);
}
/** 协议头和主体之间的空行 */
bos.write(CRLF);
remoteOutputStream.write(bos.toByteArray());
remoteOutputStream.flush();
/** 如果存在contentLength */
if(contentLength > 0)
{
copy(inputStream, remoteOutputStream, 4096, contentLength);
}
try
{
/**
* 将目标主机返回的数据写入到客户端
* 此处应该检查一下Content-Length, 并且根据Content-Length来决定要写入多少数据
* 不过很多服务器经常会不返回Content-Length,
* 没有Content-Length, read函数会一直读取
* 因此使用timeout来解决阻塞的问题
* 超时的时候自动退出线程, 否则该线程就无法释放了
*/
remote.setSoTimeout(10000);
copy(remoteInputStream, outputStream, 4096);
}
catch(SocketTimeoutException e)
{
}
catch(Exception e)
{
e.printStackTrace();
}
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
try
{
if(socket != null)
{
socket.close();
}
}
catch(IOException e)
{
}
try
{
if(remote != null)
{
remote.close();
}
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
public static byte[] readLine(InputStream stream) throws IOException
{
int b = -1;
ByteArrayOutputStream bos = new ByteArrayOutputStream(2048);
while((b = stream.read()) != -1)
{
if(b == '\n')
{
bos.write(b);
break;
}
bos.write(b);
}
return bos.toByteArray();
}
public static void copy(InputStream inputStream, OutputStream outputStream, int bufferSize) throws IOException
{
int length = 0;
byte[] buffer = new byte[bufferSize];
while((length = inputStream.read(buffer, 0, bufferSize)) > -1)
{
outputStream.write(buffer, 0, length);
}
outputStream.flush();
}
public static void copy(InputStream inputStream, OutputStream outputStream, int bufferSize, long size) throws IOException
{
if(size > 0)
{
int readBytes = 0;
long count = size;
int length = Math.min(bufferSize, (int)(size));
byte[] buffer = new byte[length];
while(count > 0)
{
if(count > length)
{
readBytes = inputStream.read(buffer, 0, length);
}
else
{
readBytes = inputStream.read(buffer, 0, (int)count);
}
if(readBytes > 0)
{
outputStream.write(buffer, 0, readBytes);
count -= readBytes;
}
else
{
break;
}
}
outputStream.flush();
}
}
}