HTTP代理服务器是一种特殊的网络服务,容许一个网络终端(通常为客户端)经过这个服务与另外一个网络终端(通常为服务器)进行非直接的链接。一些网关、路由器等网络设备具有网络代理功能。通常认为代理服务有利于保障网络终端的隐私或安全,防止攻击。html
http请求通过代理服务器,代理服务器只要负责转发相应的http响应体就能够了。git
https请求通过代理服务器,会发送一个CONNECT报文,用于和代理服务器创建隧道,若是代理服务器返回HTTP 200,则创建成功,后续代理服务器只要负责转发数据就行,实际上SSL/TLS握手仍是发生在客户端和真实服务器。github
建立SocketServer监听端口,根据http请求头方法若是是CONNECT就是HTTPS请求不然都为HTTP请求,接着根据HOST头创建代理服务器与目标服务器的链接,而后转发数据。HTTPS请求须要特殊处理,由于CONNECT请求并不须要转发,要返回一个HTTP 200的响应创建隧道,以后才进行转发。web
//监听端口 ServerSocket serverSocket = new ServerSocket(port); for (; ; ) { new SocketHandle(serverSocket.accept()).start(); }
static class SocketHandle extends Thread { private Socket socket; public SocketHandle(Socket socket) { this.socket = socket; } @Override public void run() { OutputStream clientOutput = null; InputStream clientInput = null; Socket proxySocket = null; InputStream proxyInput = null; OutputStream proxyOutput = null; try { clientInput = socket.getInputStream(); clientOutput = socket.getOutputStream(); String line; String host = ""; LineBuffer lineBuffer = new LineBuffer(1024); StringBuilder headStr = new StringBuilder(); //读取HTTP请求头,并拿到HOST请求头和method while (null != (line = lineBuffer.readLine(clientInput))) { System.out.println(line); headStr.append(line + "\r\n"); if (line.length() == 0) { break; } else { String[] temp = line.split(" "); if (temp[0].contains("Host")) { host = temp[1]; } } } String type = headStr.substring(0, headStr.indexOf(" ")); //根据host头解析出目标服务器的host和port String[] hostTemp = host.split(":"); host = hostTemp[0]; int port = 80; if (hostTemp.length > 1) { port = Integer.valueOf(hostTemp[1]); } //链接到目标服务器 proxySocket = new Socket(host, port); proxyInput = proxySocket.getInputStream(); proxyOutput = proxySocket.getOutputStream(); //根据HTTP method来判断是https仍是http请求 if ("CONNECT".equalsIgnoreCase(type)) {//https先创建隧道 clientOutput.write("HTTP/1.1 200 Connection Established\r\n\r\n".getBytes()); clientOutput.flush(); } else {//http直接将请求头转发 proxyOutput.write(headStr.toString().getBytes()); } //新开线程转发客户端请求至目标服务器 new ProxyHandleThread(clientInput, proxyOutput).start(); //转发目标服务器响应至客户端 while (true) { clientOutput.write(proxyInput.read()); } } catch (IOException e) { e.printStackTrace(); } finally { if (proxyInput != null) { try { proxyOutput.close(); } catch (IOException e) { e.printStackTrace(); } } if (proxyOutput != null) { try { proxyOutput.close(); } catch (IOException e) { e.printStackTrace(); } } if (proxySocket != null) { try { proxySocket.close(); } catch (IOException e) { e.printStackTrace(); } } if (clientInput != null) { try { clientInput.close(); } catch (IOException e) { e.printStackTrace(); } } if (clientOutput != null) { try { clientOutput.close(); } catch (IOException e) { e.printStackTrace(); } } if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
static class ProxyHandleThread extends Thread { private InputStream input; private OutputStream output; public ProxyHandleThread(InputStream input, OutputStream output, CountDownLatch cdl) { this.input = input; this.output = output; } @Override public void run() { try { while (true) { output.write(input.read()); } } catch (IOException e) { e.printStackTrace(); } } }
以上一个简单的HTTP代理服务器就实现了,不过其中问题也有不少,如BIO模型的缺陷,异常处理机制。
下一篇会用netty来实现一个高性能的HTTP代理服务器。
代码托管在github上,欢迎start安全