├── README.md ├── src └── main │ ├── java │ └── com │ │ └── hekewangzi │ │ └── httpProxyServer │ │ ├── httpMessage │ │ ├── exception │ │ │ ├── ConnectServerError.java │ │ │ └── BuildHttpMessageError.java │ │ ├── startLine │ │ │ ├── StartLine.java │ │ │ ├── ResponseStartLine.java │ │ │ └── RequestStartLine.java │ │ ├── HttpRequestMessage.java │ │ ├── HttpResponseMessage.java │ │ └── HttpMessage.java │ │ ├── proxy │ │ ├── ClientProxy.java │ │ ├── ServerProxy.java │ │ └── Proxy.java │ │ ├── threadPools │ │ └── ThreadPoolManager.java │ │ ├── constants │ │ ├── Properties.java │ │ ├── Head.java │ │ ├── RequestMethod.java │ │ └── HttpResponseStatus.java │ │ ├── threads │ │ ├── https │ │ │ ├── ServerTranslateHttpsToClientThread.java │ │ │ └── ClientTranslateHttpsToServerThread.java │ │ └── HttpProxyThread.java │ │ ├── HttpProxyServerMain.java │ │ └── utils │ │ ├── SocketUtil.java │ │ └── PasswordUtils.java │ └── resources │ └── log4j.properties └── pom.xml /README.md: -------------------------------------------------------------------------------- 1 | # HttpProxyServer 2 | Http/Https代理服务器服务端 3 | 4 | 先在服务器上运行HttpProxyServer,再在你的电脑上运行HttpProxyClient即可实现代理 5 | 若需代理Google、Youtube等网站,运行HttpProxyServer的服务器要能访问这些网站,香港或国外的服务器都可以 6 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/httpMessage/exception/ConnectServerError.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer.httpMessage.exception; 2 | 3 | /** 4 | * 连接服务器失败 Exception 5 | * 6 | * @author qq 7 | * 8 | */ 9 | public class ConnectServerError extends Exception { 10 | /** 11 | * 12 | */ 13 | private static final long serialVersionUID = 3520679469478229841L; 14 | 15 | public ConnectServerError() { 16 | } 17 | 18 | public ConnectServerError(String message) { 19 | super(message); 20 | } 21 | 22 | public ConnectServerError(Throwable t) { 23 | super(t); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/httpMessage/exception/BuildHttpMessageError.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer.httpMessage.exception; 2 | 3 | /** 4 | * 构建HttpMessage报文失败 Exception 5 | * 6 | * @author qq 7 | * 8 | */ 9 | public class BuildHttpMessageError extends Exception { 10 | /** 11 | * 12 | */ 13 | private static final long serialVersionUID = 8920548046687303458L; 14 | 15 | public BuildHttpMessageError() { 16 | } 17 | 18 | public BuildHttpMessageError(String message) { 19 | super(message); 20 | } 21 | 22 | public BuildHttpMessageError(Throwable t) { 23 | super(t); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/proxy/ClientProxy.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer.proxy; 2 | 3 | import java.net.Socket; 4 | 5 | import com.hekewangzi.httpProxyServer.httpMessage.HttpRequestMessage; 6 | import com.hekewangzi.httpProxyServer.utils.SocketUtil; 7 | 8 | /** 9 | * 代理本地客户端 10 | * 11 | * @author qq 12 | * 13 | */ 14 | public class ClientProxy extends Proxy { 15 | public ClientProxy(Socket clientSocket, Socket serverSocket, HttpRequestMessage requestMessage) { 16 | super(clientSocket, serverSocket, requestMessage); 17 | } 18 | 19 | @Override 20 | public void proxyHttps() { 21 | /* 22 | * 将客户端数据发给服务端 23 | */ 24 | if (!SocketUtil.writeSocket(super.serverOutputStream, requestMessage, super.isEncryptRequest())) { 25 | return; 26 | } 27 | 28 | super.proxyHttps(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/threadPools/ThreadPoolManager.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer.threadPools; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | import java.util.concurrent.Executors; 5 | 6 | import com.hekewangzi.httpProxyServer.constants.Properties; 7 | 8 | /** 9 | * 线程池管理对象 10 | * 11 | * @author qq 12 | * 13 | */ 14 | public class ThreadPoolManager { 15 | private ThreadPoolManager() { 16 | 17 | } 18 | 19 | /** 20 | * 线程池(以后重构,属性) 21 | * 22 | * 创建一个线程池,它可以执行Runnable/Callabel对象所代表的线程 23 | */ 24 | private static ExecutorService executorService = Executors.newFixedThreadPool(Properties.nThreads); 25 | 26 | /** 27 | * 提交线程 28 | * 29 | * @param thread 30 | * 要执行的线程 31 | */ 32 | public static void execute(Thread thread) { 33 | executorService.execute(thread); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/constants/Properties.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer.constants; 2 | 3 | /** 4 | * 配置 enum 5 | * 6 | * @author qq 7 | * 8 | */ 9 | public class Properties { 10 | /* 11 | * 客户端相关配置(本地代理) 12 | */ 13 | /** 14 | * 客户端Socket读取超时时间(单位: 秒) 15 | * 16 | */ 17 | public final static int ClientSoceketReadTimeout = 10 * 1000; 18 | 19 | /* 20 | * 代理服务器相关配置 21 | */ 22 | /** 23 | * 服务端监听端口 24 | */ 25 | public final static int ListenerPort = 6666; 26 | 27 | /* 28 | * 服务器相关配置(各个要访问的服务器) 29 | */ 30 | /** 31 | * 服务器Socket读取超时时间(单位: 秒) 32 | * 33 | */ 34 | public final static int ServerSocketReadTimeout = 30 * 1000; 35 | 36 | /** 37 | * 服务器连接超时时间(单位: 秒) 38 | * 39 | */ 40 | public final static int ServerConnectTimeout = 10 * 1000; 41 | 42 | /* 43 | * 其他 44 | */ 45 | /** 46 | * 线程池corePoolSize 47 | * 48 | */ 49 | public final static int nThreads = 1000; 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/constants/Head.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer.constants; 2 | 3 | /** 4 | * 头部 enum 5 | * 6 | * @author qq 7 | * 8 | */ 9 | public class Head { 10 | /* 11 | * *********************************************************************** 12 | * 请求头部 13 | * *********************************************************************** 14 | */ 15 | public static final String GET = "GET"; 16 | /* 17 | * *********************************************************************** 18 | * 响应头部 19 | * *********************************************************************** 20 | */ 21 | 22 | /* 23 | * *********************************************************************** 24 | * 实体头部 25 | * *********************************************************************** 26 | */ 27 | 28 | /* 29 | * *********************************************************************** 30 | * 通用头部 31 | * *********************************************************************** 32 | */ 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/httpMessage/startLine/StartLine.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer.httpMessage.startLine; 2 | 3 | /** 4 | * 抽象起始行 5 | * 6 | * @author qq 7 | * 8 | */ 9 | public abstract class StartLine { 10 | /** 11 | * 使用协议 12 | */ 13 | protected String protocol; 14 | 15 | /** 16 | * 协议主版本号 17 | */ 18 | protected int masterVersion; 19 | 20 | /** 21 | * 协议次版本号 22 | */ 23 | protected int minorVersion; 24 | 25 | /* 26 | * getter、setter 27 | */ 28 | public String getProtocol() { 29 | return protocol; 30 | } 31 | 32 | public void setProtocol(String protocol) { 33 | this.protocol = protocol; 34 | } 35 | 36 | public int getMasterVersion() { 37 | return masterVersion; 38 | } 39 | 40 | public void setMasterVersion(int masterVersion) { 41 | this.masterVersion = masterVersion; 42 | } 43 | 44 | public int getMinorVersion() { 45 | return minorVersion; 46 | } 47 | 48 | public void setMinorVersion(int minorVersion) { 49 | this.minorVersion = minorVersion; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/proxy/ServerProxy.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer.proxy; 2 | 3 | import java.net.Socket; 4 | 5 | import com.hekewangzi.httpProxyServer.constants.HttpResponseStatus; 6 | import com.hekewangzi.httpProxyServer.httpMessage.HttpRequestMessage; 7 | import com.hekewangzi.httpProxyServer.httpMessage.HttpResponseMessage; 8 | import com.hekewangzi.httpProxyServer.utils.SocketUtil; 9 | 10 | /** 11 | * 代理服务端 12 | * 13 | * @author qq 14 | * 15 | */ 16 | public class ServerProxy extends Proxy { 17 | 18 | public ServerProxy(Socket clientSocket, Socket serverSocket, HttpRequestMessage requestMessage) { 19 | super(clientSocket, serverSocket, requestMessage); 20 | } 21 | 22 | @Override 23 | public void proxyHttps() { 24 | /* 25 | * 响应客户端Web隧道建立成功 26 | */ 27 | HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpResponseStatus._200); 28 | httpResponseMessage.addHeader("Connection", "close"); 29 | SocketUtil.writeSocket(super.clientOutputStream, httpResponseMessage, super.isEncryptResponse()); 30 | 31 | super.proxyHttps(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/constants/RequestMethod.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer.constants; 2 | 3 | /** 4 | * 请求方法 enum 5 | * 6 | * @author qq 7 | * 8 | */ 9 | public enum RequestMethod { 10 | /** 11 | * 从服务器获取一份文档 12 | * 13 | * 请求实体(不支持) 14 | * 15 | * 响应实体(支持) 16 | */ 17 | GET, 18 | 19 | /** 20 | * 向服务器发送需要处理的数据 21 | * 22 | * 请求实体(支持) 23 | * 24 | * 响应实体(支持) 25 | */ 26 | POST, 27 | 28 | /** 29 | * 只从服务器获取文档的首部 30 | * 31 | * 请求实体(不支持) 32 | * 33 | * 响应实体(不支持) 34 | */ 35 | HEAD, 36 | 37 | /** 38 | * 将请求的主体部分存储在服务器上 39 | * 40 | * 请求实体(支持) 41 | * 42 | * 响应实体(支持) 43 | */ 44 | PUT, 45 | 46 | /** 47 | * 对可能经过代理服务器传送到服务器上去的报文进行追踪 48 | * 49 | * 请求实体(不支持) 50 | * 51 | * 响应实体(支持) 52 | */ 53 | TRACE, 54 | 55 | /** 56 | * 决定可以在服务器上执行哪些方法 57 | * 58 | * 请求实体(不支持) 59 | * 60 | * 响应实体(不支持) 61 | */ 62 | OPTIONS, 63 | 64 | /** 65 | * 从服务器上删除一份文档 66 | * 67 | * 请求实体(不支持) 68 | * 69 | * 响应实体(支持) 70 | */ 71 | DELETE, 72 | 73 | /** 74 | * 用于https 75 | * 76 | * 请求实体(不支持) 77 | * 78 | * 响应实体(支持) 79 | */ 80 | CONNECT; 81 | } 82 | -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | #################################################################################################### 2 | # Log4j Properties 3 | #################################################################################################### 4 | 5 | ################################################## 6 | # Appender Properties 7 | ################################################## 8 | # 1 Console Appender 9 | log4j.appender.ConsoleAppender=org.apache.log4j.ConsoleAppender 10 | log4j.appender.RollingFileAppender.Threshold=DEBUG 11 | 12 | log4j.appender.ConsoleAppender.layout=org.apache.log4j.PatternLayout 13 | # date [level] [message] [class.method():lineNumber] 14 | log4j.appender.ConsoleAppender.layout.ConversionPattern=%n%n%d{yyyy-MM-dd HH:mm:ss} [%p] [%c.%M():%L]%n%m 15 | 16 | # 2 RollingFile Appender 17 | log4j.appender.RollingFileAppender=org.apache.log4j.RollingFileAppender 18 | log4j.appender.RollingFileAppender.Threshold=INFO 19 | 20 | log4j.appender.RollingFileAppender.File=httpProxyServer.log 21 | log4j.appender.RollingFileAppender.Append=true 22 | log4j.appender.RollingFileAppender.MaxFileSize=500MB 23 | log4j.appender.RollingFileAppender.MaxBackupIndex=10 24 | 25 | log4j.appender.RollingFileAppender.layout=org.apache.log4j.PatternLayout 26 | # date [level] [message] [class.method():lineNumber] 27 | log4j.appender.RollingFileAppender.layout.ConversionPattern=%n%n%d{yyyy-MM-dd HH:mm:ss} [%p] [%c.%M():%L]%n%m 28 | 29 | ################################################## 30 | # Logger Properties 31 | ################################################## 32 | # 1 Root Logger 33 | log4j.rootLogger=DEBUG,ConsoleAppender,RollingFileAppender 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/httpMessage/startLine/ResponseStartLine.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer.httpMessage.startLine; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | /** 6 | * 响应行 7 | * 8 | * @author qq 9 | * 10 | */ 11 | public class ResponseStartLine extends StartLine { 12 | /** 13 | * 状态码 14 | */ 15 | private int status; 16 | 17 | /** 18 | * 状态描述 19 | */ 20 | private String description; 21 | 22 | public ResponseStartLine(String startLine) { 23 | if (StringUtils.isNotBlank(startLine)) { 24 | /* 25 | * 响应行 26 | */ 27 | String[] startLineArr = startLine.split(" "); // 改用正在匹配多个空格 28 | 29 | // HTTP协议和协议版本 30 | String[] protocolAndVersion = startLineArr[0].split("/"); 31 | this.setProtocol(protocolAndVersion[0]); 32 | 33 | String[] version = protocolAndVersion[1].split("\\."); 34 | this.setMasterVersion(Integer.parseInt(version[0])); 35 | this.setMinorVersion(Integer.parseInt(version[1])); 36 | 37 | // 状态码 38 | this.setStatus(Integer.parseInt(startLineArr[1])); 39 | 40 | // 状态描述 41 | String description = ""; 42 | for (int i = 2; i < startLineArr.length; i++) { 43 | description += startLineArr[i] + " "; 44 | } 45 | this.setDescription(description); 46 | } 47 | } 48 | 49 | /* 50 | * 样例: http/1.1 200 ok 51 | */ 52 | @Override 53 | public String toString() { 54 | return String.format("%s/%s.%s %s %s", super.protocol, super.masterVersion, super.minorVersion, this.status, 55 | this.description); 56 | } 57 | 58 | /* 59 | * getter、setter 60 | */ 61 | public int getStatus() { 62 | return status; 63 | } 64 | 65 | public void setStatus(int status) { 66 | this.status = status; 67 | } 68 | 69 | public String getDescription() { 70 | return description; 71 | } 72 | 73 | public void setDescription(String description) { 74 | this.description = description; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/threads/https/ServerTranslateHttpsToClientThread.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer.threads.https; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.net.Socket; 7 | import java.net.SocketTimeoutException; 8 | 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import com.hekewangzi.httpProxyServer.utils.SocketUtil; 13 | 14 | /** 15 | * 处理服务端转发Https数据给客户端线程类 16 | * 17 | * 将服务端的Https数据流输出给客户端 18 | * 19 | * @author qq 20 | * 21 | */ 22 | public class ServerTranslateHttpsToClientThread extends Thread { 23 | private final static Logger log = LoggerFactory.getLogger(ServerTranslateHttpsToClientThread.class); 24 | /* 25 | * Socket 26 | */ 27 | /** 28 | * 客户端Socket 29 | */ 30 | private Socket clientSocket; 31 | 32 | /** 33 | * 服务端Socket 34 | */ 35 | private Socket serverSocket; 36 | 37 | /* 38 | * constructor 39 | */ 40 | private ServerTranslateHttpsToClientThread() { 41 | } 42 | 43 | public ServerTranslateHttpsToClientThread(Socket clientSocket, Socket serverSocket) { 44 | super(); 45 | this.clientSocket = clientSocket; 46 | this.serverSocket = serverSocket; 47 | } 48 | 49 | @Override 50 | public void run() { 51 | /* 52 | * 53 | */ 54 | InputStream serverInputStream = null; // 服务端输入流 55 | OutputStream clientOutputStream = null; // 客户端输出流 56 | try { 57 | clientOutputStream = clientSocket.getOutputStream(); 58 | serverInputStream = serverSocket.getInputStream(); 59 | } catch (IOException e) { 60 | e.printStackTrace(); 61 | } 62 | 63 | /* 64 | * 65 | */ 66 | int readByte = -1; 67 | 68 | try { 69 | while ((readByte = serverInputStream.read()) != -1) { 70 | clientOutputStream.write(readByte); 71 | } 72 | } catch (SocketTimeoutException e) { // 读取Socket超时 73 | e.printStackTrace(); 74 | SocketUtil.closeSocket(clientSocket, serverSocket); 75 | } catch (IOException e) { 76 | e.printStackTrace(); 77 | SocketUtil.closeSocket(clientSocket, serverSocket); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/threads/https/ClientTranslateHttpsToServerThread.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer.threads.https; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.net.Socket; 7 | import java.net.SocketTimeoutException; 8 | 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import com.hekewangzi.httpProxyServer.utils.SocketUtil; 13 | 14 | /** 15 | * 处理客户端转发Https数据给服务端线程类 16 | * 17 | * 将客户端的Https数据流输出给服务端 18 | * 19 | * @author qq 20 | * 21 | */ 22 | public class ClientTranslateHttpsToServerThread extends Thread { 23 | private final static Logger log = LoggerFactory.getLogger(ClientTranslateHttpsToServerThread.class); 24 | 25 | /* 26 | * Socket 27 | */ 28 | /** 29 | * 客户端Socket 30 | */ 31 | private Socket clientSocket; 32 | 33 | /** 34 | * 服务端Socket 35 | */ 36 | private Socket serverSocket; 37 | 38 | /* 39 | * constructor 40 | */ 41 | private ClientTranslateHttpsToServerThread() { 42 | } 43 | 44 | public ClientTranslateHttpsToServerThread(Socket clientSocket, Socket serverSocket) { 45 | super(); 46 | this.clientSocket = clientSocket; 47 | this.serverSocket = serverSocket; 48 | } 49 | 50 | @Override 51 | public void run() { 52 | /* 53 | * 54 | */ 55 | InputStream clientInputStream = null; // 客户端输入流 56 | OutputStream serverOutputStream = null;// 服务端输出流 57 | try { 58 | clientInputStream = clientSocket.getInputStream(); 59 | serverOutputStream = serverSocket.getOutputStream(); 60 | } catch (IOException e) { 61 | e.printStackTrace(); 62 | return; 63 | } 64 | 65 | /* 66 | * 67 | */ 68 | int readByte = -1; 69 | try { 70 | while ((readByte = clientInputStream.read()) != -1) { 71 | serverOutputStream.write(readByte); 72 | } 73 | } catch (SocketTimeoutException e) { // 读取Socket超时 74 | e.printStackTrace(); 75 | SocketUtil.closeSocket(clientSocket, serverSocket); 76 | } catch (IOException e) { 77 | e.printStackTrace(); 78 | SocketUtil.closeSocket(clientSocket, serverSocket); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/httpMessage/startLine/RequestStartLine.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer.httpMessage.startLine; 2 | 3 | import org.apache.commons.lang3.EnumUtils; 4 | import org.apache.commons.lang3.StringUtils; 5 | 6 | import com.hekewangzi.httpProxyServer.constants.RequestMethod; 7 | 8 | /** 9 | * 请求行 10 | * 11 | * @author qq 12 | * 13 | */ 14 | public class RequestStartLine extends StartLine { 15 | /** 16 | * 请求方法 17 | */ 18 | private RequestMethod method; 19 | 20 | /** 21 | * 请求路径 22 | */ 23 | private String url; 24 | 25 | /* 26 | * constructor 27 | */ 28 | public RequestStartLine(String startLine) { 29 | if (StringUtils.isNotBlank(startLine)) { 30 | startLine = startLine.trim(); 31 | int firstIndex = startLine.indexOf(" "); // 第一个空格索引 32 | int lastIndex = startLine.lastIndexOf(" "); // 最后一个空格索引 33 | 34 | // 请求方法 35 | String requestMethodStr = startLine.substring(0, firstIndex).trim().toUpperCase(); 36 | RequestMethod requestMethod = EnumUtils.getEnum(RequestMethod.class, requestMethodStr); 37 | this.setMethod(requestMethod); 38 | // 请求URL 39 | this.setUrl(startLine.substring(firstIndex, lastIndex).trim()); 40 | 41 | // 请求协议和协议版本 42 | String[] protocolAndVersion = startLine.substring(lastIndex).trim().split("/"); 43 | super.setProtocol(protocolAndVersion[0]); 44 | 45 | String[] version = protocolAndVersion[1].split("\\."); 46 | super.setMasterVersion(Integer.parseInt(version[0])); 47 | super.setMinorVersion(Integer.parseInt(version[1])); 48 | } 49 | } 50 | 51 | /* 52 | * 样例: GET http://c.biancheng.net/cpp/ HTTP/1.1 53 | */ 54 | @Override 55 | public String toString() { 56 | return String.format("%s %s %s/%s.%s", this.method, this.url, super.protocol, super.masterVersion, 57 | super.minorVersion); 58 | } 59 | 60 | /* 61 | * getter、setter 62 | */ 63 | public String getUrl() { 64 | return url; 65 | } 66 | 67 | public void setUrl(String url) { 68 | this.url = url; 69 | } 70 | 71 | public RequestMethod getMethod() { 72 | return method; 73 | } 74 | 75 | public void setMethod(RequestMethod method) { 76 | this.method = method; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/HttpProxyServerMain.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer; 2 | 3 | import java.io.IOException; 4 | import java.net.ServerSocket; 5 | import java.net.Socket; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.slf4j.bridge.SLF4JBridgeHandler; 10 | 11 | import com.hekewangzi.httpProxyServer.constants.Properties; 12 | import com.hekewangzi.httpProxyServer.threadPools.ThreadPoolManager; 13 | import com.hekewangzi.httpProxyServer.threads.HttpProxyThread; 14 | 15 | /** 16 | * Http代理服务器入口类 17 | * 18 | * @author qq 19 | * 20 | */ 21 | public class HttpProxyServerMain { 22 | private final static Logger log = LoggerFactory.getLogger(HttpProxyServerMain.class); 23 | 24 | /** 25 | * 服务端Socket 26 | */ 27 | private static ServerSocket serverSocket; 28 | 29 | /** 30 | * 程序入口 31 | * 32 | * @param args 33 | */ 34 | public static void main(String[] args) { 35 | new HttpProxyServerMain().init(); 36 | 37 | try { 38 | while (true) { 39 | Socket clientSocket = serverSocket.accept(); 40 | // 每当客户端连接后启动一条线程为该客户端服务 41 | ThreadPoolManager.execute(new HttpProxyThread(clientSocket)); 42 | } 43 | } catch (IOException e) { 44 | e.printStackTrace(); 45 | } catch (Exception e) { 46 | e.printStackTrace(); 47 | } 48 | } 49 | 50 | /** 51 | * 系统初始化 52 | */ 53 | private void init() { 54 | log.info("系统初始化开始..."); 55 | this.initLog4j(); 56 | this.initServerSocket(); 57 | log.info("系统初始化完成."); 58 | } 59 | 60 | /** 61 | * 初始化日志 62 | */ 63 | private void initLog4j() { 64 | log.info("日志初始化开始..."); 65 | // 使用SLF4J不需要使用如下配置,否则会日志输出两次 66 | // BasicConfigurator.configure(); 67 | SLF4JBridgeHandler.install(); // 日志桥接 68 | log.info("日志初始化结束..."); 69 | } 70 | 71 | /** 72 | * 初始化ServerSocket 73 | * 74 | * @throws IOException 75 | */ 76 | private void initServerSocket() { 77 | log.info("ServerSocket初始化开始..."); 78 | 79 | try { 80 | serverSocket = new ServerSocket(Properties.ListenerPort); 81 | 82 | log.info("绑定IP:{}", serverSocket.getInetAddress().getHostAddress()); 83 | log.info("绑定端口:{}", serverSocket.getLocalPort()); 84 | } catch (IOException e) { 85 | e.printStackTrace(); 86 | System.exit(0); 87 | } 88 | 89 | log.info("ServerSocket初始化结束."); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/httpMessage/HttpRequestMessage.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer.httpMessage; 2 | 3 | import java.io.InputStream; 4 | import java.util.Map.Entry; 5 | import java.util.Set; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import com.hekewangzi.httpProxyServer.constants.RequestMethod; 11 | import com.hekewangzi.httpProxyServer.httpMessage.exception.BuildHttpMessageError; 12 | import com.hekewangzi.httpProxyServer.httpMessage.startLine.RequestStartLine; 13 | import com.hekewangzi.httpProxyServer.httpMessage.startLine.StartLine; 14 | import com.hekewangzi.httpProxyServer.utils.PasswordUtils; 15 | 16 | /** 17 | * * Http请求报文 18 | * 19 | * @author qq 20 | * 21 | */ 22 | public class HttpRequestMessage extends HttpMessage { 23 | private final static Logger log = LoggerFactory.getLogger(HttpRequestMessage.class); 24 | 25 | /* 26 | * constructor 27 | */ 28 | public HttpRequestMessage(InputStream inputStream) throws BuildHttpMessageError { 29 | super(inputStream); 30 | } 31 | 32 | @Override 33 | public StartLine buildStartLien(String startLine) { 34 | return new RequestStartLine(startLine); 35 | } 36 | 37 | /** 38 | * 是否包含实体 39 | * 40 | * true:POST、PUT 41 | * 42 | * CONNECT有吗? 43 | * 44 | * @return 45 | */ 46 | @Override 47 | public boolean isSupportBody() { 48 | RequestMethod requestMethod = ((RequestStartLine) super.getStartLine()).getMethod(); 49 | 50 | switch (requestMethod) { 51 | case POST: 52 | case PUT: 53 | return true; 54 | default: 55 | return false; 56 | } 57 | } 58 | 59 | /** 60 | * 加密请求报文 61 | */ 62 | @Override 63 | public HttpMessage encryptHttpMessage() { 64 | /* 65 | * 加密请求行(加密请求url即可) 66 | */ 67 | StartLine startLine = this.getStartLine(); 68 | if (startLine instanceof RequestStartLine) { 69 | RequestStartLine requestStartLine = (RequestStartLine) startLine; 70 | requestStartLine.setUrl(PasswordUtils.base64Encrypt(requestStartLine.getUrl())); 71 | 72 | super.setStartLine(requestStartLine); 73 | } 74 | 75 | /* 76 | * 加密头部 77 | */ 78 | if (!super.headerIsEmpty()) { 79 | Set> entrys = super.getHeaders().entrySet(); 80 | for (Entry entry : entrys) { 81 | super.addHeader(entry.getKey(), PasswordUtils.base64Encrypt(entry.getValue())); 82 | } 83 | } 84 | 85 | return this; 86 | } 87 | 88 | /** 89 | * 解密请求报文 90 | */ 91 | @Override 92 | public HttpMessage decryptHttpMessage() { 93 | /* 94 | * 解密请求行 95 | */ 96 | StartLine startLine = this.getStartLine(); 97 | if (startLine instanceof RequestStartLine) { 98 | RequestStartLine requestStartLine = (RequestStartLine) startLine; 99 | requestStartLine.setUrl(PasswordUtils.base64Decrypt(requestStartLine.getUrl())); 100 | 101 | super.setStartLine(requestStartLine); 102 | } 103 | 104 | /* 105 | * 解密头部 106 | */ 107 | if (!super.headerIsEmpty()) { 108 | Set> entrys = super.getHeaders().entrySet(); 109 | for (Entry entry : entrys) { 110 | super.addHeader(entry.getKey(), PasswordUtils.base64Decrypt(entry.getValue())); 111 | } 112 | } 113 | return this; 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/utils/SocketUtil.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer.utils; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | import java.net.ConnectException; 6 | import java.net.InetAddress; 7 | import java.net.InetSocketAddress; 8 | import java.net.Socket; 9 | import java.net.SocketAddress; 10 | import java.net.SocketTimeoutException; 11 | import java.net.UnknownHostException; 12 | 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | import com.hekewangzi.httpProxyServer.httpMessage.HttpMessage; 17 | import com.hekewangzi.httpProxyServer.httpMessage.exception.ConnectServerError; 18 | 19 | /** 20 | * Socket 工具类 21 | * 22 | * @author qq 23 | * 24 | */ 25 | public class SocketUtil { 26 | private final static Logger log = LoggerFactory.getLogger(SocketUtil.class); 27 | 28 | /** 29 | * 连接服务器 30 | * 31 | * @param host 32 | * 服务器主机 33 | * @param port 34 | * 服务器端口 35 | * @param connectTimeout 36 | * 连接服务器超时时长 37 | * @return 连接成功,返回服务端Socket 38 | * @throws ConnectServerError 39 | */ 40 | public static Socket connectServer(String host, int port, int connectTimeout) throws ConnectServerError { 41 | Socket serverSocket = new Socket(); 42 | SocketAddress endpoint; 43 | try { 44 | endpoint = new InetSocketAddress(InetAddress.getByName(host), port); 45 | serverSocket.connect(endpoint, connectTimeout); 46 | } catch (UnknownHostException e) { 47 | e.printStackTrace(); 48 | throw new ConnectServerError("Connect Server UnknownHostException"); 49 | } catch (SocketTimeoutException e) { // 连接超时,以后考虑重连 50 | e.printStackTrace(); 51 | throw new ConnectServerError("Connect Server Is Timeout."); 52 | } catch (ConnectException e) { // 连接失败 53 | e.printStackTrace(); 54 | throw new ConnectServerError("Connect Server ConnectionException"); 55 | } catch (IOException e) { 56 | e.printStackTrace(); 57 | throw new ConnectServerError("Connect Server IOException"); 58 | } 59 | return serverSocket; 60 | } 61 | 62 | /** 63 | * 写Socket 64 | * 65 | * @param outputStream 66 | * @param socketStr 67 | * @return 68 | */ 69 | public static boolean writeSocket(OutputStream outputStream, String socketStr) { 70 | boolean result = false; 71 | try { 72 | outputStream.write(socketStr.getBytes("ISO-8859-1")); 73 | result = true; 74 | } catch (IOException e) { 75 | log.error("writeSocket IOException", e); 76 | } 77 | 78 | return result; 79 | } 80 | 81 | /** 82 | * 写Socket 83 | * 84 | * @param outputStream 85 | * @param httpMessage 86 | * @param encrypt 87 | * 是否加密(true: 加密) 88 | * @return 89 | */ 90 | public static boolean writeSocket(OutputStream outputStream, HttpMessage httpMessage, boolean encrypt) { 91 | if (encrypt) { 92 | httpMessage.encryptHttpMessage(); 93 | } 94 | return writeSocket(outputStream, httpMessage.toString()); 95 | } 96 | 97 | /** 98 | * 关闭Socket 99 | * 100 | * @param sockets 101 | */ 102 | public static void closeSocket(Socket... sockets) { 103 | if (sockets != null && sockets.length > 0) { 104 | int length = sockets.length; 105 | for (int i = 0; i < length; i++) { 106 | Socket socket = sockets[i]; 107 | if (socket != null && !socket.isClosed()) { 108 | try { 109 | socket.close(); 110 | } catch (IOException e) { 111 | e.printStackTrace(); 112 | } 113 | } 114 | } 115 | } 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/httpMessage/HttpResponseMessage.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer.httpMessage; 2 | 3 | import java.io.InputStream; 4 | import java.util.Map.Entry; 5 | import java.util.Set; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import com.hekewangzi.httpProxyServer.constants.HttpResponseStatus; 11 | import com.hekewangzi.httpProxyServer.httpMessage.exception.BuildHttpMessageError; 12 | import com.hekewangzi.httpProxyServer.httpMessage.startLine.ResponseStartLine; 13 | import com.hekewangzi.httpProxyServer.httpMessage.startLine.StartLine; 14 | import com.hekewangzi.httpProxyServer.utils.PasswordUtils; 15 | 16 | /** 17 | * Http响应报文 18 | * 19 | * @author qq 20 | * 21 | */ 22 | /** 23 | * @author qq 24 | * 25 | */ 26 | public class HttpResponseMessage extends HttpMessage { 27 | private final static Logger log = LoggerFactory.getLogger(HttpResponseMessage.class); 28 | 29 | /* 30 | * constructor 31 | */ 32 | private HttpResponseMessage() { 33 | super(); 34 | } 35 | 36 | public HttpResponseMessage(InputStream inputStream) throws BuildHttpMessageError { 37 | super(inputStream); 38 | } 39 | 40 | public HttpResponseMessage(HttpResponseStatus httpResponseStatus) { 41 | this.buildByHttpRespnseState(httpResponseStatus); 42 | } 43 | 44 | /** 45 | * 根据HttpResponseStatus构建 46 | * 47 | * @param httpResponseStatus 48 | * @return 49 | */ 50 | private HttpResponseMessage buildByHttpRespnseState(HttpResponseStatus httpResponseStatus) { 51 | ResponseStartLine responseStartLine = new ResponseStartLine(null); 52 | 53 | responseStartLine.setProtocol("HTTP"); 54 | responseStartLine.setMasterVersion(1); 55 | responseStartLine.setMinorVersion(1); 56 | responseStartLine.setStatus(httpResponseStatus.getStatus()); 57 | responseStartLine.setDescription(httpResponseStatus.getDescription()); 58 | 59 | super.setStartLine(responseStartLine); 60 | 61 | return this; 62 | } 63 | 64 | @Override 65 | public StartLine buildStartLien(String startLine) { 66 | return new ResponseStartLine(startLine); 67 | } 68 | 69 | /** 70 | * 是否包含实体 71 | * 72 | * true:GET、POST、PUT、TRACE、DELETE、CONNECT 73 | * 74 | * @return 75 | */ 76 | @Override 77 | public boolean isSupportBody() { 78 | // 报空指针异常 79 | // RequestMethod requestMethod = ((RequestStartLine) 80 | // super.getStartLine()).getMethod(); 81 | // log.info(requestMethod.toString()); 82 | // 83 | // switch (requestMethod) { 84 | // case GET: 85 | // case POST: 86 | // case PUT: 87 | // case TRACE: 88 | // case DELETE: 89 | // case CONNECT: 90 | // return true; 91 | // default: 92 | // return false; 93 | // } 94 | return true; 95 | } 96 | 97 | /** 98 | * 加密响应报文 99 | * 100 | * @return 101 | */ 102 | @Override 103 | public HttpMessage encryptHttpMessage() { 104 | /* 105 | * 加密头部 106 | */ 107 | if (!super.headerIsEmpty()) { 108 | Set> entrys = super.getHeaders().entrySet(); 109 | for (Entry entry : entrys) { 110 | super.addHeader(entry.getKey(), PasswordUtils.base64Encrypt(entry.getValue())); 111 | } 112 | } 113 | return this; 114 | } 115 | 116 | /** 117 | * 解密响应报文 118 | * 119 | * @return 120 | */ 121 | @Override 122 | public HttpMessage decryptHttpMessage() { 123 | /* 124 | * 解密头部 125 | */ 126 | if (!super.headerIsEmpty()) { 127 | Set> entrys = super.getHeaders().entrySet(); 128 | for (Entry entry : entrys) { 129 | super.addHeader(entry.getKey(), PasswordUtils.base64Decrypt(entry.getValue())); 130 | } 131 | } 132 | return this; 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/threads/HttpProxyThread.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer.threads; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.net.Socket; 6 | import java.net.SocketException; 7 | 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import com.hekewangzi.httpProxyServer.constants.Properties; 12 | import com.hekewangzi.httpProxyServer.constants.RequestMethod; 13 | import com.hekewangzi.httpProxyServer.httpMessage.HttpRequestMessage; 14 | import com.hekewangzi.httpProxyServer.httpMessage.exception.BuildHttpMessageError; 15 | import com.hekewangzi.httpProxyServer.httpMessage.exception.ConnectServerError; 16 | import com.hekewangzi.httpProxyServer.httpMessage.startLine.RequestStartLine; 17 | import com.hekewangzi.httpProxyServer.proxy.Proxy; 18 | import com.hekewangzi.httpProxyServer.proxy.ServerProxy; 19 | import com.hekewangzi.httpProxyServer.utils.SocketUtil; 20 | 21 | /** 22 | * Http代理线程 23 | * 24 | * @author qq 25 | * 26 | */ 27 | public class HttpProxyThread extends Thread { 28 | private final static Logger log = LoggerFactory.getLogger(HttpProxyThread.class); 29 | 30 | /* 31 | * 客户端相关 32 | */ 33 | /** 34 | * 客户端Socket 35 | */ 36 | private Socket clientSocket; 37 | 38 | /** 39 | * 客户端输入流 40 | */ 41 | private InputStream clientInputStream; 42 | 43 | /* 44 | * 服务端相关 45 | */ 46 | /** 47 | * 服务端Socket 48 | */ 49 | private Socket serverSocket; 50 | 51 | /* 52 | * constructor 53 | */ 54 | private HttpProxyThread() { 55 | } 56 | 57 | public HttpProxyThread(Socket clientSocket) { 58 | super(); 59 | this.clientSocket = clientSocket; 60 | try { 61 | this.clientInputStream = clientSocket.getInputStream(); 62 | } catch (IOException e) { 63 | e.printStackTrace(); 64 | } 65 | } 66 | 67 | @Override 68 | public void run() { 69 | /* 70 | * 解析客户端请求 71 | */ 72 | HttpRequestMessage requestMessage = null; 73 | try { 74 | requestMessage = new HttpRequestMessage(clientInputStream); 75 | } catch (BuildHttpMessageError e1) { 76 | e1.printStackTrace(); 77 | 78 | SocketUtil.closeSocket(clientSocket, serverSocket); 79 | return; 80 | } 81 | requestMessage = (HttpRequestMessage) requestMessage.decryptHttpMessage(); // 解密 82 | 83 | // 请求方法 84 | RequestMethod httpRequestMethod = ((RequestStartLine) requestMessage.getStartLine()).getMethod(); 85 | 86 | System.out.println("[原始请求:]"); 87 | System.out.print(requestMessage); 88 | System.out.println("----------"); 89 | 90 | /* 91 | * 连接服务器 92 | */ 93 | String hostHeader = requestMessage.getHeader("host"); // 如果没有Host首部,则从Url中解析 94 | String host = hostHeader; 95 | int port = 80; // 默认端口 96 | if (RequestMethod.CONNECT == httpRequestMethod) { // https 97 | port = 443; 98 | } 99 | if (hostHeader.contains(":")) { 100 | String[] hostArr = hostHeader.split(":"); 101 | host = hostArr[0]; 102 | port = Integer.parseInt(hostArr[1]); 103 | } 104 | 105 | /* 106 | * 107 | */ 108 | try { 109 | this.serverSocket = SocketUtil.connectServer(host, port, Properties.ServerConnectTimeout); 110 | } catch (ConnectServerError e) { 111 | e.printStackTrace(); 112 | 113 | SocketUtil.closeSocket(clientSocket, serverSocket); 114 | return; 115 | } 116 | 117 | if (RequestMethod.CONNECT != httpRequestMethod) { // 代理http 118 | try { 119 | this.clientSocket.setSoTimeout(Properties.ClientSoceketReadTimeout); // 设置读取浏览器Socket超时时间 120 | this.serverSocket.setSoTimeout(Properties.ServerSocketReadTimeout); // 设置读取代理服务器Socket超时时间 121 | } catch (SocketException e) { 122 | e.printStackTrace(); 123 | } 124 | } 125 | 126 | /* 127 | * 转发流量 128 | */ 129 | Proxy proxy = new ServerProxy(clientSocket, serverSocket, requestMessage); 130 | proxy.proxy(); 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/utils/PasswordUtils.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer.utils; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | 5 | import org.apache.commons.codec.DecoderException; 6 | import org.apache.commons.codec.binary.Base64; 7 | import org.apache.commons.codec.binary.Hex; 8 | import org.apache.commons.codec.digest.DigestUtils; 9 | 10 | /** 11 | * 加密解密 工具类 12 | * 13 | * @author qq 14 | * 15 | */ 16 | public class PasswordUtils { 17 | /***************************************************************************************************** 18 | * base64加密解密 19 | *****************************************************************************************************/ 20 | /** 21 | * base64加密 22 | * 23 | * @param encryptContent 24 | * 加密内容 25 | * 26 | * @return 加密后的内容 27 | */ 28 | public static String base64Encrypt(String encryptContent) { 29 | Base64 base64 = new Base64(); 30 | try { 31 | return base64.encodeToString(encryptContent.getBytes("UTF-8")); 32 | } catch (UnsupportedEncodingException e) { 33 | throw new IllegalArgumentException("加密失败", e); 34 | } 35 | } 36 | 37 | /** 38 | * base64解密 39 | * 40 | * @param decryptContent 41 | * 解密内容 42 | * 43 | * @return 解密后的内容 44 | */ 45 | public static String base64Decrypt(String decryptContent) { 46 | byte[] bytes = new Base64().decodeBase64(decryptContent); 47 | return new String(bytes); 48 | } 49 | 50 | /***************************************************************************************************** 51 | * Hex加密解密 52 | *****************************************************************************************************/ 53 | /** 54 | * Hex加密 55 | * 56 | * @param encryptContent 57 | * 加密内容 58 | * 59 | * @return 加密后的内容 60 | */ 61 | public static String hexEncrypt(String encryptContent) { 62 | try { 63 | return Hex.encodeHexString(encryptContent.getBytes("UTF-8")); 64 | } catch (UnsupportedEncodingException e) { 65 | throw new IllegalArgumentException("加密失败", e); 66 | } 67 | } 68 | 69 | /** 70 | * Hex解密 71 | * 72 | * @param decryptContent 73 | * 解密内容 74 | * 75 | * @return 解密后的内容 76 | */ 77 | public static String hexDecrypt(String decryptContent) { 78 | Hex hex = new Hex(); 79 | try { 80 | byte[] bytes = hex.decode(decryptContent.getBytes()); 81 | return new String(bytes, "UTF-8"); 82 | } catch (DecoderException e) { 83 | throw new IllegalArgumentException("解密失败", e); 84 | } catch (UnsupportedEncodingException e) { 85 | throw new IllegalArgumentException("解密失败", e); 86 | } 87 | } 88 | 89 | /***************************************************************************************************** 90 | * MD5加密 91 | *****************************************************************************************************/ 92 | /** 93 | * MD5加密 94 | * 95 | * @param encryptContent 96 | * 加密内容 97 | * 98 | * @return 加密后的内容 99 | */ 100 | public static String md5Encrypt(String encryptContent) { 101 | try { 102 | return DigestUtils.md5Hex(encryptContent.getBytes("UTF-8")); 103 | } catch (UnsupportedEncodingException e) { 104 | throw new IllegalArgumentException("加密失败", e); 105 | } 106 | } 107 | 108 | /***************************************************************************************************** 109 | * SHA加密 110 | *****************************************************************************************************/ 111 | /** 112 | * SHA加密 113 | * 114 | * @param encryptContent 115 | * 加密内容 116 | * 117 | * @return 加密后的内容 118 | */ 119 | public static String shaEncrypt(String encryptContent) { 120 | try { 121 | return DigestUtils.shaHex(encryptContent.getBytes("UTF-8")); 122 | } catch (UnsupportedEncodingException e) { 123 | throw new IllegalArgumentException("加密失败", e); 124 | } 125 | } 126 | 127 | /***************************************************************************************************** 128 | * 129 | *****************************************************************************************************/ 130 | 131 | public static void main(String[] args) { 132 | String xx = base64Decrypt( 133 | "R0VUIGh0dHA6Ly9jLmJpYW5jaGVuZy5uZXQvY3BwLyBIVFRQLzEuMQ0KYWNjZXB0LWxhbmd1YWdlOiB6aC1DTix6aDtxPTAuOA0KY29va2llOiBVTV9kaXN0aW5jdGlkPTE1YWRhMmJmODljNGRhLTAzZThmOWYzY2U4YzdiLTFkM2U2ODUwLTEzYzY4MC0xNWFkYTJiZjg5ZDY3NDsgQ05aWkRBVEEzODc1NDg0PWNuenpfZWlkJTNEMzUzMzAxMzI2LTE0ODk3MTM5NTUtJTI2bnRpbWUlM0QxNDkwMDIzMTU5DQpob3N0OiBjLmJpYW5jaGVuZy5uZXQNCnVwZ3JhZGUtaW5zZWN1cmUtcmVxdWVzdHM6IDENCmNvbm5lY3Rpb246IGNsb3NlDQpjYWNoZS1jb250cm9sOiBtYXgtYWdlPTANCmFjY2VwdC1lbmNvZGluZzogZ3ppcCwgZGVmbGF0ZSwgc2RjaA0KdXNlci1hZ2VudDogTW96aWxsYS81LjAgKE1hY2ludG9zaDsgSW50ZWwgTWFjIE9TIFggMTBfMTFfNikgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzU2LjAuMjkyNC44NyBTYWZhcmkvNTM3LjM2DQphY2NlcHQ6IHRleHQvaHRtbCxhcHBsaWNhdGlvbi94aHRtbCt4bWwsYXBwbGljYXRpb24veG1sO3E9MC45LGltYWdlL3dlYnAsKi8qO3E9MC44DQoNCg=="); 134 | System.out.print(xx); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.hekewangzi 6 | httpProxyServer 7 | 0.0.1-SNAPSHOT 8 | jar 9 | 10 | http-proxy-server 11 | http://maven.apache.org 12 | 13 | 14 | 15 | 3.8.1 16 | 17 | 18 | 19 | 20 | 1.7.13 21 | 22 | 23 | 3.4 24 | 1.9.2 25 | 2.4 26 | 1.3.1 27 | 1.10 28 | 1.6 29 | 3.4 30 | 31 | 32 | 33 | 3.2 34 | 35 | 2.4 36 | 37 | 2.10.3 38 | 39 | 2.8 40 | 41 | 2.6 42 | 43 | 44 | 1.7 45 | 46 | 47 | 48 | 49 | 50 | junit 51 | junit 52 | ${junit.version} 53 | test 54 | 55 | 56 | 57 | 58 | 59 | org.slf4j 60 | slf4j-api 61 | ${slf4j.version} 62 | 63 | 64 | 65 | 66 | org.slf4j 67 | slf4j-log4j12 68 | ${slf4j.version} 69 | 70 | 71 | 72 | 73 | org.slf4j 74 | jcl-over-slf4j 75 | ${slf4j.version} 76 | 77 | 78 | 79 | 80 | org.slf4j 81 | jul-to-slf4j 82 | ${slf4j.version} 83 | 84 | 85 | 86 | 87 | org.apache.commons 88 | commons-lang3 89 | ${commons-lang3.version} 90 | 91 | 92 | commons-beanutils 93 | commons-beanutils 94 | ${commons-beanutils.version} 95 | 96 | 97 | commons-io 98 | commons-io 99 | ${commons-io.version} 100 | 101 | 102 | commons-fileupload 103 | commons-fileupload 104 | ${commons-fileupload.version} 105 | 106 | 107 | commons-codec 108 | commons-codec 109 | ${commons-codec.version} 110 | 111 | 112 | commons-pool 113 | commons-pool 114 | ${commons-pool.version} 115 | 116 | 117 | commons-net 118 | commons-net 119 | ${commons-net.version} 120 | 121 | 122 | 123 | 124 | HttpProxyServer 125 | 126 | 127 | 128 | 129 | org.apache.maven.plugins 130 | maven-compiler-plugin 131 | ${maven-compiler-plugin.version} 132 | 133 | 134 | ${jdk.version} 135 | 136 | 137 | ${jdk.version} 138 | true 139 | 140 | 141 | 142 | 143 | 144 | org.apache.maven.plugins 145 | maven-dependency-plugin 146 | ${maven-dependency-plugin} 147 | 148 | 149 | copy-dependencies 150 | prepare-package 151 | 152 | copy-dependencies 153 | 154 | 155 | 156 | ${project.build.directory}/libs 157 | false 158 | false 159 | true 160 | compile 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | org.apache.maven.plugins 169 | maven-jar-plugin 170 | ${maven-jar-plugin} 171 | 172 | 173 | 174 | 175 | com.hekewangzi.httpProxyServer.HttpProxyServerMain 176 | true 177 | 178 | libs/ 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/proxy/Proxy.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer.proxy; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.net.Socket; 7 | 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import com.hekewangzi.httpProxyServer.constants.RequestMethod; 12 | import com.hekewangzi.httpProxyServer.httpMessage.HttpRequestMessage; 13 | import com.hekewangzi.httpProxyServer.httpMessage.HttpResponseMessage; 14 | import com.hekewangzi.httpProxyServer.httpMessage.exception.BuildHttpMessageError; 15 | import com.hekewangzi.httpProxyServer.httpMessage.startLine.RequestStartLine; 16 | import com.hekewangzi.httpProxyServer.threadPools.ThreadPoolManager; 17 | import com.hekewangzi.httpProxyServer.threads.https.ClientTranslateHttpsToServerThread; 18 | import com.hekewangzi.httpProxyServer.threads.https.ServerTranslateHttpsToClientThread; 19 | import com.hekewangzi.httpProxyServer.utils.SocketUtil; 20 | 21 | /** 22 | * 代理类 23 | * 24 | * @author qq 25 | * 26 | */ 27 | public class Proxy { 28 | private final static Logger log = LoggerFactory.getLogger(Proxy.class); 29 | 30 | /* 31 | * 配置属性 32 | */ 33 | /** 34 | * 是否支持压缩(默认:支持) 35 | * 36 | * 不压缩的话,可对拦截的Http数据流进行修改 37 | */ 38 | protected boolean enabledAcceptEncoding = true; 39 | 40 | /** 41 | * 是否支持长连接(默认:不支持) 42 | */ 43 | protected boolean enabledKeepAlive = false; 44 | 45 | /** 46 | * 转发时是否加密请求(默认: 不加密) 47 | */ 48 | private boolean encryptRequest = false; 49 | 50 | /** 51 | * 转发时是否加密响应(默认: 不加密) 52 | */ 53 | private boolean encryptResponse = false; 54 | 55 | /** 56 | * 接收响应时是否解密响应(默认: 不解密) 57 | */ 58 | private boolean decryptResponse = false; 59 | 60 | /* 61 | * 客户端相关 62 | */ 63 | /** 64 | * 客户端Socket 65 | */ 66 | protected Socket clientSocket; 67 | 68 | /** 69 | * 客户端输出流 70 | */ 71 | protected OutputStream clientOutputStream; 72 | 73 | /* 74 | * 服务端相关 75 | */ 76 | /** 77 | * 服务端Socket 78 | */ 79 | protected Socket serverSocket; 80 | 81 | /** 82 | * 服务端输入流 83 | */ 84 | protected InputStream serverInputStream; 85 | 86 | /** 87 | * 服务端输出流 88 | */ 89 | protected OutputStream serverOutputStream; 90 | 91 | /* 92 | * 其他 93 | */ 94 | protected HttpRequestMessage requestMessage; 95 | 96 | /* 97 | * constructor 98 | */ 99 | private Proxy() { 100 | 101 | } 102 | 103 | public Proxy(Socket clientSocket, Socket serverSocket, HttpRequestMessage requestMessage) { 104 | this.requestMessage = requestMessage; 105 | 106 | this.clientSocket = clientSocket; 107 | 108 | this.serverSocket = serverSocket; 109 | try { 110 | this.clientOutputStream = clientSocket.getOutputStream(); 111 | 112 | this.serverInputStream = serverSocket.getInputStream(); 113 | this.serverOutputStream = serverSocket.getOutputStream(); 114 | } catch (IOException e) { 115 | e.printStackTrace(); 116 | } 117 | 118 | /* 119 | * 初始化http配置 120 | */ 121 | RequestMethod httpRequestMethod = ((RequestStartLine) requestMessage.getStartLine()).getMethod(); 122 | if (RequestMethod.CONNECT != httpRequestMethod) { // 代理http 123 | this.initHttp(); 124 | } 125 | } 126 | 127 | /** 128 | * 初始化http 129 | */ 130 | private void initHttp() { 131 | // 删除Connectino首部列出的所有首部字段 132 | String connectionHeader = requestMessage.getHeader("Connection"); 133 | if (connectionHeader != null) { 134 | String[] connectionHeaderValues = connectionHeader.split(","); 135 | requestMessage.removeHeaders(connectionHeaderValues); 136 | } 137 | 138 | if (!enabledKeepAlive) { // 不支持长连接 139 | requestMessage.addHeader("Connection", "close"); // 一次请求马上关闭连接 140 | } else { 141 | requestMessage.addHeader("Connection", requestMessage.getHeader("Proxy-Connection")); 142 | } 143 | 144 | requestMessage.removeHeader("Proxy-Connection"); 145 | 146 | if (!enabledAcceptEncoding) { // 不支持压缩 147 | requestMessage.removeHeader("Accept-Encoding"); 148 | } 149 | 150 | System.out.println("[修改后请求: ]"); 151 | System.out.print(requestMessage); 152 | System.out.println("----------"); 153 | } 154 | 155 | /** 156 | * http/https代理 157 | */ 158 | public void proxy() { 159 | RequestMethod httpRequestMethod = ((RequestStartLine) requestMessage.getStartLine()).getMethod(); 160 | if (RequestMethod.CONNECT == httpRequestMethod) { // 代理https 161 | this.proxyHttps(); 162 | } else { // 代理http 163 | this.proxyHttp(); 164 | } 165 | } 166 | 167 | /** 168 | * 代理Http 169 | * 170 | * 1、将客户端的Http数据流输出给服务端 171 | * 172 | * 2、将服务端的Http数据流输出给客户端 173 | * 174 | * 3、关闭客户端Socket、关闭服务端Socket 175 | * 176 | * @param requestMessage 177 | */ 178 | public void proxyHttp() { 179 | /* 180 | * 将客户端数据发给服务端 181 | */ 182 | if (!SocketUtil.writeSocket(serverOutputStream, requestMessage, this.encryptRequest)) { 183 | return; 184 | } 185 | 186 | /* 187 | * 将服务端数据转发给客户端 188 | */ 189 | HttpResponseMessage responseMessage = null; 190 | try { 191 | responseMessage = new HttpResponseMessage(serverInputStream); 192 | } catch (BuildHttpMessageError e) { 193 | e.printStackTrace(); 194 | return; 195 | } 196 | if (this.decryptResponse) { 197 | responseMessage.decryptHttpMessage(); 198 | } 199 | 200 | if (!SocketUtil.writeSocket(clientOutputStream, responseMessage, this.encryptResponse)) { 201 | return; 202 | } 203 | 204 | // responseMessage.removeHeader("Set-Cookie"); // 有多个Set-Cookie 205 | // responseMessage.addHeader("Cache-Control", "no-cache"); 206 | 207 | /* 208 | * 关闭连接 209 | */ 210 | SocketUtil.closeSocket(this.clientSocket, this.serverSocket); 211 | } 212 | 213 | /** 214 | * 代理Https 215 | * 216 | * 1、响应客户端Web隧道建立成功 217 | * 218 | * 2、将客户端的Https数据流输出给服务端(新建一条线程) 219 | * 220 | * 3、将服务端的Http数据流输出给客户端(新建一条线程) 221 | * 222 | */ 223 | public void proxyHttps() { 224 | System.out.println("[代理https请求开始...]"); 225 | 226 | /* 227 | * 将客户端数据转发给服务端 228 | */ 229 | Thread clientToServer = new ClientTranslateHttpsToServerThread(clientSocket, serverSocket); 230 | ThreadPoolManager.execute(clientToServer); 231 | 232 | /* 233 | * 将服务端数据转发给客户端 234 | */ 235 | Thread serverToClient = new ServerTranslateHttpsToClientThread(clientSocket, serverSocket); 236 | ThreadPoolManager.execute(serverToClient); 237 | } 238 | 239 | /* 240 | * getter、setter 241 | */ 242 | public boolean isEnabledAcceptEncoding() { 243 | return enabledAcceptEncoding; 244 | } 245 | 246 | public void setEnabledAcceptEncoding(boolean enabledAcceptEncoding) { 247 | this.enabledAcceptEncoding = enabledAcceptEncoding; 248 | } 249 | 250 | public boolean isEnabledKeepAlive() { 251 | return enabledKeepAlive; 252 | } 253 | 254 | public void setEnabledKeepAlive(boolean enabledKeepAlive) { 255 | this.enabledKeepAlive = enabledKeepAlive; 256 | } 257 | 258 | public boolean isEncryptRequest() { 259 | return encryptRequest; 260 | } 261 | 262 | public void setEncryptRequest(boolean encryptRequest) { 263 | this.encryptRequest = encryptRequest; 264 | } 265 | 266 | public boolean isEncryptResponse() { 267 | return encryptResponse; 268 | } 269 | 270 | public void setEncryptResponse(boolean encryptResponse) { 271 | this.encryptResponse = encryptResponse; 272 | } 273 | 274 | public boolean isDecryptResponse() { 275 | return decryptResponse; 276 | } 277 | 278 | public void setDecryptResponse(boolean decryptResponse) { 279 | this.decryptResponse = decryptResponse; 280 | } 281 | 282 | } 283 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/httpMessage/HttpMessage.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer.httpMessage; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.net.SocketTimeoutException; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.Map.Entry; 9 | import java.util.Set; 10 | 11 | import org.apache.commons.lang3.StringUtils; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import com.hekewangzi.httpProxyServer.httpMessage.exception.BuildHttpMessageError; 16 | import com.hekewangzi.httpProxyServer.httpMessage.startLine.StartLine; 17 | 18 | /** 19 | * 抽象Http报文 20 | * 21 | * @author qq 22 | * 23 | */ 24 | public abstract class HttpMessage { 25 | private final static Logger log = LoggerFactory.getLogger(HttpMessage.class); 26 | 27 | /** 28 | * 起始行 29 | */ 30 | private StartLine startLine; 31 | 32 | /** 33 | * 首部 34 | * 35 | * key全部转为小写 36 | */ 37 | private Map headers = new HashMap(); 38 | 39 | /** 40 | * 实体 41 | */ 42 | private String body; 43 | 44 | /* 45 | * constructor 46 | */ 47 | public HttpMessage() { 48 | 49 | } 50 | 51 | public HttpMessage(InputStream inputStream) throws BuildHttpMessageError { 52 | this.buildHttpMessage(inputStream); 53 | } 54 | 55 | /* 56 | * *********************************************************************** 57 | * 起始行相关方法 58 | * *********************************************************************** 59 | */ 60 | /** 61 | * 获取起始行 62 | * 63 | * @return 64 | */ 65 | public StartLine getStartLine() { 66 | return this.startLine; 67 | } 68 | 69 | /** 70 | * 设置起始行 71 | * 72 | * @param startLine 73 | */ 74 | public void setStartLine(StartLine startLine) { 75 | this.startLine = startLine; 76 | } 77 | 78 | /* 79 | * *********************************************************************** 80 | * 首部相关方法 81 | * *********************************************************************** 82 | */ 83 | /** 84 | * 添加首部 85 | * 86 | * @param key 87 | * @param value 88 | */ 89 | public void addHeader(String key, String value) { 90 | if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(value)) { 91 | this.headers.put(key.trim().toLowerCase(), value.trim()); 92 | } 93 | } 94 | 95 | /** 96 | * 批量添加首部 97 | * 98 | * @param keyValues 99 | * 格式:key1,value1,key2,value2 100 | */ 101 | public void addHeaders(String... keyValues) { 102 | if (keyValues != null && keyValues.length >= 2) { // 最少两个参数 103 | int length = keyValues.length; 104 | if (length % 2 != 0) { // 奇数个,去掉最后一个 105 | --length; 106 | } 107 | 108 | for (int i = 0; i < length; i += 2) { 109 | this.addHeader(keyValues[i], keyValues[i + 1]); 110 | } 111 | } 112 | } 113 | 114 | /** 115 | * 批量添加首部 116 | * 117 | * @param headers 118 | */ 119 | public void addHeaders(Map headers) { 120 | if (headers != null && !headers.isEmpty()) { 121 | Set> entrys = headers.entrySet(); 122 | for (Entry entry : entrys) { 123 | this.addHeader(entry.getKey(), entry.getValue()); 124 | } 125 | } 126 | } 127 | 128 | /** 129 | * 获取首部 130 | * 131 | * @param key 132 | * @return 133 | */ 134 | public String getHeader(String key) { 135 | return StringUtils.isNotBlank(key) ? headers.get(key.trim().toLowerCase()) : null; 136 | } 137 | 138 | /** 139 | * 获取所有头部 140 | * 141 | * @return 142 | */ 143 | public Map getHeaders() { 144 | return this.headers; 145 | } 146 | 147 | /** 148 | * 删除首部 149 | * 150 | * @param key 151 | */ 152 | public void removeHeader(String key) { 153 | if (StringUtils.isNotBlank(key)) { 154 | headers.remove(key.trim().toLowerCase()); 155 | } 156 | } 157 | 158 | /** 159 | * 批量删除首部 160 | * 161 | * @param keys 162 | */ 163 | public void removeHeaders(String... keys) { 164 | if (keys != null && keys.length > 0) { 165 | int length = keys.length; 166 | for (int i = 0; i < length; i++) { 167 | this.removeHeader(keys[i]); 168 | } 169 | } 170 | } 171 | 172 | /** 173 | * 是否有首部 174 | * 175 | * @return true:无首部 176 | */ 177 | public boolean headerIsEmpty() { 178 | return headers.isEmpty(); 179 | } 180 | 181 | /* 182 | * *********************************************************************** 183 | * 实体相关方法 184 | * *********************************************************************** 185 | */ 186 | /** 187 | * 获取实体 188 | * 189 | * @return 190 | */ 191 | public String getBody() { 192 | return body; 193 | } 194 | 195 | /** 196 | * 设置实体 197 | * 198 | * @param body 199 | */ 200 | public void setBody(String body) { 201 | this.body = body; 202 | } 203 | 204 | /** 205 | * 是否支持实体 206 | * 207 | * @return 208 | */ 209 | public abstract boolean isSupportBody(); 210 | 211 | /* 212 | * *********************************************************************** 213 | * 解析相关方法 214 | * *********************************************************************** 215 | */ 216 | /** 217 | * 构建起始行 218 | * 219 | * @param inputStream 220 | * @return 输入流 221 | * @throws BuildHttpMessageError 222 | * @throws IOException 223 | */ 224 | private String buildStartLine(InputStream inputStream) throws BuildHttpMessageError { 225 | StringBuffer line = new StringBuffer(); 226 | int byteOfData = -1; 227 | 228 | try { 229 | while ((byteOfData = inputStream.read()) != -1) { 230 | char readChar = (char) byteOfData; 231 | System.out.print(readChar); 232 | if (readChar == '\n') { // 行尾返回 233 | break; 234 | } else { 235 | if (readChar == '\r') { 236 | continue; 237 | } 238 | line.append(readChar); 239 | } 240 | } 241 | } catch (SocketTimeoutException e) { // 读取超时 242 | e.printStackTrace(); 243 | throw new BuildHttpMessageError("buildStartLine SocketTimeoutException"); 244 | } catch (IOException e) { 245 | e.printStackTrace(); 246 | throw new BuildHttpMessageError("buildStartLine IOException"); 247 | } 248 | return line.toString().trim(); 249 | } 250 | 251 | /** 252 | * 构建StartLine 253 | * 254 | * @param startLine 255 | * @return 256 | */ 257 | public abstract StartLine buildStartLien(String startLine); 258 | 259 | /** 260 | * 构建头部 261 | * 262 | * @param inputStream 263 | * 输入流 264 | * @return 265 | * @throws BuildHttpMessageError 266 | */ 267 | private void buildHeader(InputStream inputStream) throws BuildHttpMessageError { 268 | StringBuffer header = new StringBuffer(); // 每行的数据 269 | int byteOfData = -1; 270 | 271 | try { 272 | while ((byteOfData = inputStream.read()) != -1) { 273 | char readChar = (char) byteOfData; 274 | if (readChar == '\n') { // 行尾 275 | if (header.length() == 0) { // 该行长度为0,返回 276 | break; 277 | } 278 | 279 | String headerStr = header.toString(); 280 | int colonIndex = headerStr.indexOf(":");// 不能使用split(":"),因为value中可能包含":" 281 | this.addHeader(headerStr.substring(0, colonIndex), headerStr.substring(colonIndex + 1)); 282 | 283 | header.setLength(0); // 清空line 284 | } else { // 非行尾 285 | if (readChar == '\r') { 286 | continue; 287 | } 288 | header.append(readChar); 289 | } 290 | } 291 | } catch (SocketTimeoutException e) { // 读取超时 292 | e.printStackTrace(); 293 | throw new BuildHttpMessageError("buildHeader SocketTimeoutException"); 294 | } catch (IOException e) { 295 | e.printStackTrace(); 296 | throw new BuildHttpMessageError("buildHeader IOException"); 297 | } 298 | } 299 | 300 | /** 301 | * 构建实体 302 | * 303 | * @param inputStream 304 | * 输入流 305 | * @return 306 | * @throws IOException 307 | */ 308 | private void buildBody(InputStream inputStream) { 309 | StringBuffer line = new StringBuffer(); 310 | int byteOfData = -1; 311 | 312 | try { 313 | while ((byteOfData = inputStream.read()) != -1) { 314 | char readChar = (char) byteOfData; 315 | System.out.print(readChar); 316 | line.append(readChar); 317 | } 318 | } catch (SocketTimeoutException e) { // 读取超时 319 | e.printStackTrace(); 320 | } catch (IOException e) { 321 | e.printStackTrace(); 322 | } 323 | 324 | String lineStr = line.toString(); // 不能trim(),trim()之后长度就不一样了,mlgbd 325 | this.setBody(lineStr); 326 | } 327 | 328 | /** 329 | * 根据输入流构建HttpMessage报文 330 | * 331 | * @param inputStream 332 | * @throws BuildHttpMessageError 333 | */ 334 | public HttpMessage buildHttpMessage(InputStream inputStream) throws BuildHttpMessageError { 335 | if (inputStream == null) { 336 | throw new BuildHttpMessageError("inputStream is null"); 337 | } 338 | 339 | /* 340 | * 构建起始行 341 | */ 342 | String startLineStr = this.buildStartLine(inputStream); 343 | StartLine startLine = this.buildStartLien(startLineStr); 344 | this.startLine = startLine; 345 | 346 | if (StringUtils.isBlank(startLineStr) || this.startLine == null) { 347 | throw new BuildHttpMessageError("startLine is null"); 348 | } 349 | 350 | /* 351 | * 构建头部 352 | */ 353 | this.buildHeader(inputStream); 354 | 355 | /* 356 | * 构建实体 357 | */ 358 | if (this.isSupportBody()) { 359 | this.buildBody(inputStream); 360 | } 361 | 362 | return this; 363 | } 364 | 365 | /* 366 | * *********************************************************************** 367 | * 加密、解密方法 368 | * *********************************************************************** 369 | */ 370 | /** 371 | * 加密报文 372 | * 373 | * @return 374 | */ 375 | public abstract HttpMessage encryptHttpMessage(); 376 | 377 | /** 378 | * 解密报文 379 | * 380 | * @return 381 | */ 382 | public abstract HttpMessage decryptHttpMessage(); 383 | 384 | /* 385 | * *********************************************************************** 386 | * 其他方法 387 | * *********************************************************************** 388 | */ 389 | /** 390 | * 返回Http报文 391 | * 392 | * @return 按Http报文格式输出字符串 393 | */ 394 | @Override 395 | public String toString() { 396 | StringBuffer httpMessage = new StringBuffer(); 397 | 398 | if (this.getStartLine() == null) { 399 | return null; 400 | } 401 | 402 | /* 403 | * 起始行 404 | */ 405 | httpMessage.append(this.getStartLine()); 406 | httpMessage.append("\r\n"); 407 | 408 | /* 409 | * 首部 410 | */ 411 | if (!this.headerIsEmpty()) { 412 | Set> entrys = headers.entrySet(); 413 | for (Entry entry : entrys) { 414 | httpMessage.append(entry.getKey()); 415 | httpMessage.append(": "); 416 | httpMessage.append(entry.getValue()); 417 | httpMessage.append("\r\n"); 418 | } 419 | } 420 | httpMessage.append("\r\n"); 421 | 422 | /* 423 | * 实体 424 | */ 425 | if (StringUtils.isNotBlank(this.body)) { 426 | httpMessage.append(this.body); 427 | } 428 | 429 | return httpMessage.toString(); 430 | } 431 | 432 | } 433 | -------------------------------------------------------------------------------- /src/main/java/com/hekewangzi/httpProxyServer/constants/HttpResponseStatus.java: -------------------------------------------------------------------------------- 1 | package com.hekewangzi.httpProxyServer.constants; 2 | 3 | /** 4 | * Http响应状态码 enum 5 | * 6 | * @author qq 7 | * 8 | */ 9 | public enum HttpResponseStatus { 10 | /* 11 | * *********************************************************************** 12 | * 1xx 消息 13 | * 14 | * 这一类型的状态码,代表请求已被接受,需要继续处理。这类响应是临时响应,只包含状态行和某些可选的响应头信息,并以空行结束。 15 | * 16 | * 由于HTTP/1.0协议中没有定义任何1xx状态码,所以除非在某些试验条件下,服务器禁止向此类客户端发送1xx响应。 17 | * 18 | * 这些状态码代表的响应都是信息性的,标示客户应该采取的其他行动。 19 | * *********************************************************************** 20 | */ 21 | /** 22 | * 客户端应当继续发送请求。这个临时响应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝。客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽略这个响应。服务器必须在请求完成后向客户端发送一个最终响应 23 | */ 24 | _100(100, "Continue"), 25 | 26 | /** 27 | * 服务器已经理解了客户端的请求,并将通过Upgrade消息头通知客户端采用不同的协议来完成这个请求。在发送完这个响应最后的空行后,服务器将会切换到在Upgrade消息头中定义的那些协议。 28 | * 29 | * 只有在切换新的协议更有好处的时候才应该采取类似措施。例如,切换到新的HTTP版本(如HTTP/2)比旧版本更有优势,或者切换到一个实时且同步的协议(如WebSocket)以传送利用此类特性的资源。 30 | */ 31 | _101(101, "Switching Protocols"), 32 | 33 | /** 34 | * 由WebDAV(RFC 2518)扩展的状态码,代表处理将被继续执行 35 | */ 36 | _102(102, "Processing"), 37 | 38 | /* 39 | * *********************************************************************** 40 | * 2XX 成功 41 | * 42 | * 这一类型的状态码,代表请求已成功被服务器接收、理解、并接受 43 | * *********************************************************************** 44 | */ 45 | /** 46 | * 请求已成功,请求所希望的响应头或数据体将随此响应返回 47 | */ 48 | _200(200, "OK"), 49 | 50 | /** 51 | * 请求已经被实现,而且有一个新的资源已经依据请求的需要而创建,且其URI已经随Location头信息返回。假如需要的资源无法及时创建的话,应当返回'202 52 | * Accepted' 53 | */ 54 | _201(201, "Accepted"), 55 | 56 | /** 57 | * 服务器已接受请求,但尚未处理。正如它可能被拒绝一样,最终该请求可能会也可能不会被执行。在异步操作的场合下,没有比发送这个状态码更方便的做法了。 58 | * 返回202状态码的响应的目的是允许服务器接受其他过程的请求(例如某个每天只执行一次的基于批处理的操作),而不必让客户端一直保持与服务器的连接直到批处理操作全部完成。在接受请求处理并返回202状态码的响应应当在返回的实体中包含一些指示处理当前状态的信息,以及指向处理状态监视器或状态预测的指针,以便用户能够估计操作是否已经完成 59 | */ 60 | _202(202, "Processing"), 61 | 62 | /** 63 | * 服务器已成功处理了请求,但返回的实体头部元信息不是在原始服务器上有效的确定集合,而是来自本地或者第三方的拷贝。当前的信息可能是原始版本的子集或者超集。例如,包含资源的元数据可能导致原始服务器知道元信息的超集。使用此状态码不是必须的,而且只有在响应不使用此状态码便会返回200 64 | * OK的情况下才是合适的 65 | */ 66 | _203(203, "Non-Authoritative Information"), 67 | 68 | /** 69 | * 服务器成功处理了请求,但不需要返回任何实体内容,并且希望返回更新了的元信息。响应可能通过实体头部的形式,返回新的或更新后的元信息。如果存在这些头部信息,则应当与所请求的变量相呼应。 70 | * 如果客户端是浏览器的话,那么用户浏览器应保留发送了该请求的页面,而不产生任何文档视图上的变化,即使按照规范新的或更新后的元信息应当被应用到用户浏览器活动视图中的文档。 71 | * 由于204响应被禁止包含任何消息体,因此它始终以消息头后的第一个空行结尾 72 | */ 73 | _204(204, "No Content"), 74 | 75 | /** 76 | * 服务器成功处理了请求,且没有返回任何内容。但是与204响应不同,返回此状态码的响应要求请求者重置文档视图。该响应主要是被用于接受用户输入后,立即重置表单,以便用户能够轻松地开始另一次输入。 77 | * 与204响应一样,该响应也被禁止包含任何消息体,且以消息头后的第一个空行结束 78 | */ 79 | _205(205, "Reset Content"), 80 | 81 | /** 82 | * 服务器已经成功处理了部分GET请求。类似于FlashGet或者迅雷这类的HTTP 83 | * 下载工具都是使用此类响应实现断点续传或者将一个大文档分解为多个下载段同时下载。 84 | * 该请求必须包含Range头信息来指示客户端希望得到的内容范围,并且可能包含If-Range来作为请求条件。 响应必须包含如下的头部域: 85 | * Content-Range用以指示本次响应中返回的内容的范围;如果是Content-Type为multipart/byteranges的多段下载,则每一multipart段中都应包含Content-Range域用以指示本段的内容范围。假如响应中包含Content-Length,那么它的数值必须匹配它返回的内容范围的真实字节数。 86 | * Date ETag和/或Content-Location,假如同样的请求本应该返回200响应。 Expires, 87 | * Cache-Control,和/或Vary,假如其值可能与之前相同变量的其他响应对应的值不同的话。 88 | * 假如本响应请求使用了If-Range强缓存验证,那么本次响应不应该包含其他实体头;假如本响应的请求使用了If-Range弱缓存验证,那么本次响应禁止包含其他实体头;这避免了缓存的实体内容和更新了的实体头信息之间的不一致。否则,本响应就应当包含所有本应该返回200响应中应当返回的所有实体头部域。 89 | * 假如ETag或Last-Modified头部不能精确匹配的话,则客户端缓存应禁止将206响应返回的内容与之前任何缓存过的内容组合在一起。 90 | * 任何不支持Range以及Content-Range头的缓存都禁止缓存206响应返回的内容 91 | */ 92 | _206(206, "Partial Content"), 93 | 94 | /** 95 | * 由WebDAV(RFC 2518)扩展的状态码,代表之后的消息体将是一个XML消息,并且可能依照之前子请求数量的不同,包含一系列独立的响应代码 96 | */ 97 | _207(207, "Multi-Status"), 98 | 99 | /* 100 | * *********************************************************************** 101 | * 3XX 重定向 102 | * 103 | * 这类状态码代表需要客户端采取进一步的操作才能完成请求。通常,这些状态码用来重定向,后续的请求地址(重定向目标) 104 | * 在本次响应的Location域中指明。 105 | * 106 | * 当且仅当后续的请求所使用的方法是GET或者HEAD时,用户浏览器才可以在没有用户介入的情况下自动提交所需要的后续请求。 107 | * 客户端应当自动监测无限循环重定向(例如:A→B→C→……→A或A→A),因为这会导致服务器和客户端大量不必要的资源消耗。按照HTTP/1. 108 | * 0版规范的建议,浏览器不应自动访问超过5次的重定向 109 | * *********************************************************************** 110 | */ 111 | /** 112 | * 被请求的资源有一系列可供选择的回馈信息,每个都有自己特定的地址和浏览器驱动的商议信息。用户或浏览器能够自行选择一个首选的地址进行重定向。 113 | * 除非这是一个HEAD请求,否则该响应应当包括一个资源特性及地址的列表的实体,以便用户或浏览器从中选择最合适的重定向地址。这个实体的格式由Content-Type定义的格式所决定。浏览器可能根据响应的格式以及浏览器自身能力,自动作出最合适的选择。当然,RFC 114 | * 2616规范并没有规定这样的自动选择该如何进行。 115 | * 如果服务器本身已经有了首选的回馈选择,那么在Location中应当指明这个回馈的URI;浏览器可能会将这个Location值作为自动重定向的地址。此外,除非额外指定,否则这个响应也是可缓存的。 116 | */ 117 | _300(300, "Multiple Choices"), 118 | /** 119 | * 被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个URI之一。如果可能,拥有链接编辑功能的客户端应当自动把请求的地址修改为从服务器反馈回来的地址。除非额外指定,否则这个响应也是可缓存的。 120 | * 新的永久性的URI应当在响应的Location域中返回。除非这是一个HEAD请求,否则响应的实体中应当包含指向新的URI的超链接及简短说明。 121 | * 如果这不是一个GET或者HEAD请求,因此浏览器禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化。 122 | * 注意:对于某些使用HTTP/1.0协议的浏览器,当它们发送的POST请求得到了一个301响应的话,接下来的重定向请求将会变成GET方式 123 | */ 124 | _301(301, "Moved Permanently"), 125 | 126 | /** 127 | * 请求的资源现在临时从不同的URI响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。 128 | * 新的临时性的URI应当在响应的Location域中返回。除非这是一个HEAD请求,否则响应的实体中应当包含指向新的URI的超链接及简短说明。 129 | * 如果这不是一个GET或者HEAD请求,那么浏览器禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化。 注意:虽然RFC 130 | * 1945和RFC 131 | * 2068规范不允许客户端在重定向时改变请求的方法,但是很多现存的浏览器将302响应视作为303响应,并且使用GET方式访问在Location中规定的URI,而无视原先请求的方法。状态码303和307被添加了进来,用以明确服务器期待客户端进行何种反应 132 | * 133 | */ 134 | _302(302, "Found"), 135 | /** 136 | * 对应当前请求的响应可以在另一个URI上被找到,而且客户端应当采用GET的方式访问那个资源。这个方法的存在主要是为了允许由脚本激活的POST请求输出重定向到一个新的资源。这个新的URI不是原始资源的替代引用。同时,303响应禁止被缓存。当然,第二个请求(重定向)可能被缓存。 137 | * 新的URI应当在响应的Location域中返回。除非这是一个HEAD请求,否则响应的实体中应当包含指向新的URI的超链接及简短说明。 138 | * 注意:许多HTTP/1.1版以前的浏览器不能正确理解303状态。如果需要考虑与这些浏览器之间的互动,302状态码应该可以胜任,因为大多数的浏览器处理302响应时的方式恰恰就是上述规范要求客户端处理303响应时应当做的 139 | */ 140 | _303(303, "See Other"), 141 | 142 | /** 143 | * 如果客户端发送了一个带条件的GET请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个状态码。304响应禁止包含消息体,因此始终以消息头后的第一个空行结尾。 144 | * 该响应必须包含以下的头信息: 145 | * Date,除非这个服务器没有时钟。假如没有时钟的服务器也遵守这些规则,那么代理服务器以及客户端可以自行将Date字段添加到接收到的响应头中去(正如RFC 146 | * 2068中规定的一样),缓存机制将会正常工作。 ETag和/或Content-Location,假如同样的请求本应返回200响应。 147 | * Expires, Cache-Control,和/或Vary,假如其值可能与之前相同变量的其他响应对应的值不同的话。 148 | * 假如本响应请求使用了强缓存验证,那么本次响应不应该包含其他实体头;否则(例如,某个带条件的GET请求使用了弱缓存验证),本次响应禁止包含其他实体头;这避免了缓存了的实体内容和更新了的实体头信息之间的不一致。 149 | * 假如某个304响应指明了当前某个实体没有缓存,那么缓存系统必须忽视这个响应,并且重复发送不包含限制条件的请求。 150 | * 假如接收到一个要求更新某个缓存条目的304响应,那么缓存系统必须更新整个条目以反映所有在响应中被更新的字段的值 151 | */ 152 | _304(304, "Not Modified"), 153 | 154 | /** 155 | * 被请求的资源必须通过指定的代理才能被访问。Location域中将给出指定的代理所在的URI信息,接收者需要重复发送一个单独的请求,通过这个代理才能访问相应资源。只有原始服务器才能创建305响应。 156 | * 注意:RFC 2068中没有明确305响应是为了重定向一个单独的请求,而且只能被原始服务器建立。忽视这些限制可能导致严重的安全后果 157 | */ 158 | _305(305, "Use Proxy"), 159 | 160 | /** 161 | * 在最新版的规范中,306状态码已经不再被使用 162 | */ 163 | _306(306, "Switch Proxy"), 164 | 165 | /** 166 | * 请求的资源现在临时从不同的URI响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。 167 | * 新的临时性的URI应当在响应的Location域中返回。除非这是一个HEAD请求,否则响应的实体中应当包含指向新的URI的超链接及简短说明。因为部分浏览器不能识别307响应,因此需要添加上述必要信息以便用户能够理解并向新的URI发出访问请求。 168 | * 如果这不是一个GET或者HEAD请求,那么浏览器禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化 169 | */ 170 | _307(307, "Temporary Redirect"), 171 | 172 | /* 173 | * *********************************************************************** 174 | * 4XX 客户端错误 175 | * 176 | * 这类的状态码代表了客户端看起来可能发生了错误,妨碍了服务器的处理。除非响应的是一个HEAD请求,否则服务器就应该返回一个解释当前错误状况的实体, 177 | * 以及这是临时的还是永久性的状况。这些状态码适用于任何请求方法。浏览器应当向用户显示任何包含在此类错误响应中的实体内容。 178 | * 179 | * 如果错误发生时客户端正在传送数据,那么使用TCP的服务器实现应当仔细确保在关闭客户端与服务器之间的连接之前,客户端已经收到了包含错误信息的数据包。 180 | * 如果客户端在收到错误信息后继续向服务器发送数据,服务器的TCP栈将向客户端发送一个重置数据包,以清除该客户端所有还未识别的输入缓冲, 181 | * 以免这些数据被服务器上的应用程序读取并干扰后者 182 | * *********************************************************************** 183 | */ 184 | /** 185 | * Bad Request 由于包含语法错误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求 186 | */ 187 | _400(400, "Bad Request"), 188 | 189 | /** 190 | * 当前请求需要用户验证。该响应必须包含一个适用于被请求资源的WWW-Authenticate信息头用以询问用户信息。客户端可以重复提交一个包含恰当的Authorization头信息的请求。如果当前请求已经包含了Authorization证书,那么401响应代表着服务器验证已经拒绝了那些证书。如果401响应包含了与前一个响应相同的身份验证询问,且浏览器已经至少尝试了一次验证,那么浏览器应当向用户展示响应中包含的实体信息,因为这个实体信息中可能包含了相关诊断信息。参见RFC 191 | * 2617。 192 | */ 193 | _401(401, "Unauthorized"), 194 | 195 | /** 196 | * 该状态码是为了将来可能的需求而预留的 197 | */ 198 | _402(402, "Payment Required"), 199 | 200 | /** 201 | * 服务器已经理解请求,但是拒绝执行它。与401响应不同的是,身份验证并不能提供任何帮助,而且这个请求也不应该被重复提交。如果这不是一个HEAD请求,而且服务器希望能够讲清楚为何请求不能被执行,那么就应该在实体内描述拒绝的原因。当然服务器也可以返回一个404响应,假如它不希望让客户端获得任何信息 202 | */ 203 | _403(403, "Forbidden"), 204 | 205 | /** 206 | * 请求失败,请求所希望得到的资源未被在服务器上发现。没有信息能够告诉用户这个状况到底是暂时的还是永久的。假如服务器知道情况的话,应当使用410状态码来告知旧资源因为某些内部的配置机制问题,已经永久的不可用,而且没有任何可以跳转的地址。404这个状态码被广泛应用于当服务器不想揭示到底为何请求被拒绝或者没有其他适合的响应可用的情况下。 207 | */ 208 | _404(404, "Not Found"), 209 | 210 | /** 211 | * 请求行中指定的请求方法不能被用于请求相应的资源。该响应必须返回一个Allow头信息用以表示出当前资源能够接受的请求方法的列表。 212 | * 鉴于PUT,DELETE方法会对服务器上的资源进行写操作,因而绝大部分的网页服务器都不支持或者在默认配置下不允许上述请求方法,对于此类请求均会返回405错误 213 | */ 214 | _405(405, "Method Not Allowed"), 215 | 216 | /** 217 | * 请求的资源的内容特性无法满足请求头中的条件,因而无法生成响应实体。 218 | * 除非这是一个HEAD请求,否则该响应就应当返回一个包含可以让用户或者浏览器从中选择最合适的实体特性以及地址列表的实体。实体的格式由Content-Type头中定义的媒体类型决定。浏览器可以根据格式及自身能力自行作出最佳选择。但是,规范中并没有定义任何作出此类自动选择的标准 219 | */ 220 | _406(406, "Not Acceptable"), 221 | /** 222 | * 与401响应类似,只不过客户端必须在代理服务器上进行身份验证。代理服务器必须返回一个Proxy-Authenticate用以进行身份询问。客户端可以返回一个Proxy-Authorization信息头用以验证。参见RFC 223 | * 2617。 224 | */ 225 | _407(407, "Proxy Authentication Required"), 226 | /** 227 | * 请求超时。客户端没有在服务器预备等待的时间内完成一个请求的发送。客户端可以随时再次提交这一请求而无需进行任何更改 228 | */ 229 | _408(408, "Request Timeout"), 230 | /** 231 | * 由于和被请求的资源的当前状态之间存在冲突,请求无法完成。这个代码只允许用在这样的情况下才能被使用:用户被认为能够解决冲突,并且会重新提交新的请求。该响应应当包含足够的信息以便用户发现冲突的源头。 232 | * 冲突通常发生于对PUT请求的处理中。例如,在采用版本检查的环境下,某次PUT提交的对特定资源的修改请求所附带的版本信息与之前的某个(第三方)请求向冲突,那么此时服务器就应该返回一个409错误,告知用户请求无法完成。此时,响应实体中很可能会包含两个冲突版本之间的差异比较,以便用户重新提交归并以后的新版本。 233 | */ 234 | _409(409, "Conflict"), 235 | /** 236 | * 被请求的资源在服务器上已经不再可用,而且没有任何已知的转发地址。这样的状况应当被认为是永久性的。如果可能,拥有链接编辑功能的客户端应当在获得用户许可后删除所有指向这个地址的引用。如果服务器不知道或者无法确定这个状况是否是永久的,那么就应该使用404状态码。除非额外说明,否则这个响应是可缓存的。 237 | * 410响应的目的主要是帮助网站管理员维护网站,通知用户该资源已经不再可用,并且服务器拥有者希望所有指向这个资源的远端连接也被删除。这类事件在限时、增值服务中很普遍。同样,410响应也被用于通知客户端在当前服务器站点上,原本属于某个个人的资源已经不再可用。当然,是否需要把所有永久不可用的资源标记为'410 238 | * Gone',以及是否需要保持此标记多长时间,完全取决于服务器拥有者。 239 | */ 240 | _410(410, "Gone"), 241 | /** 242 | * 服务器拒绝在没有定义Content-Length头的情况下接受请求。在添加了表明请求消息体长度的有效Content-Length头之后,客户端可以再次提交该请求 243 | */ 244 | _411(411, "Length Required"), 245 | /** 246 | * 服务器在验证在请求的头字段中给出先决条件时,没能满足其中的一个或多个。这个状态码允许客户端在获取资源时在请求的元信息(请求头字段数据)中设置先决条件,以此避免该请求方法被应用到其希望的内容以外的资源上。 247 | */ 248 | _412(412, "Precondition Failed"), 249 | /** 250 | * 服务器拒绝处理当前请求,因为该请求提交的实体数据大小超过了服务器愿意或者能够处理的范围。此种情况下,服务器可以关闭连接以免客户端继续发送此请求。 251 | * 如果这个状况是临时的,服务器应当返回一个Retry-After的响应头,以告知客户端可以在多少时间以后重新尝试 252 | */ 253 | _413(413, "Request Entity Too Large"), 254 | /** 255 | * 请求的URI长度超过了服务器能够解释的长度,因此服务器拒绝对该请求提供服务。这比较少见,通常的情况包括: 256 | * 本应使用POST方法的表单提交变成了GET方法,导致查询字符串过长。 257 | * 重定向URI“黑洞”,例如每次重定向把旧的URI作为新的URI的一部分,导致在若干次重定向后URI超长。 258 | * 客户端正在尝试利用某些服务器中存在的安全漏洞攻击服务器。这类服务器使用固定长度的缓冲读取或操作请求的URI,当GET后的参数超过某个数值后,可能会产生缓冲区溢出,导致任意代码被执行[1]。没有此类漏洞的服务器,应当返回414状态码。 259 | */ 260 | _414(414, "Request-URI Too Long"), 261 | /** 262 | * 对于当前请求的方法和所请求的资源,请求中提交的实体并不是服务器中所支持的格式,因此请求被拒绝 263 | */ 264 | _415(415, "Unsupported Media Type"), 265 | /** 266 | * 如果请求中包含了Range请求头,并且Range中指定的任何数据范围都与当前资源的可用范围不重合,同时请求中又没有定义If-Range请求头,那么服务器就应当返回416状态码。 267 | * 假如Range使用的是字节范围,那么这种情况就是指请求指定的所有数据范围的首字节位置都超过了当前资源的长度。服务器也应当在返回416状态码的同时,包含一个Content-Range实体头,用以指明当前资源的长度。这个响应也被禁止使用multipart/byteranges作为其Content-Type。 268 | */ 269 | _416(416, "Requested Range Not Satisfiable"), 270 | /** 271 | * 在请求头Expect中指定的预期内容无法被服务器满足,或者这个服务器是一个代理服务器,它有明显的证据证明在当前路由的下一个节点上,Expect的内容无法被满足 272 | */ 273 | _417(417, "Expectation Failed"), 274 | /** 275 | * 本操作码是在1998年作为IETF的传统愚人节笑话, 在RFC 2324 276 | * 超文本咖啡壶控制协议中定义的,并不需要在真实的HTTP服务器中定义。当一个控制茶壶的HTCPCP收到BREW或POST指令要求其煮咖啡时应当回传此错误。 277 | */ 278 | _418(418, "I'm a teapot"), 279 | 280 | /** 281 | * 从当前客户端所在的IP地址到服务器的连接数超过了服务器许可的最大范围。通常,这里的IP地址指的是从服务器上看到的客户端地址(比如用户的网关或者代理服务器地址)。在这种情况下,连接数的计算可能涉及到不止一个终端用户。 282 | */ 283 | _421(421, "There are too many connections from your internet address"), 284 | /** 285 | * 请求格式正确,但是由于含有语义错误,无法响应。(RFC 4918 WebDAV) 286 | */ 287 | _422(422, "Unprocessable Entity"), 288 | /** 289 | * 当前资源被锁定。(RFC 4918 WebDAV) 290 | */ 291 | _423(423, "Locked"), 292 | /** 293 | * 由于之前的某个请求发生的错误,导致当前请求失败,例如PROPPATCH。(RFC 4918 WebDAV) 294 | */ 295 | _424(424, "Failed Dependency"), 296 | /** 297 | * 在WebDav Advanced Collections草案中定义,但是未出现在《WebDAV顺序集协议》(RFC 3658)中 298 | */ 299 | _425(425, "Unordered Collection"), 300 | /** 301 | * 客户端应当切换到TLS/1.0。(RFC 2817) 302 | */ 303 | _426(426, "Upgrade Required"), 304 | /** 305 | * 由微软扩展,代表请求应当在执行完适当的操作后进行重试 306 | */ 307 | _449(449, "Retry With"), 308 | /** 309 | * (由IETF在2015核准后新增加)该访问因法律的要求而被拒绝。[2][3] 310 | * 311 | */ 312 | _451(451, "Unavailable For Legal Reasons"), 313 | 314 | /* 315 | * *********************************************************************** 316 | * 5XX 服务器错误 317 | * 318 | * 这类状态码代表了服务器在处理请求的过程中有错误或者异常状态发生,也有可能是服务器意识到以当前的软硬件资源无法完成对请求的处理。 319 | * 除非这是一个HEAD请求,否则服务器应当包含一个解释当前错误状态以及这个状况是临时的还是永久的解释信息实体。 320 | * 浏览器应当向用户展示任何在当前响应中被包含的实体。 321 | * 322 | * 这些状态码适用于任何响应方法 323 | * *********************************************************************** 324 | */ 325 | /** 326 | * 服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器的程序码出错时出现 327 | */ 328 | _500(500, "Internal Server Error"), 329 | 330 | /** 331 | * 服务器不支持当前请求所需要的某个功能。当服务器无法识别请求的方法,并且无法支持其对任何资源的请求 332 | * 333 | */ 334 | _501(501, "Not Implemented"), 335 | 336 | /** 337 | * 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应 338 | */ 339 | _502(502, "Bad Gateway"), 340 | 341 | /** 342 | * 由于临时的服务器维护或者过载,服务器当前无法处理请求。这个状况是临时的,并且将在一段时间以后恢复。如果能够预计延迟时间,那么响应中可以包含一个Retry-After头用以标明这个延迟时间。如果没有给出这个Retry-After信息,那么客户端应当以处理500响应的方式处理它 343 | */ 344 | _503(503, "Service Unavailable"), 345 | 346 | /** 347 | * 作为网关或者代理工作的服务器尝试执行请求时,未能及时从上游服务器(URI标识出的服务器,例如HTTP、FTP、LDAP)或者辅助服务器(例如DNS)收到响应。 348 | * 注意:某些代理服务器在DNS查询超时时会返回400或者500错误。 349 | */ 350 | _504(504, "Gateway Timeout"), 351 | 352 | /** 353 | * 服务器不支持,或者拒绝支持在请求中使用的HTTP版本。这暗示着服务器不能或不愿使用与客户端相同的版本。响应中应当包含一个描述了为何版本不被支持以及服务器支持哪些协议的实体 354 | */ 355 | _505(505, "HTTP Version Not Supported"), 356 | 357 | /** 358 | * 由《透明内容协商协议》(RFC 359 | * 2295)扩展,代表服务器存在内部配置错误:被请求的协商变元资源被配置为在透明内容协商中使用自己,因此在一个协商处理中不是一个合适的重点 360 | */ 361 | _506(506, "Variant Also Negotiates"), 362 | 363 | /** 364 | * 服务器无法存储完成请求所必须的内容。这个状况被认为是临时的。(WebDAV RFC 4918) 365 | */ 366 | _507(507, "Insufficient Storage"), 367 | 368 | /** 369 | * 服务器达到带宽限制。这不是一个官方的状态码,但是仍被广泛使用 370 | */ 371 | _509(509, "Bandwidth Limit Exceeded"), 372 | 373 | /** 374 | * 获取资源所需要的策略并没有被满足。(RFC 2774) 375 | */ 376 | _510(510, "Not Extended"); 377 | 378 | /** 379 | * 状态码 380 | */ 381 | private int status; 382 | 383 | /** 384 | * 描述 385 | */ 386 | private String description; 387 | 388 | /* 389 | * constructor 390 | */ 391 | private HttpResponseStatus(int status, String description) { 392 | this.status = status; 393 | this.description = description; 394 | } 395 | 396 | /* 397 | * getter、setter 398 | */ 399 | public int getStatus() { 400 | return status; 401 | } 402 | 403 | public void setStatus(int status) { 404 | this.status = status; 405 | } 406 | 407 | public String getDescription() { 408 | return description; 409 | } 410 | 411 | public void setDescription(String description) { 412 | this.description = description; 413 | } 414 | 415 | } 416 | --------------------------------------------------------------------------------