├── 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 | 
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 |
--------------------------------------------------------------------------------