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安全