上一篇对整个http协议有个大致的了解,是时候进一步尝试做一下代理了。
写在前面
- HTTP 代理原理及实现
- 目标是通过中间人代理的方式代理普通Http请求, 使用隧道代理方式代理Https请求。
思考
- 本文先考虑中间人代理方式
- 前面
Http文件服务器Demo - ProxyHttpServer
是来了请求直接读取文件内容回复;现在代理的话其实只是换个方式,也就是来了请求,根据请求访问网页,再把得到的内容回复过去。
这样的话,变动的地方在于要再开一个线程:- 本来的线程用来接收客户端的请求、创建到服务器的连接、构造到服务器的数据;
- 新建的线程则专门用来接收服务器的数据,转发给客户端。
实现
其实明白了原理,似乎也没啥好bb的。。
创建到服务器的TCP连接,打开面向服务器的监听线程,专用于转发数据给客户端
private void connecToServer(HttpRequest httpRequest) throws IOException {
//默认ip:port参数
String dstIp = httpRequest.host;
int dstPort = 80;
// connect方法, 从首行url获取目的参数
if (httpRequest.method.toLowerCase().equals("connect")) {
dstPort = 443;
dstIp = httpRequest.url;
}
// Get/Post方法,从Host获取目的参数
Matcher matcher = HttpResource.patternHost.matcher(dstIp);
if (matcher.find()) {
dstIp = matcher.group(1);
dstPort = Integer.parseInt(matcher.group(2));
}
if (socketServer == null) {
socketServer = new Socket();
socketServer.connect(new InetSocketAddress(dstIp, dstPort));
// 获取服务器之间的输入输出流
inFromSever = new StreamReader(monitor, socketServer,
new BufferedInputStream(socketServer.getInputStream()));
outToServer = new BufferedOutputStream(socketServer.getOutputStream());
// 打开面向服务器的监听线程,专用于转发数据给客户端
ProxyDealer proxyDealer = new ProxyDealer(this);
SocketServer.httpProxyThreadPool.execute(proxyDealer);
}
}
处理客户端的消息,构造Http请求发送给服务器
private void doProxyNormal(HttpRequest httpRequest) throws IOException {
// TODO do something with the httpRequest. Put 'X-forward...', for example.
httpRequest.headers.put("X-Forwarded-For", "123.123.123.123");
connecToServer(httpRequest);
// 向服务器发送Http请求
outToServer.write(String.format("%s %s %s\r\n", httpRequest.method, httpRequest.url, httpRequest.version).getBytes());
outToServer.write(String.format("Host: %s\r\n", httpRequest.host).getBytes());
for (Entry<String, String> entry : httpRequest.headers.entrySet()) {
if (!entry.getKey().toLowerCase().contains("proxy") && !entry.getKey().toLowerCase().contains("forward")
&& !entry.getKey().toLowerCase().contains("authorization")) {
outToServer.write((entry.getKey() + ": " + entry.getValue()).getBytes());
outToServer.write(HttpResource.BREAK_LINE);
}
}
if (httpRequest.dataLength > 0) {
outToServer.write(String.format("Content-Length: %d\r\n", httpRequest.dataLength).getBytes());
}
outToServer.write(HttpResource.BREAK_LINE);
if (httpRequest.dataLength > 0) {
int count = 0;
int rSize = in.read(in.readBuffer);
System.out.println(new String(in.readBuffer, 0, rSize));
while (rSize < httpRequest.dataLength - count) {
outToServer.write(in.readBuffer, 0, rSize);
rSize = in.read(in.readBuffer);
count += rSize;
}
outToServer.write(in.readBuffer, 0, httpRequest.dataLength - count);
}
outToServer.flush();
}