├── src └── main │ ├── resources │ ├── Tears.sh │ ├── shutdown.sh │ ├── log4j.properties │ └── config.json │ └── java │ └── com │ └── zjh │ └── tears │ ├── filter │ ├── Filter.java │ └── socket │ │ ├── HeaderSocketFilter.java │ │ ├── TestSocketFilter.java │ │ ├── SocketFilterChain.java │ │ ├── SocketFilter.java │ │ └── HTTPRequestSocketFilter.java │ ├── exception │ ├── ShutdownException.java │ ├── FilterException.java │ ├── PermissionException.java │ ├── ServerException.java │ ├── NotFoundException.java │ ├── HTTPRequestFilterException.java │ └── HTTPException.java │ ├── listener │ └── socket │ │ ├── HeaderSocketListener.java │ │ ├── DebugSocketListener.java │ │ ├── LogSocketListener.java │ │ ├── SocketListenerChain.java │ │ └── SocketListener.java │ ├── strategy │ ├── HTTPStrategy.java │ ├── DefaultCode206Strategy.java │ ├── DefaultCode200Strategy.java │ └── ReverseProxyStrategy.java │ ├── handler │ ├── HeaderHTTPHandler.java │ ├── HTTPHandlerChain.java │ ├── ResponseHeaderHTTPHandler.java │ ├── URLDecodeHTTPHandler.java │ ├── HTTPHandler.java │ ├── DefaultIndexHTTPHandler.java │ ├── StaticFileHTTPHandler.java │ ├── AcceptFileHTTPHandler.java │ └── StaticFileChooseStrategyHTTPHandler.java │ ├── logger │ └── MyAppender.java │ ├── Starter.java │ ├── util │ ├── ClassParse.java │ ├── Util.java │ └── HTTPRequest.java │ ├── Keeper.java │ ├── model │ ├── HTTPMethod.java │ ├── ReverseProxy.java │ ├── Request.java │ ├── Response.java │ └── SocketObject.java │ ├── manager │ ├── HTTPHandlerChainManager.java │ └── SocketChainManager.java │ ├── ShutdownServer.java │ ├── process │ ├── ResponseProcess.java │ ├── HTTPProcess.java │ └── SocketProcess.java │ ├── Server.java │ ├── factory │ ├── ErrorResponseFactory.java │ └── RequestFactory.java │ └── config │ └── Config.java ├── target └── classes │ ├── Tears.sh │ ├── shutdown.sh │ ├── com │ └── zjh │ │ └── tears │ │ ├── Keeper.class │ │ ├── Server.class │ │ ├── Starter.class │ │ ├── util │ │ ├── Util.class │ │ ├── ClassParse.class │ │ ├── HTTPRequest.class │ │ ├── ClassParse$1.class │ │ └── ClassParse$ClassHandler.class │ │ ├── ShutdownServer.class │ │ ├── config │ │ └── Config.class │ │ ├── filter │ │ ├── Filter.class │ │ └── socket │ │ │ ├── SocketFilter.class │ │ │ ├── SocketFilterChain.class │ │ │ ├── TestSocketFilter.class │ │ │ ├── HeaderSocketFilter.class │ │ │ └── HTTPRequestSocketFilter.class │ │ ├── model │ │ ├── Request.class │ │ ├── Response.class │ │ ├── HTTPMethod.class │ │ ├── ReverseProxy.class │ │ └── SocketObject.class │ │ ├── handler │ │ ├── HTTPHandler.class │ │ ├── HTTPHandlerChain.class │ │ ├── HeaderHTTPHandler.class │ │ ├── AcceptFileHTTPHandler.class │ │ ├── StaticFileHTTPHandler.class │ │ ├── URLDecodeHTTPHandler.class │ │ ├── DefaultIndexHTTPHandler.class │ │ ├── ResponseHeaderHTTPHandler.class │ │ └── StaticFileChooseStrategyHTTPHandler.class │ │ ├── logger │ │ └── MyAppender.class │ │ ├── process │ │ ├── HTTPProcess.class │ │ ├── SocketProcess.class │ │ └── ResponseProcess.class │ │ ├── strategy │ │ ├── HTTPStrategy.class │ │ ├── ReverseProxyStrategy.class │ │ ├── DefaultCode200Strategy.class │ │ └── DefaultCode206Strategy.class │ │ ├── exception │ │ ├── HTTPException.class │ │ ├── FilterException.class │ │ ├── ServerException.class │ │ ├── NotFoundException.class │ │ ├── PermissionException.class │ │ ├── ShutdownException.class │ │ └── HTTPRequestFilterException.class │ │ ├── factory │ │ ├── RequestFactory$1.class │ │ ├── RequestFactory.class │ │ ├── ErrorResponseFactory.class │ │ ├── ErrorResponseFactory$1.class │ │ ├── RequestFactory$Handler.class │ │ └── ErrorResponseFactory$Holder.class │ │ ├── manager │ │ ├── SocketChainManager.class │ │ └── HTTPHandlerChainManager.class │ │ └── listener │ │ └── socket │ │ ├── SocketListener.class │ │ ├── LogSocketListener.class │ │ ├── DebugSocketListener.class │ │ ├── HeaderSocketListener.class │ │ └── SocketListenerChain.class │ ├── log4j.properties │ └── config.json ├── .gitignore ├── doc └── 1486735359756.png ├── README.md └── pom.xml /src/main/resources/Tears.sh: -------------------------------------------------------------------------------- 1 | java com.zjh.tears.Starter -------------------------------------------------------------------------------- /target/classes/Tears.sh: -------------------------------------------------------------------------------- 1 | java com.zjh.tears.Starter -------------------------------------------------------------------------------- /target/classes/shutdown.sh: -------------------------------------------------------------------------------- 1 | java com.zjh.tears.Keeper -------------------------------------------------------------------------------- /src/main/resources/shutdown.sh: -------------------------------------------------------------------------------- 1 | java com.zjh.tears.Keeper -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | Tears.iml 4 | out 5 | log 6 | -------------------------------------------------------------------------------- /doc/1486735359756.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/doc/1486735359756.png -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/Keeper.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/Keeper.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/Server.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/Server.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/Starter.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/Starter.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/util/Util.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/util/Util.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/ShutdownServer.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/ShutdownServer.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/config/Config.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/config/Config.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/filter/Filter.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/filter/Filter.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/model/Request.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/model/Request.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/model/Response.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/model/Response.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/model/HTTPMethod.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/model/HTTPMethod.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/util/ClassParse.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/util/ClassParse.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/util/HTTPRequest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/util/HTTPRequest.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/handler/HTTPHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/handler/HTTPHandler.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/logger/MyAppender.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/logger/MyAppender.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/model/ReverseProxy.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/model/ReverseProxy.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/model/SocketObject.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/model/SocketObject.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/process/HTTPProcess.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/process/HTTPProcess.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/util/ClassParse$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/util/ClassParse$1.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/process/SocketProcess.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/process/SocketProcess.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/strategy/HTTPStrategy.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/strategy/HTTPStrategy.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/exception/HTTPException.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/exception/HTTPException.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/factory/RequestFactory$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/factory/RequestFactory$1.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/factory/RequestFactory.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/factory/RequestFactory.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/handler/HTTPHandlerChain.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/handler/HTTPHandlerChain.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/process/ResponseProcess.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/process/ResponseProcess.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/exception/FilterException.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/exception/FilterException.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/exception/ServerException.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/exception/ServerException.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/filter/socket/SocketFilter.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/filter/socket/SocketFilter.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/handler/HeaderHTTPHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/handler/HeaderHTTPHandler.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/manager/SocketChainManager.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/manager/SocketChainManager.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/exception/NotFoundException.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/exception/NotFoundException.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/exception/PermissionException.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/exception/PermissionException.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/exception/ShutdownException.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/exception/ShutdownException.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/factory/ErrorResponseFactory.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/factory/ErrorResponseFactory.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/handler/AcceptFileHTTPHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/handler/AcceptFileHTTPHandler.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/handler/StaticFileHTTPHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/handler/StaticFileHTTPHandler.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/handler/URLDecodeHTTPHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/handler/URLDecodeHTTPHandler.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/strategy/ReverseProxyStrategy.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/strategy/ReverseProxyStrategy.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/util/ClassParse$ClassHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/util/ClassParse$ClassHandler.class -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/filter/Filter.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.filter; 2 | 3 | /** 4 | * Created by zhangjiahao on 2017/2/7. 5 | */ 6 | public interface Filter { 7 | } 8 | -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/factory/ErrorResponseFactory$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/factory/ErrorResponseFactory$1.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/factory/RequestFactory$Handler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/factory/RequestFactory$Handler.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/filter/socket/SocketFilterChain.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/filter/socket/SocketFilterChain.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/filter/socket/TestSocketFilter.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/filter/socket/TestSocketFilter.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/handler/DefaultIndexHTTPHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/handler/DefaultIndexHTTPHandler.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/listener/socket/SocketListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/listener/socket/SocketListener.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/manager/HTTPHandlerChainManager.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/manager/HTTPHandlerChainManager.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/strategy/DefaultCode200Strategy.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/strategy/DefaultCode200Strategy.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/strategy/DefaultCode206Strategy.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/strategy/DefaultCode206Strategy.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/filter/socket/HeaderSocketFilter.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/filter/socket/HeaderSocketFilter.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/handler/ResponseHeaderHTTPHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/handler/ResponseHeaderHTTPHandler.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/listener/socket/LogSocketListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/listener/socket/LogSocketListener.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/exception/HTTPRequestFilterException.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/exception/HTTPRequestFilterException.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/factory/ErrorResponseFactory$Holder.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/factory/ErrorResponseFactory$Holder.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/listener/socket/DebugSocketListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/listener/socket/DebugSocketListener.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/listener/socket/HeaderSocketListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/listener/socket/HeaderSocketListener.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/listener/socket/SocketListenerChain.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/listener/socket/SocketListenerChain.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/filter/socket/HTTPRequestSocketFilter.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/filter/socket/HTTPRequestSocketFilter.class -------------------------------------------------------------------------------- /target/classes/com/zjh/tears/handler/StaticFileChooseStrategyHTTPHandler.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zjhch123/Tears/HEAD/target/classes/com/zjh/tears/handler/StaticFileChooseStrategyHTTPHandler.class -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/exception/ShutdownException.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.exception; 2 | 3 | /** 4 | * Created by zhangjiahao on 2017/2/20. 5 | */ 6 | public class ShutdownException { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/listener/socket/HeaderSocketListener.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.listener.socket; 2 | 3 | 4 | /** 5 | * Created by zhangjiahao on 2017/2/8. 6 | */ 7 | public class HeaderSocketListener extends SocketListener { 8 | } 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tears 2 | 特点: 3 | 4 | 1. 由Java语言编写的静态资源服务器 5 | 2. 简单。配制简单,修改简单,二次开发简单 6 | 3. 使用基于JSON的配置文件,灵活 7 | 4. 支持反向代理功能,可以与多种服务器组合 8 | 9 | 架构: 10 | 11 | ![架构](https://raw.githubusercontent.com/zjhch123/Tears/master/doc/1486735359756.png) 12 | // zjh:架构图有点乱画的嫌疑 13 | 14 | 15 | # 文档补全计划未开始 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/exception/FilterException.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.exception; 2 | 3 | /** 4 | * Created by zhangjiahao on 2017/2/7. 5 | */ 6 | public class FilterException extends Exception { 7 | public FilterException(String message) { 8 | super(message); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/exception/PermissionException.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.exception; 2 | 3 | /** 4 | * Created by zhangjiahao on 2017/2/8. 5 | */ 6 | public class PermissionException extends HTTPException { 7 | public PermissionException() { 8 | super(null, 403, "Forbidden"); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/exception/ServerException.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.exception; 2 | 3 | /** 4 | * Created by zhangjiahao on 2017/2/8. 5 | */ 6 | public class ServerException extends HTTPException { 7 | public ServerException(Exception source) { 8 | super(source, 500, "Server Error"); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/exception/NotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.exception; 2 | 3 | 4 | /** 5 | * Created by zhangjiahao on 2017/2/8. 6 | */ 7 | public class NotFoundException extends HTTPException { 8 | public NotFoundException(Exception source) { 9 | super(source, 404, "Not Found"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/exception/HTTPRequestFilterException.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.exception; 2 | 3 | /** 4 | * Created by zhangjiahao on 2017/2/12. 5 | */ 6 | public class HTTPRequestFilterException extends FilterException { 7 | public HTTPRequestFilterException() { 8 | // 这里日志记的是message,不是stackTrace 9 | super("Get a request but it is not a HTTP request"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/strategy/HTTPStrategy.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.strategy; 2 | 3 | import com.zjh.tears.exception.HTTPException; 4 | import com.zjh.tears.model.Request; 5 | import com.zjh.tears.model.Response; 6 | 7 | /** 8 | * Created by zhangjiahao on 2017/2/9. 9 | */ 10 | public interface HTTPStrategy { 11 | public void doWithStrategy(Request req, Response res) throws HTTPException; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/filter/socket/HeaderSocketFilter.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.filter.socket; 2 | 3 | import com.zjh.tears.exception.FilterException; 4 | import com.zjh.tears.model.SocketObject; 5 | 6 | /** 7 | * Created by zhangjiahao on 2017/2/7. 8 | */ 9 | public class HeaderSocketFilter extends SocketFilter { 10 | @Override 11 | public void execute(SocketObject socketObject) throws FilterException { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/handler/HeaderHTTPHandler.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.handler; 2 | 3 | import com.zjh.tears.exception.HTTPException; 4 | import com.zjh.tears.model.Request; 5 | import com.zjh.tears.model.Response; 6 | 7 | /** 8 | * Created by zhangjiahao on 2017/2/8. 9 | */ 10 | public class HeaderHTTPHandler extends HTTPHandler { 11 | @Override 12 | public void doWithRequest(Request req, Response res) throws HTTPException { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/logger/MyAppender.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.logger; 2 | 3 | import org.apache.log4j.DailyRollingFileAppender; 4 | import org.apache.log4j.Priority; 5 | 6 | /** 7 | * Created by zhangjiahao on 2017/2/11. 8 | */ 9 | public class MyAppender extends DailyRollingFileAppender { 10 | public boolean isAsSevereAsThreshold(Priority priority) { 11 | //只判断是否相等,而不判断优先级 12 | return this.getThreshold().equals(priority); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/listener/socket/DebugSocketListener.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.listener.socket; 2 | 3 | import com.zjh.tears.model.SocketObject; 4 | 5 | import java.net.Socket; 6 | 7 | /** 8 | * Created by zhangjiahao on 2017/2/7. 9 | */ 10 | public class DebugSocketListener extends SocketListener { 11 | @Override 12 | public void preHandler(SocketObject socketObject) { 13 | Socket socket = socketObject.getSocket(); 14 | 15 | 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/filter/socket/TestSocketFilter.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.filter.socket; 2 | 3 | import com.zjh.tears.exception.FilterException; 4 | import com.zjh.tears.model.SocketObject; 5 | 6 | /** 7 | * Created by zhangjiahao on 2017/2/10. 8 | */ 9 | public class TestSocketFilter extends SocketFilter { 10 | @Override 11 | public void execute(SocketObject socketObject) throws FilterException { 12 | System.out.println("testSocketFilter"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/Starter.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears; 2 | 3 | import com.zjh.tears.config.Config; 4 | 5 | /** 6 | * Created by zhangjiahao on 2017/2/7. 7 | */ 8 | public class Starter { 9 | public static void main(String[] args) { 10 | Server runServer = new Server(Config.PORT, Config.THREAD_POOL_SIZE); 11 | Runnable run = () -> runServer.start(); 12 | ShutdownServer shutdownServer = new ShutdownServer(8008, runServer); 13 | Runnable shutdown = () -> shutdownServer.start(); 14 | 15 | new Thread(run).start(); 16 | new Thread(shutdown).start(); 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/util/ClassParse.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.util; 2 | 3 | import com.zjh.tears.exception.ServerException; 4 | 5 | /** 6 | * Created by zhangjiahao on 2017/2/9. 7 | */ 8 | public class ClassParse { 9 | 10 | private ClassParse() { 11 | } 12 | 13 | public static ClassParse getInstance() { 14 | return ClassHandler.instance; 15 | } 16 | 17 | private final static class ClassHandler { 18 | private static final ClassParse instance = new ClassParse(); 19 | } 20 | 21 | public Object getObject(String name) throws Exception { 22 | Object obj; 23 | Class c = Class.forName(name); 24 | obj = c.newInstance(); 25 | return obj; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/handler/HTTPHandlerChain.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.handler; 2 | 3 | import com.zjh.tears.exception.HTTPException; 4 | import com.zjh.tears.model.Request; 5 | import com.zjh.tears.model.Response; 6 | 7 | /** 8 | * Created by zhangjiahao on 2017/2/8. 9 | */ 10 | public class HTTPHandlerChain { 11 | 12 | private HTTPHandler header; 13 | 14 | public HTTPHandlerChain(HTTPHandler header) { 15 | this.header = header; 16 | } 17 | 18 | public HTTPHandler getHeader() { 19 | return this.header; 20 | } 21 | 22 | public void doWithRequest(Request req, Response res) throws HTTPException { 23 | header.execute(req, res); 24 | } 25 | 26 | public void destory() { 27 | header = null; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/listener/socket/LogSocketListener.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.listener.socket; 2 | 3 | import com.zjh.tears.model.Request; 4 | import com.zjh.tears.model.Response; 5 | import com.zjh.tears.model.SocketObject; 6 | import org.apache.log4j.Logger; 7 | 8 | /** 9 | * Created by zhangjiahao on 2017/2/10. 10 | */ 11 | public class LogSocketListener extends SocketListener { 12 | 13 | private Logger logger = Logger.getLogger(this.getClass()); 14 | 15 | @Override 16 | public void afterHandler(SocketObject socketObject) { 17 | Request request = socketObject.getRequest(); 18 | Response response = socketObject.getResponse(); 19 | logger.info(request.getMethod() + " " + request.getPath() + " " + response.getCode() + " " + response.getMessage()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/filter/socket/SocketFilterChain.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.filter.socket; 2 | 3 | import com.zjh.tears.exception.FilterException; 4 | import com.zjh.tears.model.SocketObject; 5 | import com.zjh.tears.process.HTTPProcess; 6 | 7 | 8 | /** 9 | * Created by zhangjiahao on 2017/2/7. 10 | */ 11 | public class SocketFilterChain { 12 | 13 | private SocketFilter header; 14 | 15 | public SocketFilterChain(SocketFilter header) { 16 | this.header = header; 17 | } 18 | 19 | public SocketFilter getHeader() { 20 | return this.header; 21 | } 22 | 23 | public void execute(SocketObject socketObject) throws FilterException { 24 | header.doFilter(socketObject); 25 | } 26 | 27 | public void destory() { 28 | this.header = null; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/handler/ResponseHeaderHTTPHandler.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.handler; 2 | 3 | import com.zjh.tears.config.Config; 4 | import com.zjh.tears.exception.HTTPException; 5 | import com.zjh.tears.model.Request; 6 | import com.zjh.tears.model.Response; 7 | import com.zjh.tears.util.Util; 8 | 9 | /** 10 | * Created by zhangjiahao on 2017/2/8. 11 | */ 12 | public class ResponseHeaderHTTPHandler extends HTTPHandler { 13 | @Override 14 | public void doWithRequest(Request req, Response res) throws HTTPException { 15 | if(res.getBody() != null) { 16 | res.setHeader("Content-Length", String.valueOf(res.getBody().length)); 17 | } 18 | res.setHeader("Server", Config.SERVER_NAME); 19 | res.setHeader("Date", Util.getGMTString()); 20 | res.setHeader("Accept-Ranges", "bytes"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/listener/socket/SocketListenerChain.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.listener.socket; 2 | 3 | import com.zjh.tears.exception.FilterException; 4 | import com.zjh.tears.model.SocketObject; 5 | import com.zjh.tears.process.HTTPProcess; 6 | 7 | /** 8 | * Created by zhangjiahao on 2017/2/7. 9 | */ 10 | public class SocketListenerChain { 11 | 12 | private SocketListener header; 13 | 14 | public SocketListenerChain(SocketListener header) { 15 | this.header = header; 16 | } 17 | 18 | public SocketListener getHeader() { 19 | return this.header; 20 | } 21 | 22 | public byte[] execute(SocketObject socketObject, HTTPProcess target) throws FilterException { 23 | return header.doHandler(socketObject, target); 24 | } 25 | 26 | public void destory() { 27 | this.header = null; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/handler/URLDecodeHTTPHandler.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.handler; 2 | 3 | import com.zjh.tears.config.Config; 4 | import com.zjh.tears.exception.HTTPException; 5 | import com.zjh.tears.exception.ServerException; 6 | import com.zjh.tears.model.Request; 7 | import com.zjh.tears.model.Response; 8 | 9 | import java.io.UnsupportedEncodingException; 10 | 11 | /** 12 | * Created by zhangjiahao on 2017/2/10. 13 | */ 14 | public class URLDecodeHTTPHandler extends HTTPHandler { 15 | @Override 16 | public void doWithRequest(Request req, Response res) throws HTTPException { 17 | String path = req.getRealPath(); 18 | try { 19 | path = java.net.URLDecoder.decode(path, Config.DEFAULT_CHARSET); 20 | req.setRealPath(path); 21 | } catch (UnsupportedEncodingException e) { 22 | throw new ServerException(e); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/Keeper.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears; 2 | 3 | import java.io.IOException; 4 | import java.net.Socket; 5 | import java.util.Date; 6 | 7 | /** 8 | * Created by zhangjiahao on 2017/2/21. 9 | */ 10 | public class Keeper { 11 | public static void main(String[] args) { 12 | Socket socket = null; 13 | try { 14 | socket = new Socket("localhost", 8008); 15 | socket.getOutputStream().write("shutdown".getBytes()); 16 | System.out.println("Server closed at " + new Date()); 17 | System.out.println("Bye~"); 18 | } catch (IOException e) { 19 | e.printStackTrace(); 20 | } finally { 21 | try { 22 | if(socket != null) 23 | socket.close(); 24 | } catch (IOException e) { 25 | e.printStackTrace(); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/exception/HTTPException.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.exception; 2 | 3 | /** 4 | * Created by zhangjiahao on 2017/2/7. 5 | */ 6 | public class HTTPException extends Exception { 7 | protected int code; 8 | protected String errMessage; 9 | 10 | public HTTPException(Exception source, int code, String errMessage) { 11 | this.code = code; 12 | this.errMessage = errMessage; 13 | if(source != null) { 14 | this.setStackTrace(source.getStackTrace()); 15 | } 16 | } 17 | 18 | public int getCode() { 19 | return code; 20 | } 21 | 22 | public void setCode(int code) { 23 | this.code = code; 24 | } 25 | 26 | @Override 27 | public String getMessage() { 28 | return errMessage; 29 | } 30 | 31 | public void setErrMessage(String errMessage) { 32 | this.errMessage = errMessage; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/filter/socket/SocketFilter.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.filter.socket; 2 | 3 | import com.zjh.tears.exception.FilterException; 4 | import com.zjh.tears.filter.Filter; 5 | import com.zjh.tears.model.SocketObject; 6 | 7 | /** 8 | * Created by zhangjiahao on 2017/2/7. 9 | */ 10 | public abstract class SocketFilter implements Filter { 11 | 12 | private SocketFilter next; 13 | 14 | public SocketFilter getNext() { 15 | return this.next; 16 | } 17 | 18 | public void setNext(SocketFilter next) { 19 | this.next = next; 20 | } 21 | 22 | public void doFilter(SocketObject socketObject) throws FilterException { 23 | this.execute(socketObject); 24 | if(this.getNext() != null) { 25 | this.getNext().doFilter(socketObject); 26 | } 27 | } 28 | 29 | public abstract void execute(SocketObject socketObject) throws FilterException; 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/handler/HTTPHandler.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.handler; 2 | 3 | import com.zjh.tears.exception.HTTPException; 4 | import com.zjh.tears.exception.NotFoundException; 5 | import com.zjh.tears.model.Request; 6 | import com.zjh.tears.model.Response; 7 | 8 | /** 9 | * Created by zhangjiahao on 2017/2/8. 10 | */ 11 | public abstract class HTTPHandler { 12 | 13 | public HTTPHandler next; 14 | 15 | public void setNext(HTTPHandler next) { 16 | this.next = next; 17 | } 18 | 19 | public HTTPHandler getNext() { 20 | return this.next; 21 | } 22 | 23 | public void execute(Request req, Response res) throws HTTPException { 24 | this.doWithRequest(req, res); 25 | if(this.getNext() != null) { 26 | this.getNext().execute(req, res); 27 | } 28 | } 29 | 30 | public abstract void doWithRequest(Request req, Response res) throws HTTPException; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/model/HTTPMethod.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.model; 2 | 3 | /** 4 | * Created by zhangjiahao on 2017/2/8. 5 | */ 6 | public enum HTTPMethod { 7 | GET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE, CONNECT; 8 | 9 | public static HTTPMethod parse(String method) { 10 | switch (method.toUpperCase()) { 11 | case "GET": 12 | return GET; 13 | case "POST": 14 | return POST; 15 | case "PUT": 16 | return PUT; 17 | case "DELETE": 18 | return DELETE; 19 | case "HEAD": 20 | return HEAD; 21 | case "OPTIONS": 22 | return OPTIONS; 23 | case "TRACE": 24 | return TRACE; 25 | case "CONNECT": 26 | return CONNECT; 27 | default: 28 | return GET; 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/manager/HTTPHandlerChainManager.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.manager; 2 | 3 | import com.zjh.tears.config.Config; 4 | import com.zjh.tears.exception.HTTPException; 5 | import com.zjh.tears.handler.HTTPHandlerChain; 6 | import com.zjh.tears.model.Request; 7 | import com.zjh.tears.model.Response; 8 | 9 | /** 10 | * Created by zhangjiahao on 2017/2/8. 11 | */ 12 | public class HTTPHandlerChainManager { 13 | 14 | private HTTPHandlerChain httpHandlerChain; 15 | 16 | public HTTPHandlerChainManager() { 17 | this.httpHandlerChain = new HTTPHandlerChain(Config.HTTP_HANDLER_HEADER); 18 | } 19 | 20 | public void doWithRequest(Request req, Response res) throws HTTPException { 21 | this.httpHandlerChain.doWithRequest(req, res); 22 | } 23 | 24 | public void destory() { 25 | if(httpHandlerChain != null) { 26 | httpHandlerChain.destory(); 27 | httpHandlerChain = null; 28 | } 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger = debug, stdout, I, D 2 | 3 | log4j.appender.stdout = org.apache.log4j.ConsoleAppender 4 | log4j.appender.stdout.Target = System.out 5 | log4j.appender.stdout.layout = org.apache.log4j.PatternLayout 6 | log4j.appender.stdout.layout.ConversionPattern = Server: %m%n 7 | 8 | log4j.appender.I = com.zjh.tears.logger.MyAppender 9 | log4j.appender.I.Threshold = INFO 10 | log4j.appender.I.Append = true 11 | log4j.appender.I.File = ./log/access-logger.log 12 | log4j.appender.I.layout = org.apache.log4j.PatternLayout 13 | log4j.appender.I.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} %m%n 14 | 15 | log4j.appender.D = org.apache.log4j.DailyRollingFileAppender 16 | log4j.appender.D.File = ./log/message-logger.log 17 | log4j.appender.D.Append = true 18 | log4j.appender.D.Threshold = DEBUG 19 | log4j.appender.D.layout = org.apache.log4j.PatternLayout 20 | log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n -------------------------------------------------------------------------------- /target/classes/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger = debug, stdout, I, D 2 | 3 | log4j.appender.stdout = org.apache.log4j.ConsoleAppender 4 | log4j.appender.stdout.Target = System.out 5 | log4j.appender.stdout.layout = org.apache.log4j.PatternLayout 6 | log4j.appender.stdout.layout.ConversionPattern = Server: %m%n 7 | 8 | log4j.appender.I = com.zjh.tears.logger.MyAppender 9 | log4j.appender.I.Threshold = INFO 10 | log4j.appender.I.Append = true 11 | log4j.appender.I.File = ./log/access-logger.log 12 | log4j.appender.I.layout = org.apache.log4j.PatternLayout 13 | log4j.appender.I.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} %m%n 14 | 15 | log4j.appender.D = org.apache.log4j.DailyRollingFileAppender 16 | log4j.appender.D.File = ./log/message-logger.log 17 | log4j.appender.D.Append = true 18 | log4j.appender.D.Threshold = DEBUG 19 | log4j.appender.D.layout = org.apache.log4j.PatternLayout 20 | log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/filter/socket/HTTPRequestSocketFilter.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.filter.socket; 2 | 3 | import com.zjh.tears.exception.FilterException; 4 | import com.zjh.tears.exception.HTTPRequestFilterException; 5 | import com.zjh.tears.model.SocketObject; 6 | 7 | /** 8 | * Created by zhangjiahao on 2017/2/8. 9 | */ 10 | public class HTTPRequestSocketFilter extends SocketFilter { 11 | @Override 12 | public void execute(SocketObject socketObject) throws FilterException { 13 | if(socketObject.getRequest().getRequestSource() == null || socketObject.getRequest().getRequestSource().length() == 0) { 14 | throw new HTTPRequestFilterException(); 15 | } 16 | String firstLine = socketObject.getRequest().getRequestSource().split("\n")[0]; 17 | if(firstLine == null) { 18 | throw new HTTPRequestFilterException(); 19 | } 20 | if(firstLine.split(" ").length != 3) { 21 | throw new HTTPRequestFilterException(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/handler/DefaultIndexHTTPHandler.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.handler; 2 | 3 | import com.zjh.tears.config.Config; 4 | import com.zjh.tears.exception.HTTPException; 5 | import com.zjh.tears.model.Request; 6 | import com.zjh.tears.model.Response; 7 | import java.io.File; 8 | /** 9 | * Created by zhangjiahao on 2017/2/9. 10 | */ 11 | public class DefaultIndexHTTPHandler extends HTTPHandler { 12 | @Override 13 | public void doWithRequest(Request req, Response res) throws HTTPException { 14 | File requestFile = new File(req.getRealPath()); 15 | if(requestFile.isDirectory() && req.getRealPath().endsWith("/")) { 16 | for(String file : Config.DEFAULT_INDEX) { 17 | String path = req.getRealPath() + File.separator + file; 18 | if(new File(path).exists()) { 19 | req.setRealPath(path); 20 | req.setRequestPath(req.getRequestPath() + File.separator + file); 21 | break; 22 | } 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/handler/StaticFileHTTPHandler.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.handler; 2 | 3 | import com.zjh.tears.config.Config; 4 | import com.zjh.tears.exception.HTTPException; 5 | import com.zjh.tears.exception.ServerException; 6 | import com.zjh.tears.model.Request; 7 | import com.zjh.tears.model.Response; 8 | import com.zjh.tears.strategy.HTTPStrategy; 9 | import com.zjh.tears.util.ClassParse; 10 | 11 | /** 12 | * Created by zhangjiahao on 2017/2/8. 13 | */ 14 | public class StaticFileHTTPHandler extends HTTPHandler { 15 | 16 | private HTTPStrategy staticFileStrategy; 17 | private ClassParse parse = ClassParse.getInstance(); 18 | 19 | @Override 20 | public void doWithRequest(Request req, Response res) throws HTTPException { 21 | try { 22 | staticFileStrategy = (HTTPStrategy)parse.getObject(Config.STATIC_FILE_STRATEGYS.get(req.getHandlerStrategyName())); 23 | staticFileStrategy.doWithStrategy(req, res); 24 | } catch (HTTPException e) { 25 | throw e; 26 | } catch (Exception e) { 27 | throw new ServerException(e); 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/listener/socket/SocketListener.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.listener.socket; 2 | 3 | import com.zjh.tears.exception.FilterException; 4 | import com.zjh.tears.model.SocketObject; 5 | import com.zjh.tears.process.HTTPProcess; 6 | 7 | /** 8 | * Created by zhangjiahao on 2017/2/7. 9 | */ 10 | public abstract class SocketListener { 11 | 12 | private SocketListener next; 13 | 14 | public void setNext(SocketListener next) { 15 | this.next = next; 16 | } 17 | 18 | public SocketListener getNext() { 19 | return this.next; 20 | } 21 | 22 | public void preHandler(SocketObject socketObject) {} 23 | 24 | public void afterHandler(SocketObject socketObject) {} 25 | 26 | public byte[] doHandler(SocketObject socketObject, HTTPProcess target) throws FilterException { 27 | this.preHandler(socketObject); 28 | byte[] res; 29 | if(this.getNext() != null) { 30 | res = this.getNext().doHandler(socketObject, target); 31 | } else { 32 | res = target.process(socketObject); 33 | } 34 | this.afterHandler(socketObject); 35 | return res; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/ShutdownServer.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears; 2 | 3 | import com.zjh.tears.model.SocketObject; 4 | 5 | import java.io.IOException; 6 | import java.net.ServerSocket; 7 | 8 | /** 9 | * Created by zhangjiahao on 2017/2/20. 10 | */ 11 | public class ShutdownServer extends Server { 12 | 13 | private Server server; 14 | 15 | public ShutdownServer(int port, Server server) { 16 | super(port); 17 | this.server = server; 18 | } 19 | 20 | @Override 21 | public void start() { 22 | try { 23 | ServerSocket serverSocket = new ServerSocket(this.getPort()); 24 | boolean flag = true; 25 | while(flag) { 26 | SocketObject shutdown = new SocketObject(serverSocket.accept()); 27 | shutdown.read(); 28 | if("shutdown".equals(shutdown.getRequestSource().replaceAll("\n|\r|\t",""))) { 29 | server.destory(); 30 | shutdown.destory(); 31 | flag = false; 32 | serverSocket.close(); 33 | } 34 | } 35 | } catch (IOException e) { 36 | e.printStackTrace(); 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/handler/AcceptFileHTTPHandler.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.handler; 2 | 3 | import com.zjh.tears.config.Config; 4 | import com.zjh.tears.exception.HTTPException; 5 | import com.zjh.tears.exception.PermissionException; 6 | import com.zjh.tears.model.Request; 7 | import com.zjh.tears.model.Response; 8 | import java.io.File; 9 | import java.util.regex.Pattern; 10 | 11 | /** 12 | * Created by zhangjiahao on 2017/2/11. 13 | */ 14 | public class AcceptFileHTTPHandler extends HTTPHandler { 15 | @Override 16 | public void doWithRequest(Request req, Response res) throws HTTPException { 17 | if(Config.ACCEPT_CONFIG_USAGE) { 18 | boolean accept = false; 19 | String path = req.getRequestPath(); 20 | for(Pattern p : Config.ACCEPT_FILE) { 21 | if(p.matcher(path).matches()) { 22 | accept = true; 23 | break; 24 | } 25 | } 26 | for(Pattern p : Config.EXCEPT_FILE) { 27 | if(p.matcher(path).matches()) { 28 | accept = false; 29 | break; 30 | } 31 | } 32 | if(!accept) { 33 | throw new PermissionException(); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/handler/StaticFileChooseStrategyHTTPHandler.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.handler; 2 | 3 | import com.zjh.tears.config.Config; 4 | import com.zjh.tears.exception.HTTPException; 5 | import com.zjh.tears.model.Request; 6 | import com.zjh.tears.model.Response; 7 | 8 | import java.util.regex.Matcher; 9 | import java.util.regex.Pattern; 10 | 11 | /** 12 | * Created by zhangjiahao on 2017/2/10. 13 | */ 14 | public class StaticFileChooseStrategyHTTPHandler extends HTTPHandler { 15 | @Override 16 | public void doWithRequest(Request req, Response res) throws HTTPException { 17 | if(Config.REVERSE_PROXY_USAGE) { 18 | for(String key : Config.REVERSE_PROXY_MAPPING.keySet()) { 19 | Pattern p = Pattern.compile(key); 20 | Matcher m = p.matcher(req.getRequestPath()); 21 | if(m.matches()) { 22 | req.setReverseProxy(Config.REVERSE_PROXY_MAPPING.get(key)); 23 | req.setHandlerStrategyName("ReverseProxy"); 24 | return; 25 | } 26 | } 27 | } 28 | 29 | if(req.getHeaders().containsKey("Range")) { 30 | req.setHandlerStrategyName("206"); 31 | } else { 32 | req.setHandlerStrategyName("200"); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/manager/SocketChainManager.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.manager; 2 | 3 | import com.zjh.tears.config.Config; 4 | import com.zjh.tears.exception.FilterException; 5 | import com.zjh.tears.filter.socket.SocketFilterChain; 6 | import com.zjh.tears.listener.socket.SocketListenerChain; 7 | import com.zjh.tears.model.SocketObject; 8 | import com.zjh.tears.process.HTTPProcess; 9 | 10 | /** 11 | * Created by zhangjiahao on 2017/2/8. 12 | */ 13 | public class SocketChainManager { 14 | 15 | private HTTPProcess target; 16 | private SocketFilterChain socketFilterChain; 17 | private SocketListenerChain socketListenerChain; 18 | 19 | public SocketChainManager(HTTPProcess target) { 20 | this.target = target; 21 | this.socketFilterChain = new SocketFilterChain(Config.SOCKET_FILTER_HEADER); 22 | this.socketListenerChain = new SocketListenerChain(Config.SOCKET_LISTENER_HEADER); 23 | } 24 | 25 | public byte[] execute(SocketObject socketObject) throws FilterException { 26 | this.socketFilterChain.execute(socketObject); 27 | return this.socketListenerChain.execute(socketObject, this.target); 28 | } 29 | 30 | public void destory() { 31 | target = null; 32 | if(socketFilterChain != null) { 33 | socketFilterChain.destory(); 34 | socketFilterChain = null; 35 | } 36 | if(socketListenerChain != null) { 37 | socketListenerChain.destory(); 38 | socketListenerChain = null; 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.zjh.tears 8 | Tears 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.apache.tika 14 | tika-core 15 | 1.28.3 16 | 17 | 18 | org.json 19 | json 20 | 20231013 21 | 22 | 23 | log4j 24 | log4j 25 | 1.2.17 26 | 27 | 28 | 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-compiler-plugin 34 | 3.3 35 | 36 | 1.8 37 | 1.8 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/process/ResponseProcess.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.process; 2 | 3 | import com.zjh.tears.exception.HTTPException; 4 | import com.zjh.tears.factory.ErrorResponseFactory; 5 | import com.zjh.tears.manager.HTTPHandlerChainManager; 6 | import com.zjh.tears.model.Response; 7 | import com.zjh.tears.model.Request; 8 | import com.zjh.tears.util.Util; 9 | import org.apache.log4j.Logger; 10 | 11 | /** 12 | * Created by zhangjiahao on 2017/2/8. 13 | */ 14 | public class ResponseProcess { 15 | 16 | private Logger logger = Logger.getLogger(this.getClass()); 17 | private HTTPHandlerChainManager httpHandlerChainManager; 18 | 19 | public ResponseProcess() { 20 | this.httpHandlerChainManager = new HTTPHandlerChainManager(); 21 | } 22 | 23 | public Response getResponse(Request req) { 24 | Response response = new Response(); 25 | try { 26 | httpHandlerChainManager.doWithRequest(req, response); 27 | } catch (HTTPException e) { 28 | response = ErrorResponseFactory.getInstance().createErrorPage(e, req); 29 | logger.warn(Util.stackTraceToString(e)); 30 | } catch (Exception e) { 31 | response = ErrorResponseFactory.getInstance().create500ErrorPage(req); 32 | logger.warn(Util.stackTraceToString(e)); 33 | } 34 | return response; 35 | } 36 | 37 | public void destory() { 38 | if(httpHandlerChainManager != null) { 39 | httpHandlerChainManager.destory(); 40 | httpHandlerChainManager = null; 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/process/HTTPProcess.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.process; 2 | 3 | import com.zjh.tears.exception.FilterException; 4 | import com.zjh.tears.factory.RequestFactory; 5 | import com.zjh.tears.manager.SocketChainManager; 6 | import com.zjh.tears.model.Request; 7 | import com.zjh.tears.model.Response; 8 | import com.zjh.tears.model.SocketObject; 9 | 10 | /** 11 | * Created by zhangjiahao on 2017/2/7. 12 | */ 13 | public class HTTPProcess { 14 | 15 | private SocketChainManager socketChainManager; 16 | private ResponseProcess responseProcess; 17 | 18 | public HTTPProcess() { 19 | this.responseProcess = new ResponseProcess(); 20 | } 21 | 22 | public void setSocketChainManager(SocketChainManager socketChainManager) { 23 | this.socketChainManager = socketChainManager; 24 | } 25 | 26 | public byte[] getResponse(SocketObject socketObject) throws FilterException { 27 | return socketChainManager.execute(socketObject); 28 | } 29 | 30 | public byte[] process(SocketObject socketObject) { 31 | Request request = RequestFactory.getInstance().create(socketObject); 32 | Response response = this.responseProcess.getResponse(request); 33 | socketObject.setResponse(response); 34 | return socketObject.getResponse().getBytes(); 35 | } 36 | 37 | public void destory() { 38 | if(socketChainManager != null) { 39 | socketChainManager.destory(); 40 | socketChainManager = null; 41 | } 42 | if(responseProcess != null) { 43 | responseProcess.destory(); 44 | responseProcess = null; 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/process/SocketProcess.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.process; 2 | 3 | import com.zjh.tears.exception.FilterException; 4 | import com.zjh.tears.exception.ShutdownException; 5 | import com.zjh.tears.manager.SocketChainManager; 6 | import com.zjh.tears.model.SocketObject; 7 | import com.zjh.tears.util.Util; 8 | import org.apache.log4j.Logger; 9 | 10 | import java.io.IOException; 11 | import java.net.Socket; 12 | 13 | /** 14 | * Created by zhangjiahao on 2017/2/7. 15 | */ 16 | public class SocketProcess implements Runnable { 17 | 18 | private Socket socket; 19 | private HTTPProcess httpProcess; 20 | 21 | private Logger logger = Logger.getLogger(this.getClass()); 22 | 23 | public SocketProcess(Socket socket) { 24 | this.socket = socket; 25 | this.httpProcess = new HTTPProcess(); 26 | this.httpProcess.setSocketChainManager(new SocketChainManager(httpProcess)); 27 | } 28 | 29 | public void process() { 30 | SocketObject socketObject = new SocketObject(this.socket); 31 | try { 32 | socketObject.read(); 33 | byte[] res = httpProcess.getResponse(socketObject); 34 | socketObject.response(res); 35 | } catch (java.net.SocketException e) { 36 | // 这里有时会报Broken pipe错误, 暂时没想好如何解决 37 | } catch (IOException e){ 38 | // logger.warn(Util.stackTraceToString(e)); 39 | } catch (FilterException e) { 40 | logger.warn(e.getMessage()); 41 | } finally { 42 | socketObject.destory(); 43 | httpProcess.destory(); 44 | } 45 | } 46 | 47 | @Override 48 | public void run() { 49 | process(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/util/Util.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.util; 2 | 3 | import org.apache.tika.Tika; 4 | 5 | import java.io.File; 6 | import java.io.FileInputStream; 7 | import java.io.IOException; 8 | import java.text.SimpleDateFormat; 9 | import java.util.Date; 10 | import java.util.Locale; 11 | import java.util.TimeZone; 12 | import java.util.stream.Collectors; 13 | import java.util.stream.Stream; 14 | 15 | /** 16 | * Created by zhangjiahao on 2017/2/8. 17 | */ 18 | public class Util { 19 | private Util() { 20 | } 21 | 22 | private static Tika tika = new Tika(); 23 | 24 | public static byte[] byteMerger(byte[] byte_1, byte[] byte_2) { 25 | if(byte_2 != null) { 26 | byte[] byte_3 = new byte[byte_1.length + byte_2.length]; 27 | System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length); 28 | System.arraycopy(byte_2, 0, byte_3, byte_1.length, byte_2.length); 29 | return byte_3; 30 | } else { 31 | return byte_1; 32 | } 33 | } 34 | 35 | public static String getContentType(File f) { 36 | try { 37 | return tika.detect(f); 38 | } catch (Exception e) { 39 | return "text/plain"; 40 | } 41 | } 42 | 43 | public static String getGMTString(Date date) { 44 | SimpleDateFormat sdf = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss 'GMT'", Locale.US); 45 | sdf.setTimeZone(TimeZone.getTimeZone("GMT")); 46 | return sdf.format(date); 47 | } 48 | 49 | public static String getGMTString() { 50 | return Util.getGMTString(new Date()); 51 | } 52 | 53 | public static String stackTraceToString(Throwable e) { 54 | return Stream.of(e.getStackTrace()).map(StackTraceElement::toString).collect(Collectors.joining("\n")); 55 | } 56 | 57 | public static void closeFileInputStream(FileInputStream fis) { 58 | if (fis != null) { 59 | try { 60 | fis.close(); 61 | } catch (IOException e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/strategy/DefaultCode206Strategy.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.strategy; 2 | 3 | import com.zjh.tears.exception.HTTPException; 4 | import com.zjh.tears.exception.NotFoundException; 5 | import com.zjh.tears.exception.ServerException; 6 | import com.zjh.tears.model.Request; 7 | import com.zjh.tears.model.Response; 8 | import com.zjh.tears.util.Util; 9 | 10 | import java.io.File; 11 | import java.io.FileInputStream; 12 | import java.io.IOException; 13 | import java.util.Date; 14 | 15 | /** 16 | * Created by zhangjiahao on 2017/2/9. 17 | */ 18 | public class DefaultCode206Strategy implements HTTPStrategy { 19 | @Override 20 | public void doWithStrategy(Request req, Response res) throws HTTPException { 21 | File file = new File(req.getRealPath()); 22 | FileInputStream fis = null; 23 | try { 24 | String[] range = req.getHeaders().get("Range").split("=")[1].split("-"); 25 | long start = Long.valueOf(range[0]); 26 | long end; 27 | try { 28 | end = Long.valueOf(range[1]); 29 | } catch (ArrayIndexOutOfBoundsException e) { 30 | end = file.length() - 1; 31 | } 32 | int size = (int) (end - start + 1); 33 | byte[] body = new byte[size]; 34 | fis = new FileInputStream(file); 35 | fis.skip(start); 36 | fis.read(body); 37 | res.setVersion(req.getVersion()); 38 | res.setMessage("Partial Content"); 39 | res.setCode(206); 40 | res.setBody(body); 41 | res.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + file.length()); 42 | res.setHeader("Content-Type", Util.getContentType(file)); 43 | res.setHeader("Last-Modified", Util.getGMTString(new Date(file.lastModified()))); 44 | res.setHeader("Connection", "close"); 45 | } catch(java.nio.file.NoSuchFileException|java.io.FileNotFoundException e) { 46 | throw new NotFoundException(e); 47 | } catch (IOException e) { 48 | throw new ServerException(e); 49 | } finally { 50 | Util.closeFileInputStream(fis); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /target/classes/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "serverConfig": { 3 | "port": "8080", 4 | "threadPoolSize": "10", 5 | "timeout": "1000", 6 | "serverName": "Tears 1.5.2", 7 | "staticRootFile": "/Users/zhangjiahao/Desktop/play", 8 | "defaultCharset": "UTF-8", 9 | "socketFilter": [ 10 | "com.zjh.tears.filter.socket.HTTPRequestSocketFilter" 11 | ], 12 | "socketListener": [ 13 | "com.zjh.tears.listener.socket.LogSocketListener" 14 | ], 15 | "httpHandler": [ 16 | "com.zjh.tears.handler.DefaultIndexHTTPHandler", 17 | "com.zjh.tears.handler.URLDecodeHTTPHandler", 18 | "com.zjh.tears.handler.AcceptFileHTTPHandler", 19 | "com.zjh.tears.handler.StaticFileChooseStrategyHTTPHandler", 20 | "com.zjh.tears.handler.StaticFileHTTPHandler", 21 | "com.zjh.tears.handler.ResponseHeaderHTTPHandler" 22 | ], 23 | 24 | "staticFileStrategy": [ 25 | { 26 | "name": "200", 27 | "strategy": "com.zjh.tears.strategy.DefaultCode200Strategy" 28 | }, 29 | { 30 | "name": "206", 31 | "strategy": "com.zjh.tears.strategy.DefaultCode206Strategy" 32 | }, 33 | { 34 | "name": "ReverseProxy", 35 | "strategy": "com.zjh.tears.strategy.ReverseProxyStrategy" 36 | } 37 | ], 38 | 39 | "reverseProxy": { 40 | "usage": "true", 41 | "proxy": [ 42 | { 43 | "rule": "*.jsp", 44 | "target": "http://localhost:8888/temp", 45 | "reverseProxyCookie": [ 46 | { 47 | "from": "/temp/", 48 | "to": "/" 49 | } 50 | ] 51 | } 52 | ] 53 | } 54 | 55 | }, 56 | "pageConfig": { 57 | "defaultIndex": [ 58 | "index.html", 59 | "page.html" 60 | ], 61 | "errorPages": [ 62 | { 63 | "code": "404", 64 | "page": "404.html" 65 | }, 66 | { 67 | "code": "403", 68 | "page": "403.html" 69 | }, 70 | { 71 | "code": "500", 72 | "page": "500.html" 73 | } 74 | ] 75 | }, 76 | "acceptConfig": { 77 | "usage": "true", 78 | "accept":[ 79 | "/*.*", 80 | "/*/*.*", 81 | "/*/*/*.*" 82 | ], 83 | "except": [ 84 | "/config.jsp" 85 | ] 86 | } 87 | } -------------------------------------------------------------------------------- /src/main/resources/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "serverConfig": { 3 | "port": "8080", 4 | "threadPoolSize": "10", 5 | "timeout": "1000", 6 | "serverName": "Tears 1.5.2", 7 | "staticRootFile": "/Users/zhangjiahao/Desktop/play", 8 | "defaultCharset": "UTF-8", 9 | "socketFilter": [ 10 | "com.zjh.tears.filter.socket.HTTPRequestSocketFilter" 11 | ], 12 | "socketListener": [ 13 | "com.zjh.tears.listener.socket.LogSocketListener" 14 | ], 15 | "httpHandler": [ 16 | "com.zjh.tears.handler.DefaultIndexHTTPHandler", 17 | "com.zjh.tears.handler.URLDecodeHTTPHandler", 18 | "com.zjh.tears.handler.AcceptFileHTTPHandler", 19 | "com.zjh.tears.handler.StaticFileChooseStrategyHTTPHandler", 20 | "com.zjh.tears.handler.StaticFileHTTPHandler", 21 | "com.zjh.tears.handler.ResponseHeaderHTTPHandler" 22 | ], 23 | 24 | "staticFileStrategy": [ 25 | { 26 | "name": "200", 27 | "strategy": "com.zjh.tears.strategy.DefaultCode200Strategy" 28 | }, 29 | { 30 | "name": "206", 31 | "strategy": "com.zjh.tears.strategy.DefaultCode206Strategy" 32 | }, 33 | { 34 | "name": "ReverseProxy", 35 | "strategy": "com.zjh.tears.strategy.ReverseProxyStrategy" 36 | } 37 | ], 38 | 39 | "reverseProxy": { 40 | "usage": "true", 41 | "proxy": [ 42 | { 43 | "rule": "*.jsp", 44 | "target": "http://localhost:8888/temp", 45 | "reverseProxyCookie": [ 46 | { 47 | "from": "/temp/", 48 | "to": "/" 49 | } 50 | ] 51 | } 52 | ] 53 | } 54 | 55 | }, 56 | "pageConfig": { 57 | "defaultIndex": [ 58 | "index.html", 59 | "page.html" 60 | ], 61 | "errorPages": [ 62 | { 63 | "code": "404", 64 | "page": "404.html" 65 | }, 66 | { 67 | "code": "403", 68 | "page": "403.html" 69 | }, 70 | { 71 | "code": "500", 72 | "page": "500.html" 73 | } 74 | ] 75 | }, 76 | "acceptConfig": { 77 | "usage": "true", 78 | "accept":[ 79 | "/*.*", 80 | "/*/*.*", 81 | "/*/*/*.*" 82 | ], 83 | "except": [ 84 | "/config.jsp" 85 | ] 86 | } 87 | } -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/Server.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears; 2 | 3 | import com.zjh.tears.config.Config; 4 | import com.zjh.tears.process.SocketProcess; 5 | import org.apache.log4j.Logger; 6 | 7 | import java.io.IOException; 8 | import java.net.ServerSocket; 9 | import java.net.Socket; 10 | import java.util.concurrent.ExecutorService; 11 | import java.util.concurrent.Executors; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | /** 15 | * Created by zhangjiahao on 2017/2/7. 16 | */ 17 | public class Server { 18 | 19 | private Logger logger = Logger.getLogger(this.getClass()); 20 | 21 | private int port; 22 | private int threadPoolSize; 23 | private ServerSocket serverSocket; 24 | private ExecutorService threadPool; 25 | 26 | private boolean flag = true; 27 | 28 | public Server(int port) { 29 | this.port = port; 30 | } 31 | 32 | public Server(int port, int threadPoolSize) { 33 | this.port = port; 34 | this.threadPoolSize = threadPoolSize; 35 | this.threadPool = Executors.newFixedThreadPool(this.threadPoolSize); 36 | } 37 | 38 | public int getPort() { 39 | return port; 40 | } 41 | 42 | public void start() { 43 | try { 44 | this.serverSocket = new ServerSocket(this.port); 45 | 46 | while (this.flag) { 47 | Socket socket = serverSocket.accept(); 48 | socket.setSoTimeout(Config.TIMEOUT); 49 | if(!threadPool.isShutdown()) 50 | threadPool.execute(new SocketProcess(socket)); 51 | } 52 | } catch (java.net.SocketException e) { 53 | // Todo 没想好怎么处理 54 | } catch (IOException e) { 55 | // e.printStackTrace(); 56 | } 57 | logger.debug("Bye~"); 58 | } 59 | 60 | public void destory() { 61 | this.flag = false; 62 | try { 63 | this.serverSocket.close(); 64 | } catch (IOException e) { 65 | e.printStackTrace(); 66 | } 67 | this.threadPool.shutdown(); 68 | try { 69 | if(!this.threadPool.awaitTermination(10, TimeUnit.MILLISECONDS)) { 70 | this.threadPool.shutdownNow(); 71 | } 72 | } catch (InterruptedException e) { 73 | this.threadPool.shutdownNow(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/factory/ErrorResponseFactory.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.factory; 2 | 3 | import com.zjh.tears.config.Config; 4 | import com.zjh.tears.exception.HTTPException; 5 | import com.zjh.tears.model.Request; 6 | import com.zjh.tears.model.Response; 7 | import com.zjh.tears.util.Util; 8 | 9 | import java.io.IOException; 10 | import java.nio.file.Files; 11 | import java.io.File; 12 | 13 | 14 | /** 15 | * Created by zhangjiahao on 2017/2/8. 16 | */ 17 | public class ErrorResponseFactory { 18 | 19 | private ErrorResponseFactory() {} 20 | 21 | private static final class Holder { 22 | public static ErrorResponseFactory instance = new ErrorResponseFactory(); 23 | } 24 | 25 | public static ErrorResponseFactory getInstance() { 26 | return Holder.instance; 27 | } 28 | 29 | private void setDefaultHeaders(Request req, Response res) { 30 | res.setHeader("Content-Length", String.valueOf(res.getBody().length) + "; charset=" + Config.DEFAULT_CHARSET); 31 | res.setHeader("Server", Config.SERVER_NAME); 32 | res.setHeader("Content-Type", "text/html; charset=" + Config.DEFAULT_CHARSET); 33 | res.setHeader("Date", Util.getGMTString()); 34 | res.setHeader("Accept-Ranges", "bytes"); 35 | res.setHeader("Connection", "close"); 36 | } 37 | 38 | public Response createErrorPage(Request req, int code, String message, String file) { 39 | Response res = new Response(); 40 | res.setVersion(req.getVersion()); 41 | res.setMessage(message); 42 | res.setCode(code); 43 | byte[] resByte; 44 | try { 45 | resByte = Files.readAllBytes(new File(Config.STATIC_ROOT_FILE + File.separator + file).toPath()); 46 | } catch (IOException e) { 47 | resByte = ("This is default " + code + " Error Page, Please set your custom Error Page").getBytes(); 48 | } 49 | res.setBody(resByte); 50 | this.setDefaultHeaders(req, res); 51 | return res; 52 | } 53 | 54 | public Response createErrorPage(HTTPException e, Request req) { 55 | return createErrorPage(req, e.getCode(), e.getMessage(), Config.ERR_PAGES.getOrDefault(e.getCode(), e.getCode() + ".html")); 56 | } 57 | 58 | public Response create500ErrorPage(Request req) { 59 | return createErrorPage(req, 500, "Server Error", Config.ERR_PAGES.getOrDefault(500, "500.html")); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/factory/RequestFactory.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.factory; 2 | 3 | 4 | import com.zjh.tears.model.HTTPMethod; 5 | import com.zjh.tears.model.Request; 6 | import com.zjh.tears.model.SocketObject; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | import java.util.stream.Stream; 11 | 12 | /** 13 | * Created by zhangjiahao on 2017/2/8. 14 | */ 15 | public class RequestFactory { 16 | 17 | private RequestFactory(){} 18 | 19 | private final static class Handler { 20 | public static final RequestFactory instance = new RequestFactory(); 21 | } 22 | 23 | public static RequestFactory getInstance() { 24 | return Handler.instance; 25 | } 26 | 27 | private void initFirstLine(String line, Request req) { 28 | String[] args = line.split(" "); 29 | req.setMethod(HTTPMethod.parse(args[0])); 30 | req.setPath(args[1]); 31 | req.setVersion(args[2]); 32 | } 33 | 34 | private void initHeaders(String line, Request req) { 35 | Map headers = new HashMap<>(); 36 | Stream.of(line.split("\n")) 37 | .map(str -> str.split(":", 2)) 38 | .forEach(arr -> headers.put(arr[0].trim(), arr[1].trim())); 39 | req.setHeaders(headers); 40 | } 41 | 42 | private void initBody(String line, Request req) { 43 | req.setBody(line); 44 | } 45 | 46 | public Request create(SocketObject socketObject) { 47 | Request request = socketObject.getRequest(); 48 | request.setRequestIP(socketObject.getSocket().getInetAddress().getHostAddress()); 49 | String[] analyStringArray = request.getRequestSource().split("\n"); 50 | String firstLine = analyStringArray[0]; 51 | StringBuilder headersBuilder = new StringBuilder(); 52 | StringBuilder bodyBuilder = new StringBuilder(); 53 | int index; 54 | for(index = 1; index < analyStringArray.length && analyStringArray[index].length() != 0; index++) { 55 | headersBuilder.append(analyStringArray[index] + "\n"); 56 | } 57 | for(;index < analyStringArray.length;index++) { 58 | bodyBuilder.append(analyStringArray[index]); 59 | } 60 | String header = headersBuilder.toString(); 61 | String body = bodyBuilder.toString(); 62 | this.initFirstLine(firstLine, request); 63 | this.initHeaders(header, request); 64 | this.initBody(body, request); 65 | 66 | return request; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/model/ReverseProxy.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.model; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * Created by zhangjiahao on 2017/2/17. 8 | */ 9 | public class ReverseProxy implements Comparable { 10 | private String rule; 11 | private String target; 12 | private Map cookiesMapping; 13 | 14 | public ReverseProxy(String rule, String target) { 15 | this(rule, target, new HashMap<>()); 16 | } 17 | 18 | public ReverseProxy(String rule, String target, Map cookiesMapping) { 19 | this.rule = rule; 20 | this.target = target; 21 | this.cookiesMapping = cookiesMapping; 22 | } 23 | 24 | public String getCookieMappingOrDefault(String from, String defaultVal) { 25 | return this.cookiesMapping.getOrDefault(from, defaultVal); 26 | } 27 | 28 | public void addCookieMapping(String from, String to) { 29 | this.cookiesMapping.put(from, to); 30 | } 31 | 32 | public String getRule() { 33 | return rule; 34 | } 35 | 36 | public void setRule(String rule) { 37 | this.rule = rule; 38 | } 39 | 40 | public String getTarget() { 41 | return target; 42 | } 43 | 44 | public void setTarget(String target) { 45 | this.target = target; 46 | } 47 | 48 | public Map getCookiesMapping() { 49 | return cookiesMapping; 50 | } 51 | 52 | public void setCookiesMapping(Map cookiesMapping) { 53 | this.cookiesMapping = cookiesMapping; 54 | } 55 | 56 | @Override 57 | public int compareTo(Object o) { 58 | if(o instanceof ReverseProxy) { 59 | ReverseProxy obj = (ReverseProxy) o; 60 | return this.rule != null ? this.rule.compareTo(obj.getRule()) : (obj.rule == null ? 0 : -1); 61 | } 62 | return -1; 63 | } 64 | 65 | @Override 66 | public boolean equals(Object o) { 67 | if (this == o) return true; 68 | if (o == null || getClass() != o.getClass()) return false; 69 | ReverseProxy that = (ReverseProxy) o; 70 | return this.rule != null ? this.rule.equals(that.rule) : this.rule == null; 71 | } 72 | 73 | @Override 74 | public int hashCode() { 75 | return rule.hashCode(); 76 | } 77 | 78 | @Override 79 | public String toString() { 80 | return "ReverseProxy{" + 81 | "rule='" + rule + '\'' + 82 | ", target='" + target + '\'' + 83 | ", cookiesMapping=" + cookiesMapping + 84 | '}'; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/strategy/DefaultCode200Strategy.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.strategy; 2 | 3 | import com.zjh.tears.config.Config; 4 | import com.zjh.tears.exception.HTTPException; 5 | import com.zjh.tears.exception.NotFoundException; 6 | import com.zjh.tears.exception.ServerException; 7 | import com.zjh.tears.model.Request; 8 | import com.zjh.tears.model.Response; 9 | import com.zjh.tears.util.Util; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | import java.nio.file.Files; 14 | import java.text.ParseException; 15 | import java.text.SimpleDateFormat; 16 | import java.util.Calendar; 17 | import java.util.Date; 18 | import java.util.Locale; 19 | import java.util.TimeZone; 20 | 21 | /** 22 | * Created by zhangjiahao on 2017/2/9. 23 | */ 24 | public class DefaultCode200Strategy implements HTTPStrategy { 25 | @Override 26 | public void doWithStrategy(Request req, Response res) throws HTTPException { 27 | try { 28 | File file = new File(req.getRealPath()); 29 | if (!file.exists()) { 30 | throw new NotFoundException(null); 31 | } 32 | if(req.getHeaders().containsKey("If-Modified-Since")) { 33 | this.modified(req, res, file); 34 | } else { 35 | this.normal(req, res, file); 36 | } 37 | } catch (IOException e) { 38 | throw new ServerException(e); 39 | } 40 | } 41 | 42 | public void modified(Request req, Response res, File file) throws IOException { 43 | String modifiedSince = req.getHeaders().get("If-Modified-Since"); 44 | SimpleDateFormat sdf = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss 'GMT'", Locale.US); 45 | sdf.setTimeZone(TimeZone.getTimeZone("GMT")); 46 | try { 47 | Date date = sdf.parse(modifiedSince); 48 | long lastModify = file.lastModified(); 49 | if(date.getTime() == lastModify) { 50 | res.setCode(304); 51 | res.setMessage("Not Modified"); 52 | res.setVersion(req.getVersion()); 53 | res.setHeader("Content-Type", Util.getContentType(file) + "; charset=" + Config.DEFAULT_CHARSET); 54 | res.setHeader("Connection", "close"); 55 | res.setHeader("Last-Modified", Util.getGMTString(new Date(file.lastModified()))); 56 | } 57 | } catch (ParseException e) { 58 | this.normal(req, res, file); 59 | } 60 | } 61 | 62 | public void normal(Request req, Response res, File file) throws IOException { 63 | res.setVersion(req.getVersion()); 64 | res.setMessage("OK"); 65 | res.setCode(200); 66 | res.setBody(Files.readAllBytes(file.toPath())); 67 | res.setHeader("Cache-Control", "max-age=3600"); 68 | res.setHeader("Content-Type", Util.getContentType(file) + "; charset=" + Config.DEFAULT_CHARSET); 69 | res.setHeader("Last-Modified", Util.getGMTString(new Date(file.lastModified()))); 70 | res.setHeader("Connection", "close"); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/strategy/ReverseProxyStrategy.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.strategy; 2 | 3 | import com.zjh.tears.exception.HTTPException; 4 | import com.zjh.tears.model.Request; 5 | import com.zjh.tears.model.Response; 6 | import com.zjh.tears.util.HTTPRequest; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.regex.Matcher; 12 | import java.util.regex.Pattern; 13 | 14 | /** 15 | * Created by zhangjiahao on 2017/2/12. 16 | */ 17 | public class ReverseProxyStrategy implements HTTPStrategy { 18 | 19 | /** 20 | * 这mapping也太不优雅了 // 其实蛮优雅的啊! 21 | */ 22 | @Override 23 | public void doWithStrategy(Request req, Response res) throws HTTPException { 24 | String target = req.getReverseProxy().getTarget() + req.getPath(); 25 | HTTPRequest request; 26 | req.getHeaders().put("X-Forwarded-For", req.getRequestIP()); 27 | if("POST".equals(req.getMethod().name())) { 28 | request = HTTPRequest.post(target, req.getHeaders(), req.getBody().getBytes()); 29 | } else { 30 | request = HTTPRequest.get(target, req.getHeaders()); 31 | } 32 | 33 | this.setResponseHeaders(req, res, request); 34 | res.setBody(request.getBodyByte()); 35 | } 36 | 37 | 38 | public void initFirstLine(Response res, String line) { 39 | String[] firstLine = line.split(" "); 40 | res.setVersion(firstLine[0]); 41 | res.setCode(Integer.valueOf(firstLine[1])); 42 | res.setMessage(firstLine[2]); 43 | } 44 | 45 | public void mappingHeaders(Response res, Map> headersFromProxy) { 46 | headersFromProxy.forEach((key, value) -> { 47 | if (key != null) { 48 | res.setHeader(key, value); 49 | } 50 | }); 51 | } 52 | 53 | private void mappingCookie(Request req, Response res, List cookies) { 54 | Map cookieMapping = req.getReverseProxy().getCookiesMapping(); 55 | for(final String cookie : cookies) { 56 | if(cookie.contains("Path")) { 57 | boolean isHTTPOnly = cookie.contains("HttpOnly"); 58 | String cookieStr = cookie.substring(0, cookie.indexOf("Path=")); 59 | String path = "/"; 60 | Pattern p = Pattern.compile("Path=((.*);\\s?|(.*))"); 61 | Matcher m = p.matcher(cookie); 62 | while(m.find()) { 63 | if(m.group(2) == null) { 64 | path = m.group(3); 65 | } else { 66 | path = m.group(2); 67 | } 68 | } 69 | String mappingPath = cookieMapping.getOrDefault(path, path); 70 | cookieStr = cookieStr + "Path=" + mappingPath + (isHTTPOnly ? "; HttpOnly" : ""); 71 | res.addHeader("Set-Cookie", cookieStr); 72 | } else { 73 | res.addHeader("Set-Cookie", cookie); 74 | } 75 | } 76 | } 77 | 78 | private void setResponseHeaders(Request req, Response res, HTTPRequest request) { 79 | String firstLine = request.getResponseHeader().get(null).get(0); 80 | this.initFirstLine(res, firstLine); 81 | 82 | Map> headersFromProxy = request.getResponseHeader(); 83 | this.mappingHeaders(res, headersFromProxy); 84 | 85 | if(headersFromProxy.containsKey("Set-Cookie")) { 86 | List cookies = headersFromProxy.get("Set-Cookie"); 87 | this.mappingCookie(req, res, cookies); 88 | } 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/model/Request.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.model; 2 | 3 | import com.zjh.tears.config.Config; 4 | 5 | import java.util.Map; 6 | 7 | /** 8 | * Created by zhangjiahao on 2017/2/8. 9 | */ 10 | public class Request { 11 | 12 | private String requestIP; 13 | 14 | private String requestSource; 15 | private HTTPMethod method; 16 | private String path; // 这个path是带参数的 17 | private String requestPath; // 这个path是不带参数的 18 | private String realPath; // 这个path是不带参数并且映射为真实路径的 19 | private String version; 20 | private Map headers; 21 | private String body; 22 | 23 | private String handlerStrategyName; 24 | 25 | private ReverseProxy reverseProxy; 26 | 27 | 28 | @Override 29 | public String toString() { 30 | return "Request{" + "\n" + 31 | "requestSource='" + requestSource + '\'' + "\n" + 32 | ", method='" + method + '\'' + "\n" + 33 | ", path='" + path + '\'' + "\n" + 34 | ", version='" + version + '\'' + "\n" + 35 | ", headers=" + headers + "\n" + 36 | ", body='" + body + '\'' + "\n" + 37 | '}'; 38 | } 39 | 40 | public String getRequestSource() { 41 | return requestSource; 42 | } 43 | 44 | public void setRequestSource(String requestSource) { 45 | this.requestSource = requestSource; 46 | } 47 | 48 | public HTTPMethod getMethod() { 49 | return method; 50 | } 51 | 52 | public void setMethod(HTTPMethod method) { 53 | this.method = method; 54 | } 55 | 56 | public String getPath() { 57 | return path; 58 | } 59 | 60 | public void setPath(String path) { 61 | this.path = path; 62 | this.setRequestPath(this.path.contains("?") ? path.split("\\?")[0] : path); 63 | this.setRealPath(Config.STATIC_ROOT_FILE + (this.path.contains("?") ? path.split("\\?")[0] : path)); 64 | } 65 | 66 | public String getRealPath() {return this.realPath;} 67 | 68 | public void setRealPath(String realPath) {this.realPath = realPath;} 69 | 70 | public String getRequestPath() {return this.requestPath;} 71 | 72 | public void setRequestPath(String path) {this.requestPath = path;} 73 | 74 | public String getVersion() { 75 | return version; 76 | } 77 | 78 | public void setVersion(String version) { 79 | this.version = version; 80 | } 81 | 82 | public Map getHeaders() { 83 | return headers; 84 | } 85 | 86 | public void setHeaders(Map headers) { 87 | this.headers = headers; 88 | } 89 | 90 | public String getBody() { 91 | return body; 92 | } 93 | 94 | public void setBody(String body) { 95 | this.body = body; 96 | } 97 | 98 | public String getHandlerStrategyName() { 99 | return handlerStrategyName; 100 | } 101 | 102 | public void setHandlerStrategyName(String handlerStrategyName) { 103 | this.handlerStrategyName = handlerStrategyName; 104 | } 105 | 106 | public String getRequestIP() { 107 | return requestIP; 108 | } 109 | 110 | public void setRequestIP(String requestIP) { 111 | this.requestIP = requestIP; 112 | } 113 | 114 | public ReverseProxy getReverseProxy() { 115 | return reverseProxy; 116 | } 117 | 118 | public void setReverseProxy(ReverseProxy reverseProxy) { 119 | this.reverseProxy = reverseProxy; 120 | } 121 | 122 | public void destory() { 123 | if(headers != null) { 124 | headers.clear(); 125 | headers = null; 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/model/Response.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.model; 2 | 3 | import com.zjh.tears.util.Util; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | /** 12 | * Created by zhangjiahao on 2017/2/8. 13 | */ 14 | public class Response { 15 | private String version; 16 | private int code; 17 | private String message; 18 | private Map> headers = new HashMap<>(); 19 | private byte[] body; 20 | 21 | public byte[] getBytes() { 22 | StringBuilder sb = new StringBuilder(); 23 | sb.append(version + " "); 24 | sb.append(code + " "); 25 | sb.append(message + "\r\n"); 26 | sb.append(this.headersToString() + "\r\n"); 27 | return Util.byteMerger(sb.toString().getBytes(), this.body); 28 | } 29 | 30 | private String headersToString() { 31 | StringBuilder sb = new StringBuilder(); 32 | headers.forEach((key, value) -> { 33 | if(value.size() == 1) { 34 | sb.append(key + ":" + value.get(0) + "\r\n"); 35 | } else { 36 | for(String val : value) { 37 | sb.append(key + ":" + val + "\r\n"); 38 | } 39 | } 40 | }); 41 | return sb.toString(); 42 | } 43 | 44 | public String getVersion() { 45 | return version; 46 | } 47 | 48 | public void setVersion(String version) { 49 | this.version = version; 50 | } 51 | 52 | public int getCode() { 53 | return code; 54 | } 55 | 56 | public void setCode(int code) { 57 | this.code = code; 58 | } 59 | 60 | public String getMessage() { 61 | return message; 62 | } 63 | 64 | public void setMessage(String message) { 65 | this.message = message; 66 | } 67 | 68 | public Map> getHeaders() { 69 | return headers; 70 | } 71 | 72 | public void setHeaders(Map> headers) { 73 | this.headers = headers; 74 | } 75 | 76 | public byte[] getBody() { 77 | return body; 78 | } 79 | 80 | public void setBody(byte[] body) { 81 | this.body = body; 82 | } 83 | 84 | /** 85 | * addHeader会在原有的基础上增加 86 | */ 87 | public void addHeader(String key, String ... values) { 88 | this.addHeader(key, Arrays.asList(values)); 89 | } 90 | 91 | public void addHeader(String key, List values) { 92 | this.addHeader(key, new ArrayList<>(values)); 93 | } 94 | 95 | public void addHeader(String key, ArrayList values) { 96 | if(this.headers.containsKey(key)) { 97 | this.headers.get(key).addAll(values); 98 | } else { 99 | this.setHeader(key, values); 100 | } 101 | } 102 | 103 | /** 104 | * setHeader会覆盖原有的Header 105 | */ 106 | public void setHeader(String key, String ... values) { 107 | this.setHeader(key, Arrays.asList(values)); 108 | } 109 | 110 | 111 | public void setHeader(String key, List values) { 112 | this.setHeader(key, new ArrayList<>(values)); 113 | } 114 | /** 115 | * setHeader会覆盖原有的Header 116 | */ 117 | public void setHeader(String key, ArrayList values) { 118 | if(this.headers.containsKey(key)) { 119 | this.headers.replace(key, values); 120 | } else { 121 | this.headers.put(key, values); 122 | } 123 | } 124 | 125 | public void destory() { 126 | if(headers != null) { 127 | headers.clear(); 128 | headers = null; 129 | } 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/model/SocketObject.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.model; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.io.OutputStream; 7 | import java.net.Socket; 8 | 9 | /** 10 | * Created by zhangjiahao on 2017/2/7. 11 | */ 12 | public class SocketObject { 13 | private Socket socket; 14 | 15 | private OutputStream os; 16 | private BufferedReader br; 17 | 18 | private Request request; 19 | private Response response; 20 | 21 | public SocketObject(Socket socket) { 22 | this.socket = socket; 23 | } 24 | 25 | public Socket getSocket() { 26 | return this.socket; 27 | } 28 | 29 | public BufferedReader openBufferedReader() throws IOException { 30 | if (this.br == null) { 31 | this.br = new BufferedReader(new InputStreamReader(socket.getInputStream())); 32 | return this.br; 33 | } else { 34 | return this.br; 35 | } 36 | } 37 | 38 | public OutputStream openOutputStream() throws IOException { 39 | if (os == null) { 40 | this.os = socket.getOutputStream(); 41 | return this.os; 42 | } else { 43 | return this.os; 44 | } 45 | } 46 | 47 | public BufferedReader getBufferedReader() { 48 | return this.br; 49 | } 50 | 51 | public OutputStream getOutputStream() { 52 | return this.os; 53 | } 54 | 55 | public void read() throws IOException { 56 | if (this.br == null) { 57 | this.openBufferedReader(); 58 | } 59 | StringBuilder sb = new StringBuilder(); 60 | String line; 61 | while ((line = br.readLine()) != null) { 62 | sb.append(line + "\n"); 63 | if (line.length() == 0) break; 64 | } 65 | while (br.ready()) { 66 | char ch = (char) br.read(); 67 | sb.append(ch); 68 | } 69 | this.getRequest().setRequestSource(sb.toString()); 70 | } 71 | 72 | public void response(byte[] data) throws IOException { 73 | if (this.os == null) { 74 | this.openOutputStream(); 75 | } 76 | os.write(data); 77 | os.flush(); 78 | } 79 | 80 | public void destory() { 81 | try { 82 | this.socket.shutdownInput(); 83 | } catch (IOException e) { 84 | // Todo 没想好怎么处理 85 | } 86 | try { 87 | this.socket.shutdownOutput(); 88 | } catch (IOException e) { 89 | // Todo 没想好怎么处理 90 | } 91 | try { 92 | if (this.br != null) br.close(); 93 | } catch (IOException e) { 94 | e.printStackTrace(); 95 | } 96 | try { 97 | if (this.os != null) os.close(); 98 | } catch (IOException e) { 99 | e.printStackTrace(); 100 | } 101 | try { 102 | if (this.socket != null) socket.close(); 103 | } catch (IOException e) { 104 | e.printStackTrace(); 105 | } 106 | if(request != null) request.destory(); 107 | if(response != null) response.destory(); 108 | } 109 | 110 | public void setResponse(Response res) { 111 | this.response = res; 112 | } 113 | public Response getResponse() { 114 | return this.response; 115 | } 116 | public void setRequest(Request req) {this.request = req;} 117 | public Request getRequest() { 118 | if(this.request == null) 119 | this.request = new Request(); 120 | return this.request; 121 | } 122 | 123 | public String getRequestSource() { 124 | return this.request.getRequestSource(); 125 | } 126 | 127 | public byte[] getResponseSource() { 128 | return this.response.getBytes(); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/util/HTTPRequest.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.util; 2 | 3 | import com.zjh.tears.config.Config; 4 | import com.zjh.tears.exception.HTTPException; 5 | import com.zjh.tears.exception.NotFoundException; 6 | import com.zjh.tears.exception.ServerException; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.FileNotFoundException; 10 | import java.io.IOException; 11 | import java.io.InputStreamReader; 12 | import java.io.OutputStream; 13 | import java.net.HttpURLConnection; 14 | import java.net.MalformedURLException; 15 | import java.net.URL; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | /** 20 | * Created by zhangjiahao on 2017/2/17. 21 | */ 22 | public class HTTPRequest { 23 | 24 | private int status; 25 | private Map> responseHeader; 26 | private String body; 27 | private byte[] bodyByte; 28 | 29 | private HTTPRequest() { 30 | } 31 | 32 | private static HTTPRequest sendRequest(String method, String target, Map headers, byte[] data) throws HTTPException { 33 | HttpURLConnection conn = null; 34 | BufferedReader br = null; 35 | OutputStream os = null; 36 | HTTPRequest request; 37 | try { 38 | URL url = new URL(target); 39 | conn = (HttpURLConnection) url.openConnection(); 40 | conn.setDoInput(true); 41 | conn.setRequestMethod(method.toUpperCase()); 42 | 43 | if (headers != null) { 44 | for (Map.Entry entry : headers.entrySet()) { 45 | String key = entry.getKey(); 46 | String value = entry.getValue(); 47 | conn.setRequestProperty(key, value); 48 | } 49 | } 50 | 51 | if (method.toUpperCase().equals("POST")) { 52 | conn.setDoOutput(true); 53 | conn.setUseCaches(false); 54 | os = conn.getOutputStream(); 55 | if (data != null) { 56 | os.write(data); 57 | os.flush(); 58 | } 59 | } 60 | 61 | conn.connect(); 62 | request = new HTTPRequest(); 63 | 64 | br = new BufferedReader(new InputStreamReader(conn.getInputStream(), Config.DEFAULT_CHARSET)); 65 | String line; 66 | StringBuilder sb = new StringBuilder(); 67 | while ((line = br.readLine()) != null) { 68 | sb.append(line + "\n"); 69 | } 70 | 71 | request.status = conn.getResponseCode(); 72 | request.responseHeader = conn.getHeaderFields(); 73 | request.body = sb.toString(); 74 | request.bodyByte = sb.toString().getBytes(); 75 | 76 | return request; 77 | } catch (MalformedURLException e) { 78 | throw new ServerException(e); 79 | } catch(FileNotFoundException e) { 80 | throw new NotFoundException(e); 81 | } catch (IOException e) { 82 | throw new ServerException(e); 83 | } finally { 84 | HTTPRequest.closeBufferedReader(br); 85 | HTTPRequest.closeConn(conn); 86 | HTTPRequest.closeOutputStream(os); 87 | } 88 | } 89 | 90 | public static HTTPRequest get(String target, Map headers) throws HTTPException { 91 | return HTTPRequest.sendRequest("get", target, headers, null); 92 | } 93 | 94 | public static HTTPRequest get(String target) throws HTTPException { 95 | return HTTPRequest.get(target, null); 96 | } 97 | 98 | public static HTTPRequest post(String target, Map headers, byte[] data) throws HTTPException { 99 | return HTTPRequest.sendRequest("post", target, headers, data); 100 | } 101 | 102 | public static HTTPRequest post(String target, byte[] data) throws HTTPException { 103 | return HTTPRequest.post(target, null, data); 104 | } 105 | 106 | public static HTTPRequest post(String target) throws HTTPException { 107 | return HTTPRequest.post(target, null, null); 108 | } 109 | 110 | private static void closeConn(HttpURLConnection conn) { 111 | if (conn != null) { 112 | conn.disconnect(); 113 | } 114 | } 115 | 116 | private static void closeBufferedReader(BufferedReader br) { 117 | if (br != null) { 118 | try { 119 | br.close(); 120 | } catch (IOException e) { 121 | e.printStackTrace(); 122 | } 123 | } 124 | } 125 | 126 | private static void closeOutputStream(OutputStream os) { 127 | if (os != null) { 128 | try { 129 | os.close(); 130 | } catch (IOException e) { 131 | e.printStackTrace(); 132 | } 133 | } 134 | } 135 | 136 | public Map> getResponseHeader() { 137 | return responseHeader; 138 | } 139 | 140 | public String getBody() { 141 | return body; 142 | } 143 | 144 | public byte[] getBodyByte() { 145 | return bodyByte; 146 | } 147 | 148 | public int getStatus() { 149 | return status; 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /src/main/java/com/zjh/tears/config/Config.java: -------------------------------------------------------------------------------- 1 | package com.zjh.tears.config; 2 | 3 | import com.zjh.tears.filter.socket.SocketFilter; 4 | import com.zjh.tears.filter.socket.HeaderSocketFilter; 5 | import com.zjh.tears.handler.HeaderHTTPHandler; 6 | import com.zjh.tears.handler.HTTPHandler; 7 | import com.zjh.tears.listener.socket.HeaderSocketListener; 8 | import com.zjh.tears.listener.socket.SocketListener; 9 | import com.zjh.tears.model.ReverseProxy; 10 | import com.zjh.tears.util.ClassParse; 11 | import com.zjh.tears.util.Util; 12 | import org.apache.log4j.Logger; 13 | import org.json.JSONArray; 14 | import org.json.JSONObject; 15 | 16 | import java.io.IOException; 17 | import java.nio.file.Files; 18 | import java.util.ArrayList; 19 | import java.util.HashMap; 20 | import java.util.HashSet; 21 | import java.util.List; 22 | import java.util.Map; 23 | import java.io.File; 24 | import java.util.Set; 25 | import java.util.regex.Pattern; 26 | import java.util.stream.Collectors; 27 | import java.util.stream.Stream; 28 | 29 | /** 30 | * Created by zhangjiahao on 2017/2/8. 31 | */ 32 | public final class Config { 33 | 34 | public static Integer PORT; 35 | public static Integer THREAD_POOL_SIZE; 36 | public static Integer TIMEOUT; 37 | public static SocketFilter SOCKET_FILTER_HEADER = new HeaderSocketFilter(); 38 | public static SocketListener SOCKET_LISTENER_HEADER = new HeaderSocketListener(); 39 | public static HTTPHandler HTTP_HANDLER_HEADER = new HeaderHTTPHandler(); 40 | 41 | public static String SERVER_NAME; 42 | 43 | public static boolean REVERSE_PROXY_USAGE = false; 44 | 45 | public static List DEFAULT_INDEX = new ArrayList<>(); 46 | public static Map ERR_PAGES = new HashMap<>(); 47 | public static Map STATIC_FILE_STRATEGYS = new HashMap<>(); 48 | 49 | public static Map REVERSE_PROXY_MAPPING = new HashMap<>(); 50 | 51 | public static String STATIC_ROOT_FILE; 52 | 53 | public static String DEFAULT_CHARSET; 54 | 55 | public static boolean ACCEPT_CONFIG_USAGE = false; 56 | public static Set ACCEPT_FILE = new HashSet<>(); 57 | public static Set EXCEPT_FILE = new HashSet<>(); 58 | 59 | private static Logger logger = Logger.getLogger(Config.class); 60 | 61 | static { 62 | logger.debug("Load Config File..."); 63 | File configFile = new File(Config.class.getClassLoader().getResource("config.json").getFile()); 64 | try { 65 | long startTime = System.currentTimeMillis(); 66 | logger.debug("\n _____ _____ ___ _____ _____ \n" + 67 | "|_ _| | ____| / | | _ \\ / ___/ \n" + 68 | " | | | |__ / /| | | |_| | | |___ \n" + 69 | " | | | __| / / | | | _ / \\___ \\ \n" + 70 | " | | | |___ / / | | | | \\ \\ ___| | \n" + 71 | " |_| |_____| /_/ |_| |_| \\_\\ /_____/ - zjh\n"); 72 | JSONObject config = new JSONObject(Files.readAllLines(configFile.toPath()).stream().collect(Collectors.joining())); 73 | logger.debug("Load Config File SUCCESS!"); 74 | logger.debug("Setting..."); 75 | JSONObject serverConfig = config.getJSONObject("serverConfig"); 76 | JSONObject pageConfig = config.getJSONObject("pageConfig"); 77 | JSONObject acceptConfig = config.getJSONObject("acceptConfig"); 78 | Config.INIT_SERVER_CONFIG(serverConfig); 79 | Config.INIT_PAGE_CONFIG(pageConfig); 80 | Config.INIT_ACCEPT_CONFIG(acceptConfig); 81 | long endTime = System.currentTimeMillis(); 82 | logger.debug("Config SUCCESS! at " + (endTime - startTime) + "ms"); 83 | logger.debug("Server start..."); 84 | } catch (IOException e) { 85 | e.printStackTrace(); 86 | logger.error(Util.stackTraceToString(e)); 87 | System.exit(-1); 88 | } catch (Exception e) { 89 | e.printStackTrace(); 90 | logger.error(Util.stackTraceToString(e)); 91 | System.exit(-1); 92 | } 93 | } 94 | 95 | private static final void INIT_SERVER_CONFIG(final JSONObject serverConfig) throws Exception { 96 | Config.PORT = serverConfig.getInt("port"); 97 | logger.debug("server port - " + Config.PORT); 98 | 99 | Config.THREAD_POOL_SIZE = serverConfig.getInt("threadPoolSize"); 100 | logger.debug("server thread pool size - " + Config.THREAD_POOL_SIZE); 101 | 102 | logger.debug("server shutdown port - 8008"); 103 | 104 | logger.debug("server shutdown secret - shutdown"); 105 | 106 | Config.TIMEOUT = serverConfig.getInt("timeout"); 107 | 108 | logger.debug("server socket timeout - " + Config.TIMEOUT); 109 | 110 | Config.SERVER_NAME = serverConfig.getString("serverName"); 111 | logger.debug("server name - " + Config.SERVER_NAME); 112 | 113 | Config.STATIC_ROOT_FILE = serverConfig.getString("staticRootFile"); 114 | logger.debug("static root file - " + Config.STATIC_ROOT_FILE); 115 | 116 | Config.DEFAULT_CHARSET = serverConfig.getString("defaultCharset"); 117 | logger.debug("default charset - " + Config.DEFAULT_CHARSET); 118 | 119 | SocketFilter headerFilter = Config.SOCKET_FILTER_HEADER; 120 | logger.debug("Init Socket Filter..."); 121 | 122 | for (Object obj : serverConfig.getJSONArray("socketFilter")) { 123 | String className = (String) obj; 124 | logger.debug("init class - " + className); 125 | SocketFilter filter = (SocketFilter) ClassParse.getInstance().getObject(className); 126 | headerFilter.setNext(filter); 127 | headerFilter = filter; 128 | } 129 | logger.debug("Init Socket Filter SUCCESS!"); 130 | 131 | SocketListener headerListener = Config.SOCKET_LISTENER_HEADER; 132 | logger.debug("Init Socket Listener..."); 133 | 134 | for (Object obj : serverConfig.getJSONArray("socketListener")) { 135 | String className = (String) obj; 136 | logger.debug("init class - " + className); 137 | SocketListener listener = (SocketListener) ClassParse.getInstance().getObject(className); 138 | headerListener.setNext(listener); 139 | headerListener = listener; 140 | } 141 | logger.debug("Init Socket Listener SUCCESS!"); 142 | 143 | HTTPHandler headerHandler = Config.HTTP_HANDLER_HEADER; 144 | logger.debug("Init HTTP Handler..."); 145 | 146 | for (Object obj : serverConfig.getJSONArray("httpHandler")) { 147 | String className = (String) obj; 148 | logger.debug("init class - " + className); 149 | HTTPHandler handler = (HTTPHandler) ClassParse.getInstance().getObject(className); 150 | headerHandler.setNext(handler); 151 | headerHandler = handler; 152 | } 153 | logger.debug("Init HTTP Handler SUCCESS!"); 154 | 155 | logger.debug("Init Static File Strategy..."); 156 | for (Object obj : serverConfig.getJSONArray("staticFileStrategy")) { 157 | JSONObject strategy = (JSONObject) obj; 158 | String strategyName = strategy.getString("name"); 159 | String strategyClass = strategy.getString("strategy"); 160 | logger.debug("init strategy - " + strategyName + ", class - " + strategyClass); 161 | STATIC_FILE_STRATEGYS.put(strategyName, strategyClass); 162 | } 163 | logger.debug("Init Static File Strategy SUCCESS!"); 164 | 165 | JSONObject reverseProxy = serverConfig.getJSONObject("reverseProxy"); 166 | Config.REVERSE_PROXY_USAGE = reverseProxy.optBoolean("usage"); 167 | logger.debug("reverse proxy usage? - " + Config.REVERSE_PROXY_USAGE ); 168 | if(Config.REVERSE_PROXY_USAGE) { 169 | for(Object obj : reverseProxy.getJSONArray("proxy")) { 170 | JSONObject reverseJSON = (JSONObject) obj; 171 | String rule = "/" + reverseJSON.getString("rule").replaceAll("\\.", "\\\\.").replaceAll("\\*", "((?![\\\\./]).)*"); 172 | String target = reverseJSON.getString("target"); 173 | ReverseProxy reverseProxyObj = new ReverseProxy(rule, target); 174 | for(Object reverseProxyCookieObj : reverseJSON.getJSONArray("reverseProxyCookie")) { 175 | JSONObject cookieMapping = (JSONObject) reverseProxyCookieObj; 176 | String from = cookieMapping.getString("from"); 177 | String to = cookieMapping.getString("to"); 178 | reverseProxyObj.addCookieMapping(from, to); 179 | } 180 | Config.REVERSE_PROXY_MAPPING.put(rule, reverseProxyObj); 181 | } 182 | } 183 | } 184 | 185 | private static final void INIT_PAGE_CONFIG(final JSONObject pageConfig) { 186 | for (Object obj : pageConfig.getJSONArray("defaultIndex")) { 187 | String index = (String) obj; 188 | Config.DEFAULT_INDEX.add(index); 189 | } 190 | logger.debug("set default Index - " + Config.DEFAULT_INDEX); 191 | for (Object obj : pageConfig.getJSONArray("errorPages")) { 192 | JSONObject errorPage = (JSONObject) obj; 193 | int code = errorPage.getInt("code"); 194 | String page = errorPage.getString("page"); 195 | Config.ERR_PAGES.put(code, page); 196 | logger.debug("set error Page - code " + code + ", page " + page); 197 | } 198 | } 199 | 200 | private static final void INIT_ACCEPT_CONFIG(final JSONObject acceptConfig) { 201 | Config.ACCEPT_CONFIG_USAGE = acceptConfig.optBoolean("usage", false); 202 | logger.debug("accept config usage? - " + Config.ACCEPT_CONFIG_USAGE); 203 | if (Config.ACCEPT_CONFIG_USAGE) { 204 | JSONArray accept = acceptConfig.getJSONArray("accept"); 205 | logger.debug("load accept path pattern - " + accept); 206 | JSONArray except = acceptConfig.getJSONArray("except"); 207 | logger.debug("load except path pattern - " + except); 208 | Config.initFile(true, accept); 209 | Config.initFile(false, except); 210 | } 211 | } 212 | 213 | private static void initFile(boolean isAccept, JSONArray fileList) { 214 | for (Object obj : fileList) { 215 | String file = (String) obj; 216 | String path = file.replaceAll("\\.", "\\\\.").replaceAll("\\*", "((?![\\\\./]).)*"); 217 | if(isAccept) { 218 | Config.ACCEPT_FILE.add(Pattern.compile(path)); 219 | } else{ 220 | Config.EXCEPT_FILE.add(Pattern.compile(path)); 221 | } 222 | } 223 | } 224 | 225 | } 226 | --------------------------------------------------------------------------------