├── webapps
├── ROOT
│ ├── b
│ │ ├── a.html
│ │ └── 1.jpg
│ ├── index.html
│ ├── WEB-INF
│ │ └── web.xml
│ └── form.html
└── test
│ ├── b
│ ├── a.html
│ └── 1.jpg
│ ├── index.htm
│ └── WEB-INF
│ └── web.xml
├── lib
├── jspc_all.jar
├── junit-4.9.jar
├── jsoup-1.12.1.jar
├── log4j-1.2.17.jar
├── servlet-api.jar
└── hutool-all-4.3.1.jar
├── static
├── HttpRequestProcess.png
└── TinyWebServerArchitecture.png
├── .idea
├── vcs.xml
├── .gitignore
├── misc.xml
├── modules.xml
├── inspectionProfiles
│ └── Project_Default.xml
└── uiDesigner.xml
├── src
└── com
│ └── yzb
│ ├── exception
│ ├── LifecycleException.java
│ ├── URLMismatchedExpection.java
│ └── ParseHttpRequestException.java
│ ├── TinyWebServer.java
│ ├── core
│ ├── Host.java
│ ├── Context.java
│ ├── StandardServletConfig.java
│ ├── Engine.java
│ ├── StandardContainer.java
│ ├── StandardConnector.java
│ ├── StandardService.java
│ ├── StandardServletContext.java
│ └── StandardServer.java
│ ├── servlets
│ ├── TestServlet3.java
│ ├── TestServlet.java
│ ├── TestServlet2.java
│ ├── SessionServlet1.java
│ ├── SessionServlet2.java
│ ├── SessionServlet3.java
│ ├── HelloServlet2.java
│ ├── HelloServlet.java
│ └── DefaultServlet.java
│ ├── common
│ ├── CommonThreadPool.java
│ ├── Container.java
│ ├── ServerContext.java
│ ├── Service.java
│ ├── Server.java
│ ├── Connector.java
│ ├── Response.java
│ ├── Request.java
│ └── Lifecycle.java
│ ├── classcloader
│ └── WebappClassLoader.java
│ ├── http
│ ├── ApplicationRequestDispatcher.java
│ ├── HttpProcessor.java
│ ├── StandardSession.java
│ ├── Dispatcher.java
│ ├── SessionManager.java
│ ├── HttpConnector.java
│ ├── HttpContant.java
│ ├── HttpResponse.java
│ ├── ApplicationContext.java
│ └── HttpRequest.java
│ └── util
│ └── ServerXMLParser.java
├── .gitignore
├── conf
├── log4j.properties
├── context.xml
└── server.xml
├── README.md
├── TinyWebServer.iml
└── test
└── com
└── yzb
├── ServerTest.java
├── Client.java
└── CommonTest.java
/webapps/ROOT/b/a.html:
--------------------------------------------------------------------------------
1 | this is a.html under b dir
--------------------------------------------------------------------------------
/webapps/test/b/a.html:
--------------------------------------------------------------------------------
1 | this is a.html under b dir
--------------------------------------------------------------------------------
/lib/jspc_all.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangzebin001/TinyWebServer/HEAD/lib/jspc_all.jar
--------------------------------------------------------------------------------
/lib/junit-4.9.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangzebin001/TinyWebServer/HEAD/lib/junit-4.9.jar
--------------------------------------------------------------------------------
/lib/jsoup-1.12.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangzebin001/TinyWebServer/HEAD/lib/jsoup-1.12.1.jar
--------------------------------------------------------------------------------
/lib/log4j-1.2.17.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangzebin001/TinyWebServer/HEAD/lib/log4j-1.2.17.jar
--------------------------------------------------------------------------------
/lib/servlet-api.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangzebin001/TinyWebServer/HEAD/lib/servlet-api.jar
--------------------------------------------------------------------------------
/webapps/ROOT/b/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangzebin001/TinyWebServer/HEAD/webapps/ROOT/b/1.jpg
--------------------------------------------------------------------------------
/webapps/test/b/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangzebin001/TinyWebServer/HEAD/webapps/test/b/1.jpg
--------------------------------------------------------------------------------
/lib/hutool-all-4.3.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangzebin001/TinyWebServer/HEAD/lib/hutool-all-4.3.1.jar
--------------------------------------------------------------------------------
/static/HttpRequestProcess.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangzebin001/TinyWebServer/HEAD/static/HttpRequestProcess.png
--------------------------------------------------------------------------------
/static/TinyWebServerArchitecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangzebin001/TinyWebServer/HEAD/static/TinyWebServerArchitecture.png
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/webapps/ROOT/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Title
6 |
7 |
8 | This is ROOT index.html.
9 |
10 |
--------------------------------------------------------------------------------
/webapps/test/index.htm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Title
6 |
7 |
8 | This is test index.html.
9 |
10 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Datasource local storage ignored files
5 | /../../../../:\yzb\TinyWebServer\.idea/dataSources/
6 | /dataSources.local.xml
7 | # Editor-based HTTP Client requests
8 | /httpRequests/
9 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/com/yzb/exception/LifecycleException.java:
--------------------------------------------------------------------------------
1 | package com.yzb.exception;
2 |
3 | /**
4 | * @Description
5 | * @Date 2021/1/28 下午10:00
6 | * @Creater BeckoninGshy
7 | */
8 | public class LifecycleException extends Exception {
9 |
10 | public LifecycleException() {
11 | }
12 |
13 | public LifecycleException(String message) {
14 | super(message);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/com/yzb/exception/URLMismatchedExpection.java:
--------------------------------------------------------------------------------
1 | package com.yzb.exception;
2 |
3 | /**
4 | * @Description
5 | * @Date 2021/2/5 下午10:01
6 | * @Creater BeckoninGshy
7 | */
8 | public class URLMismatchedExpection extends Exception{
9 |
10 | public URLMismatchedExpection() {
11 | }
12 |
13 | public URLMismatchedExpection(String message) {
14 | super(message);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.nar
17 | *.ear
18 | *.zip
19 | *.tar.gz
20 | *.rar
21 | *.pdf
22 |
23 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
24 | hs_err_pid*
25 |
26 | webapps/*.pdf
27 |
28 | logs/
29 |
30 | out/
31 |
32 |
--------------------------------------------------------------------------------
/src/com/yzb/exception/ParseHttpRequestException.java:
--------------------------------------------------------------------------------
1 | package com.yzb.exception;
2 |
3 | /**
4 | * @description: 解析HTTP请求异常类
5 | * @author: BeckoninGshy
6 | * @create: 2021/1/24 16:33
7 | */
8 | public class ParseHttpRequestException extends Exception {
9 | private static final long serialVersionUID = 54839657436794L;
10 |
11 | public ParseHttpRequestException() {
12 | }
13 |
14 | public ParseHttpRequestException(String message) {
15 | super(message);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/com/yzb/TinyWebServer.java:
--------------------------------------------------------------------------------
1 | package com.yzb;
2 |
3 | import com.yzb.common.Server;
4 | import com.yzb.exception.LifecycleException;
5 | import com.yzb.util.ServerXMLParser;
6 |
7 | /**
8 | * @Description TinyWebServer app starter
9 | * @Date 2021/1/26 下午9:20
10 | * @Creater BeckoninGshy
11 | */
12 | public class TinyWebServer {
13 | public static void main(String[] args) throws LifecycleException {
14 | Server server = ServerXMLParser.getServerWithAutoPack();
15 | server.init();
16 | server.start();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/conf/log4j.properties:
--------------------------------------------------------------------------------
1 | log4j.rootLogger = info,stdout,R
2 |
3 | log4j.appender.stdout = org.apache.log4j.ConsoleAppender
4 | log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
5 | log4j.appender.stdout.layout.ConversionPattern = %d{MM dd,yyyy HH:mm:ss a}%c%M%n%-5p:%m%n
6 |
7 | log4j.appender.R = org.apache.log4j.DailyRollingFileAppender
8 | log4j.appender.R.File = logs/log
9 | log4j.appender.R.DatePattern = ','yyyy-MM-dd'.log'
10 | log4j.appender.R.layout = org.apache.log4j.PatternLayout
11 | log4j.appender.R.layout.ConversionPattern = %d{MM dd,yyyy HH:mm:ss a} %c %M%n%-5p: %m%n
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TinyWebServer
2 | 一个小型的web服务器
3 |
4 | ### 项目架构图
5 | 
6 |
7 | ### 请求执行流程图
8 | 
9 |
10 | ### feature:
11 |
12 | - Multi-application deployment
13 | - Java servlet API support
14 | - Application-Context manager
15 | - Url Dispatcher
16 | - HTTP/1.1 protocol support
17 | - HTTP request and response
18 | - Cookie and Session
19 | - Request Dispatcher and Response SendRedirect
20 | - 404 and 500 code status
21 | - MIME type and welcome file support
22 | - Log management
23 |
24 | > default character set is UTF-8
25 |
--------------------------------------------------------------------------------
/src/com/yzb/core/Host.java:
--------------------------------------------------------------------------------
1 | package com.yzb.core;
2 |
3 | import com.yzb.common.ServerContext;
4 |
5 | import java.io.File;
6 |
7 | /**
8 | * @Description
9 | * @Date 2021/1/30 下午9:36
10 | * @Creater BeckoninGshy
11 | */
12 | public class Host extends StandardContainer {
13 |
14 | private String appBase;
15 |
16 | public String getAppBase() {
17 | return appBase;
18 | }
19 |
20 | public void setAppBase(String appBase) {
21 | if(appBase.startsWith(File.separator))
22 | this.appBase = ServerContext.serverBasePath + appBase;
23 | else this.appBase = ServerContext.serverBasePath + File.separator + appBase;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/com/yzb/servlets/TestServlet3.java:
--------------------------------------------------------------------------------
1 | package com.yzb.servlets;
2 |
3 | import javax.servlet.ServletException;
4 | import javax.servlet.http.HttpServletRequest;
5 | import javax.servlet.http.HttpServletResponse;
6 | import java.io.IOException;
7 |
8 | /**
9 | * @Description
10 | * @Date 2021/2/6 下午8:58
11 | * @Creater BeckoninGshy
12 | */
13 | public class TestServlet3 extends HelloServlet{
14 | @Override
15 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
16 | resp.getWriter().println("this is test3 response."); // should be useless.
17 | req.getRequestDispatcher("/test1").forward(req, resp);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/com/yzb/common/CommonThreadPool.java:
--------------------------------------------------------------------------------
1 | package com.yzb.common;
2 |
3 | import java.util.concurrent.LinkedBlockingDeque;
4 | import java.util.concurrent.ThreadPoolExecutor;
5 | import java.util.concurrent.TimeUnit;
6 |
7 | /**
8 | * @Description handle Connector Thread Pool
9 | * @Date 2021/1/27 下午12:07
10 | * @Creater BeckoninGshy
11 | */
12 | public class CommonThreadPool {
13 | private static ThreadPoolExecutor tpe = new ThreadPoolExecutor(20,100,60,TimeUnit.SECONDS,new LinkedBlockingDeque<>());
14 | public static void run(Runnable runnable){
15 | tpe.execute(runnable);
16 | }
17 |
18 | public static void shutdown() {
19 | tpe.shutdown();
20 | while(!tpe.isShutdown());
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/com/yzb/servlets/TestServlet.java:
--------------------------------------------------------------------------------
1 | package com.yzb.servlets;
2 |
3 | import javax.servlet.ServletException;
4 | import javax.servlet.http.HttpServlet;
5 | import javax.servlet.http.HttpServletRequest;
6 | import javax.servlet.http.HttpServletResponse;
7 | import java.io.IOException;
8 | import java.io.PrintWriter;
9 |
10 | /**
11 | * @Description
12 | * @Date 2021/2/1 下午9:35
13 | * @Creater BeckoninGshy
14 | */
15 | public class TestServlet extends HttpServlet {
16 | @Override
17 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
18 | PrintWriter writer = resp.getWriter();
19 | writer.println("this is TestSerlvet response");
20 | writer.close();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/com/yzb/servlets/TestServlet2.java:
--------------------------------------------------------------------------------
1 | package com.yzb.servlets;
2 |
3 | import javax.servlet.ServletException;
4 | import javax.servlet.http.HttpServletRequest;
5 | import javax.servlet.http.HttpServletResponse;
6 | import java.io.IOException;
7 | import java.io.PrintWriter;
8 |
9 | /**
10 | * @Description
11 | * @Date 2021/2/6 下午8:58
12 | * @Creater BeckoninGshy
13 | */
14 | public class TestServlet2 extends HelloServlet{
15 | @Override
16 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
17 | // req.getRequestDispatcher("/test1").forward(req, resp);
18 | req.getServletContext().getContext("/")
19 | .getRequestDispatcher("/b/a.html").forward(req,resp);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/com/yzb/common/Container.java:
--------------------------------------------------------------------------------
1 | package com.yzb.common;
2 |
3 | /**
4 | * @Description
5 | * @Date 2021/1/28 下午10:18
6 | * @Creater BeckoninGshy
7 | */
8 | public interface Container extends Lifecycle {
9 |
10 | public String getName();
11 |
12 | public void setName(String name);
13 |
14 | public Service getService();
15 |
16 | public void setService(Service service);
17 |
18 | public Container getParent();
19 |
20 | public void setParent(Container parent);
21 |
22 | public ClassLoader getParentClassLoader();
23 |
24 | public void setParentClassLoader(ClassLoader classLoader);
25 |
26 | public void addChild(Container container);
27 |
28 | public Container findChild(String container);
29 |
30 | public Container[] findChildren();
31 |
32 | public void removeChild(Container container);
33 |
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/com/yzb/common/ServerContext.java:
--------------------------------------------------------------------------------
1 | package com.yzb.common;
2 |
3 | import java.io.File;
4 |
5 | /**
6 | * @Description
7 | * @Date 2021/1/30 下午10:01
8 | * @Creater BeckoninGshy
9 | */
10 | public class ServerContext {
11 |
12 | public static final String serverName = "TinyWebServer";
13 |
14 | public static final String version = "0.0.1";
15 |
16 | public static final String serverBasePath = System.getProperty("user.dir").toString();
17 |
18 | public static final String serverConfigDir = serverBasePath + File.separator + "conf";
19 |
20 | public static final String serverXMLPath = serverConfigDir + File.separator + "server.xml";
21 |
22 | public static final String webXMLPath = serverConfigDir+ File.separator + "web.xml";
23 |
24 | public static final String servletLoadClassDir = serverConfigDir+ File.separator + "servlets";
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/com/yzb/common/Service.java:
--------------------------------------------------------------------------------
1 | package com.yzb.common;
2 |
3 |
4 | /**
5 | * @Description
6 | * @Date 2021/1/28 下午10:07
7 | * @Creater BeckoninGshy
8 | */
9 | public interface Service extends Lifecycle {
10 |
11 |
12 | public Container getContainer();
13 |
14 | public void setContainer(Container container);
15 |
16 | public String getInfo();
17 |
18 | public String getName();
19 |
20 | public void setName(String name);
21 |
22 | public Server getServer();
23 |
24 | public void setServer(Server server);
25 |
26 | public void addConnector(Connector connector);
27 |
28 | public Connector findConnector(String name);
29 |
30 | public String[] getConnectorNames();
31 |
32 | public Connector[] findConnectors();
33 |
34 | public void removeConnector(Connector connector);
35 |
36 | public ClassLoader getParentClassLoader();
37 |
38 | public void setParentClassLoader(ClassLoader classLoader);
39 | }
40 |
--------------------------------------------------------------------------------
/src/com/yzb/servlets/SessionServlet1.java:
--------------------------------------------------------------------------------
1 | package com.yzb.servlets;
2 |
3 | import javax.servlet.ServletException;
4 | import javax.servlet.http.HttpServlet;
5 | import javax.servlet.http.HttpServletRequest;
6 | import javax.servlet.http.HttpServletResponse;
7 | import javax.servlet.http.HttpSession;
8 | import java.io.IOException;
9 | import java.io.PrintWriter;
10 |
11 | /**
12 | * @Description
13 | * @Date 2021/2/10 下午10:30
14 | * @Creater BeckoninGshy
15 | */
16 | public class SessionServlet1 extends HttpServlet {
17 | @Override
18 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
19 | HttpSession session = req.getSession();
20 | session.setAttribute("username", "lisi");
21 | System.out.println(session.isNew());
22 | PrintWriter writer = resp.getWriter();
23 | writer.println("this is SessionServlet1 response");
24 | writer.close();
25 | }
26 | }
--------------------------------------------------------------------------------
/src/com/yzb/common/Server.java:
--------------------------------------------------------------------------------
1 | package com.yzb.common;
2 |
3 | /**
4 | * @Description
5 | * @Date 2021/1/28 下午10:31
6 | * @Creater BeckoninGshy
7 | */
8 | public interface Server extends Lifecycle {
9 |
10 | public String getName();
11 |
12 | public void setName(String name);
13 |
14 | public int getPort();
15 |
16 | public void setPort(int port);
17 |
18 | public String getAddress();
19 |
20 | public void setAddress(String address);
21 |
22 | public String getShutdown();
23 |
24 | public void setShutdown(String shutdown);
25 |
26 | public void addService(Service service);
27 |
28 | public Service findService(String service);
29 |
30 | public Service[] findServices();
31 |
32 | public String[] getServiceNames();
33 |
34 | public void removeService(Service service);
35 |
36 | public ClassLoader getParentClassLoader();
37 |
38 | public void setParentClassLoader(ClassLoader classLoader);
39 |
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/com/yzb/servlets/SessionServlet2.java:
--------------------------------------------------------------------------------
1 | package com.yzb.servlets;
2 |
3 | import javax.servlet.ServletException;
4 | import javax.servlet.http.HttpServlet;
5 | import javax.servlet.http.HttpServletRequest;
6 | import javax.servlet.http.HttpServletResponse;
7 | import javax.servlet.http.HttpSession;
8 | import java.io.IOException;
9 | import java.io.PrintWriter;
10 |
11 | /**
12 | * @Description
13 | * @Date 2021/2/10 下午10:30
14 | * @Creater BeckoninGshy
15 | */
16 | public class SessionServlet2 extends HttpServlet {
17 | @Override
18 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
19 | HttpSession session = req.getSession();
20 | String username = (String) session.getAttribute("username");
21 | System.out.println(username);
22 | PrintWriter writer = resp.getWriter();
23 | writer.println("this is SessionServlet2 response");
24 | writer.close();
25 | }
26 | }
--------------------------------------------------------------------------------
/src/com/yzb/servlets/SessionServlet3.java:
--------------------------------------------------------------------------------
1 | package com.yzb.servlets;
2 |
3 | import javax.servlet.ServletException;
4 | import javax.servlet.http.HttpServlet;
5 | import javax.servlet.http.HttpServletRequest;
6 | import javax.servlet.http.HttpServletResponse;
7 | import javax.servlet.http.HttpSession;
8 | import java.io.IOException;
9 | import java.io.PrintWriter;
10 |
11 | /**
12 | * @Description
13 | * @Date 2021/2/10 下午10:30
14 | * @Creater BeckoninGshy
15 | */
16 | public class SessionServlet3 extends HttpServlet {
17 | @Override
18 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
19 | HttpSession session = req.getSession();
20 | session.removeAttribute("username");
21 | // session.invalidate();
22 | System.out.println(session.isNew());
23 | PrintWriter writer = resp.getWriter();
24 | writer.println("this is SessionServlet3 response");
25 | writer.close();
26 | }
27 | }
--------------------------------------------------------------------------------
/TinyWebServer.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/webapps/ROOT/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | HelloServlet
5 | com.yzb.servlets.HelloServlet
6 |
7 | username
8 | zhangsan
9 |
10 |
11 | password
12 | 123
13 |
14 | 1
15 |
16 |
17 |
18 | HelloServlet2
19 | com.yzb.servlets.HelloServlet2
20 |
21 |
22 |
23 | HelloServlet
24 | /hello
25 |
26 |
27 |
28 | HelloServlet2
29 | /hello2.do
30 |
31 |
--------------------------------------------------------------------------------
/src/com/yzb/core/Context.java:
--------------------------------------------------------------------------------
1 | package com.yzb.core;
2 |
3 | import java.io.File;
4 | import java.util.Map;
5 | import java.util.concurrent.ConcurrentHashMap;
6 |
7 | /**
8 | * @Description The Context element represents a web application under webapps folder.
9 | * @Date 2021/1/30 下午9:37
10 | * @Creater BeckoninGshy
11 | */
12 | public class Context extends StandardContainer {
13 |
14 | private Map attributes = new ConcurrentHashMap<>();
15 | private String path;
16 |
17 | public Context(){}
18 |
19 | public String getPath() {
20 | return path;
21 | }
22 |
23 | public void setPath(String path) {
24 | this.path = path;
25 | }
26 |
27 | public String getRealPath(){
28 | if(getPath().equals("/")) return ((Host)parent).getAppBase() + File.separator + "ROOT";
29 | return ((Host)parent).getAppBase() + getPath();
30 | }
31 |
32 | public Object getAttribute(String key) {
33 | return attributes.get(key);
34 | }
35 |
36 | public void setAttribute(String key, Object value){
37 | attributes.put(key, value);
38 | }
39 |
40 | public void removeAttribute(String key){
41 | attributes.remove(key);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/webapps/ROOT/form.html:
--------------------------------------------------------------------------------
1 |
2 | tiny web standardServer
3 |
4 |
23 |
24 |
--------------------------------------------------------------------------------
/src/com/yzb/common/Connector.java:
--------------------------------------------------------------------------------
1 | package com.yzb.common;
2 |
3 | import java.net.Socket;
4 |
5 | /**
6 | * @Description
7 | * @Date 2021/1/28 下午10:17
8 | * @Creater BeckoninGshy
9 | */
10 | public interface Connector extends Lifecycle {
11 |
12 | public String getName();
13 |
14 | public void setName(String name);
15 |
16 | public String getProperty(String property);
17 |
18 | public boolean setProperty(String key, String value);
19 |
20 | public Service getService();
21 |
22 | public void setService(Service service);
23 |
24 | public int getPort();
25 |
26 | public void setPort(int port);
27 |
28 | public String getProtocol();
29 |
30 | public void setProtocol(String protocol);
31 |
32 | public void setConnectionTimeout(long timeout);
33 |
34 | public long getConnectionTimeout();
35 |
36 | public String getScheme();
37 |
38 | public void setScheme(String scheme);
39 |
40 | public String getURIEncoding();
41 |
42 | public void setURIEncoding(String uriEncoding);
43 |
44 | public boolean getUseBodyEncodingForURI();
45 |
46 | public void setUseBodyEncodingForURI(boolean isUse);
47 |
48 | public Request createRequest(Socket socket, Connector connector);
49 |
50 | public Response createResponse(Socket socket);
51 |
52 | public Server getServer();
53 |
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/com/yzb/core/StandardServletConfig.java:
--------------------------------------------------------------------------------
1 | package com.yzb.core;
2 |
3 | import javax.servlet.ServletConfig;
4 | import javax.servlet.ServletContext;
5 | import java.util.Collections;
6 | import java.util.Enumeration;
7 | import java.util.HashMap;
8 | import java.util.Map;
9 |
10 | /**
11 | * @Description The implementation class of ServletConfig
12 | * @Date 2021/2/4 下午9:31
13 | * @Creater BeckoninGshy
14 | */
15 | public class StandardServletConfig implements ServletConfig {
16 | private Map initParameters;
17 | private ServletContext servletContext;
18 | private String servletName;
19 |
20 | public StandardServletConfig(ServletContext servletContext,String servletName,Map initParameters){
21 | this.servletContext = servletContext;
22 | this.initParameters = initParameters;
23 | this.servletName = servletName;
24 | }
25 |
26 | @Override
27 | public String getServletName() {
28 | return servletName;
29 | }
30 |
31 | @Override
32 | public ServletContext getServletContext() {
33 | return servletContext;
34 | }
35 |
36 | @Override
37 | public String getInitParameter(String s) {
38 | return initParameters.get(s);
39 | }
40 |
41 | @Override
42 | public Enumeration getInitParameterNames() {
43 | return Collections.enumeration(initParameters.keySet());
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/com/yzb/servlets/HelloServlet2.java:
--------------------------------------------------------------------------------
1 | package com.yzb.servlets;
2 |
3 | import javax.servlet.ServletConfig;
4 | import javax.servlet.ServletException;
5 | import javax.servlet.http.HttpServlet;
6 | import javax.servlet.http.HttpServletRequest;
7 | import javax.servlet.http.HttpServletResponse;
8 | import java.io.IOException;
9 | import java.io.PrintWriter;
10 | import java.util.Enumeration;
11 |
12 | /**
13 | * @Description
14 | * @Date 2021/2/2 下午5:41
15 | * @Creater BeckoninGshy
16 | */
17 | public class HelloServlet2 extends HttpServlet {
18 |
19 | @Override
20 | public void destroy() {
21 | System.out.println("hello servlet2 is destroyed");
22 | }
23 |
24 | @Override
25 | public void init(ServletConfig sc) throws ServletException {
26 | System.out.println(sc.getServletName());
27 | Enumeration initParameterNames = sc.getInitParameterNames();
28 | while(initParameterNames.hasMoreElements()){
29 | String s = initParameterNames.nextElement();
30 | System.out.println("init name:" + s + ", value:" + sc.getInitParameter(s));
31 | }
32 | }
33 |
34 | @Override
35 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
36 | PrintWriter writer = resp.getWriter();
37 | writer.println("this is HelloSerlvet2 response");
38 | writer.close();
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/com/yzb/classcloader/WebappClassLoader.java:
--------------------------------------------------------------------------------
1 | package com.yzb.classcloader;
2 |
3 | import cn.hutool.core.io.FileUtil;
4 |
5 | import java.io.File;
6 | import java.io.IOException;
7 | import java.net.URL;
8 | import java.net.URLClassLoader;
9 | import java.util.List;
10 |
11 | /**
12 | * @Description
13 | * @Date 2021/2/1 下午8:57
14 | * @Creater BeckoninGshy
15 | */
16 | public class WebappClassLoader extends URLClassLoader {
17 |
18 | public WebappClassLoader(String docBase, ClassLoader commonClassLoader){
19 | super(new URL[]{},commonClassLoader);
20 |
21 | try {
22 | File webinfFolder = new File(docBase,"WEB-INF");
23 | File classFoler = new File(webinfFolder,"classes");
24 | File libFolder = new File(webinfFolder,"lib");
25 | URL url;
26 | url = new URL("file:"+classFoler.getAbsolutePath()+"/");
27 | this.addURL(url);
28 | List jarFile = FileUtil.loopFiles(libFolder);
29 | for (File file:jarFile){
30 | url = new URL("file:"+file.getAbsolutePath());
31 | this.addURL(url);
32 | }
33 | }catch (Exception e){
34 | e.printStackTrace();
35 | }
36 | }
37 |
38 | public void stop(){
39 | try {
40 | close();
41 | }catch (IOException e){
42 | e.printStackTrace();
43 | }
44 | }
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/src/com/yzb/servlets/HelloServlet.java:
--------------------------------------------------------------------------------
1 | package com.yzb.servlets;
2 |
3 | import javax.servlet.ServletConfig;
4 | import javax.servlet.ServletException;
5 | import javax.servlet.http.HttpServlet;
6 | import javax.servlet.http.HttpServletRequest;
7 | import javax.servlet.http.HttpServletResponse;
8 | import java.io.IOException;
9 | import java.io.PrintWriter;
10 | import java.util.Enumeration;
11 |
12 | /**
13 | * @Description
14 | * @Date 2021/2/2 下午5:41
15 | * @Creater BeckoninGshy
16 | */
17 | public class HelloServlet extends HttpServlet {
18 |
19 | @Override
20 | public void destroy() {
21 | System.out.println("hello servlet is destroyed");
22 | }
23 |
24 | @Override
25 | public void init(ServletConfig sc) throws ServletException {
26 | System.out.println(sc.getServletName());
27 | Enumeration initParameterNames = sc.getInitParameterNames();
28 | while(initParameterNames.hasMoreElements()){
29 | String s = initParameterNames.nextElement();
30 | System.out.println("init name:" + s + ", value:" + sc.getInitParameter(s));
31 | }
32 | }
33 |
34 | @Override
35 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
36 | // int i = 1/0;
37 | PrintWriter writer = resp.getWriter();
38 | writer.println("this is HelloSerlvet response");
39 | writer.close();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/com/yzb/servlets/DefaultServlet.java:
--------------------------------------------------------------------------------
1 | package com.yzb.servlets;
2 |
3 | import javax.servlet.ServletConfig;
4 | import javax.servlet.ServletException;
5 | import javax.servlet.http.HttpServlet;
6 | import javax.servlet.http.HttpServletRequest;
7 | import javax.servlet.http.HttpServletResponse;
8 | import java.io.IOException;
9 | import java.io.PrintWriter;
10 | import java.util.Enumeration;
11 |
12 | /**
13 | * @Description
14 | * @Date 2021/2/2 下午8:40
15 | * @Creater BeckoninGshy
16 | */
17 | public class DefaultServlet extends HttpServlet {
18 |
19 | @Override
20 | public void init(ServletConfig sc) throws ServletException {
21 | System.out.println(sc.getServletName());
22 | Enumeration initParameterNames = sc.getInitParameterNames();
23 | while(initParameterNames.hasMoreElements()){
24 | String s = initParameterNames.nextElement();
25 | System.out.println("init name:" + s + ", value:" + sc.getInitParameter(s));
26 | }
27 | }
28 |
29 | @Override
30 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
31 | PrintWriter writer = resp.getWriter();
32 | writer.println("this is default page");
33 | writer.close();
34 | }
35 |
36 | @Override
37 | protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
38 | doGet(req, resp);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/conf/context.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
22 | WEB-INF/web.xml
23 |
24 |
25 |
28 |
29 |
31 |
34 |
35 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/conf/server.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 |
15 |
16 |
17 |
20 |
23 |
24 |
30 |
31 |
32 |
37 |
38 |
39 |
40 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/src/com/yzb/http/ApplicationRequestDispatcher.java:
--------------------------------------------------------------------------------
1 | package com.yzb.http;
2 |
3 | import javax.servlet.RequestDispatcher;
4 | import javax.servlet.ServletException;
5 | import javax.servlet.ServletRequest;
6 | import javax.servlet.ServletResponse;
7 | import java.io.IOException;
8 |
9 | /**
10 | * @Description Defines an object that receives requests from the client and sends them to any resource (relative )
11 | * @Date 2021/2/6 下午7:57
12 | * @Creater BeckoninGshy
13 | */
14 | public class ApplicationRequestDispatcher implements RequestDispatcher {
15 |
16 | private String uri;
17 | private ApplicationContext appContext = null;
18 |
19 | public ApplicationRequestDispatcher(String uri){
20 | if(uri.startsWith("/"))
21 | this.uri = uri;
22 | else this.uri = "/" + uri;
23 | }
24 |
25 | public ApplicationRequestDispatcher(String uri, ApplicationContext appContext){
26 | this(uri);
27 | this.appContext = appContext;
28 | }
29 |
30 | @Override
31 | public void forward(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
32 | HttpRequest req= (HttpRequest) servletRequest;
33 | HttpResponse resp = (HttpResponse) servletResponse;
34 | HttpProcessor hp = new HttpProcessor();
35 | if(appContext != null) req.setServletContext(appContext);
36 | req.setForwardURI(uri);
37 | req.setForwarding();
38 | resp.resetBuffer();
39 | hp.execute(req.getSocket(), req, resp);
40 | req.setForwarded();
41 | }
42 |
43 | @Override
44 | public void include(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
45 |
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/test/com/yzb/ServerTest.java:
--------------------------------------------------------------------------------
1 | package com.yzb;
2 |
3 | import cn.hutool.core.util.RandomUtil;
4 | import com.yzb.common.Service;
5 | import com.yzb.core.StandardServer;
6 | import com.yzb.core.StandardService;
7 | import org.junit.*;
8 |
9 | /**
10 | * @Description
11 | * @Date 2021/1/29 下午1:37
12 | * @Creater BeckoninGshy
13 | */
14 | public class ServerTest {
15 | public static StandardServer ss = null;
16 | public static Service[] services = new Service[0];
17 |
18 | @BeforeClass
19 | public static void beforeClass(){
20 | ss = StandardServer.getServerInstance();
21 | services = new Service[100];
22 | }
23 |
24 |
25 |
26 | @Test
27 | public void addServicesTest(){
28 | for(int i = 0; i < 100; i++){
29 | services[i] = new StandardService();
30 | ss.addService(services[i]);
31 | }
32 | Assert.assertEquals(100,ss.findServices().length);
33 | }
34 |
35 |
36 | @Test
37 | public void findServicesTest(){
38 | Assert.assertEquals(100,ss.findServices().length);
39 | }
40 |
41 |
42 | @Test
43 | public void removeServicesTest(){
44 | Assert.assertEquals(100,ss.findServices().length);
45 | for(int i = 0; i < 100; i++){
46 | ss.removeService(services[i]);
47 | }
48 | Assert.assertEquals(0,ss.findServices().length);
49 | }
50 |
51 | @Test
52 | public void removeServicesRandomTest(){
53 | for(int i = 0; i < 100; i++){
54 | services[i] = new StandardService();
55 | ss.addService(services[i]);
56 | }
57 | int[] cnt = new int[100];
58 | for(int i = 0; i < 100; i++){
59 | int t = RandomUtil.randomInt(0,100);
60 | while(cnt[t] != 0) t = RandomUtil.randomInt(0,100);
61 | cnt[t] = 1;
62 | ss.removeService(services[t]);
63 | }
64 | Assert.assertEquals(0,ss.findServices().length);
65 | }
66 |
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/webapps/test/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | TestServlet
5 | com.yzb.servlets.TestServlet
6 |
7 |
8 | TestServlet2
9 | com.yzb.servlets.TestServlet2
10 |
11 |
12 | TestServlet3
13 | com.yzb.servlets.TestServlet3
14 |
15 |
16 | SessionServlet1
17 | com.yzb.servlets.SessionServlet1
18 |
19 |
20 | SessionServlet2
21 | com.yzb.servlets.SessionServlet2
22 |
23 |
24 | SessionServlet3
25 | com.yzb.servlets.SessionServlet3
26 |
27 |
28 |
29 | TestServlet
30 | /test1
31 |
32 |
33 | TestServlet2
34 | /test2
35 |
36 |
37 | TestServlet3
38 | /test3
39 |
40 |
41 | SessionServlet1
42 | /session1
43 |
44 |
45 | SessionServlet2
46 | /session2
47 |
48 |
49 | SessionServlet3
50 | /session3
51 |
52 |
--------------------------------------------------------------------------------
/src/com/yzb/http/HttpProcessor.java:
--------------------------------------------------------------------------------
1 | package com.yzb.http;
2 |
3 |
4 | import cn.hutool.core.util.StrUtil;
5 | import cn.hutool.log.LogFactory;
6 | import com.yzb.classcloader.WebappClassLoader;
7 | import com.yzb.exception.URLMismatchedExpection;
8 |
9 | import javax.servlet.Servlet;
10 | import javax.servlet.ServletException;
11 | import javax.servlet.http.HttpSession;
12 | import java.io.IOException;
13 | import java.io.PrintWriter;
14 | import java.net.Socket;
15 |
16 | /**
17 | * @Description processor one http request
18 | * @Date 2021/1/26 下午10:00
19 | * @Creater BeckoninGshy
20 | */
21 | public class HttpProcessor {
22 |
23 | public void execute(Socket socket, HttpRequest request, HttpResponse response){
24 | try{
25 | LogFactory.get().info("receiving from {}, request: {}", request.getRemoteAddr(), request.getRequestURI());
26 |
27 | // prepare session for every connector
28 | prepareSession(request, response);
29 | ApplicationContext appContext = (ApplicationContext) request.getServletContext();
30 | new Dispatcher().dispatch(request.getRequestURI(), appContext, request, response);
31 |
32 | } catch (URLMismatchedExpection e) {
33 | //404
34 | handle404(request.getRequestURI(), response, e.getMessage());
35 | } catch (Exception e) {
36 | //500
37 | handle500(request.getRequestURI(), response, e.getMessage());
38 | } finally {
39 | try {
40 | if(!socket.isClosed())
41 | socket.close();
42 | } catch (IOException e) {
43 | }
44 | }
45 | }
46 |
47 | private void prepareSession(HttpRequest request, HttpResponse response){
48 | String jSessionId = request.getJSessionIdFromCookie();
49 | HttpSession session = SessionManager.getSession(jSessionId, request, response);
50 | request.setSession(session);
51 | }
52 |
53 | private void handle404(String url, HttpResponse resp, String message) {
54 | LogFactory.get().info("{} is 404", url);
55 | try {
56 | resp.sendError(404, StrUtil.format(HttpContant.textFormat_404, url, message));
57 | } catch (IOException e) {
58 | }
59 | }
60 |
61 | private void handle500(String url, HttpResponse resp, String message) {
62 | LogFactory.get().info("{} is 500", url);
63 | try {
64 | resp.sendError(500, StrUtil.format(HttpContant.textFormat_500, url, message));
65 | } catch (IOException e) {
66 | }
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/src/com/yzb/http/StandardSession.java:
--------------------------------------------------------------------------------
1 | package com.yzb.http;
2 |
3 | import javax.servlet.ServletContext;
4 | import javax.servlet.http.HttpSession;
5 | import javax.servlet.http.HttpSessionContext;
6 | import java.util.Collections;
7 | import java.util.Enumeration;
8 | import java.util.Map;
9 | import java.util.concurrent.ConcurrentHashMap;
10 |
11 | /**
12 | * @Description class Session
13 | * @Date 2021/2/10 下午4:26
14 | * @Creater BeckoninGshy
15 | */
16 | public class StandardSession implements HttpSession {
17 |
18 | private ServletContext servletContext;
19 | private Map attritubes;
20 | private String ID;
21 | private long creationTime;
22 | private long lastAccessedTime;
23 | private int maxInactiveInterval;
24 |
25 |
26 | public StandardSession(String sessionId, ServletContext servletContext){
27 | this.ID = sessionId;
28 | this.servletContext = servletContext;
29 | this.creationTime = System.currentTimeMillis();
30 | this.lastAccessedTime = creationTime;
31 | this.attritubes = new ConcurrentHashMap<>();
32 | }
33 |
34 |
35 | @Override
36 | public long getCreationTime() {
37 | return creationTime;
38 | }
39 |
40 | @Override
41 | public String getId() {
42 | return ID;
43 | }
44 |
45 | @Override
46 | public long getLastAccessedTime() {
47 | return lastAccessedTime;
48 | }
49 |
50 | public void setLastAccessedTime(long lastAccessedTime){
51 | this.lastAccessedTime = lastAccessedTime;
52 | }
53 |
54 | @Override
55 | public ServletContext getServletContext() {
56 | return servletContext;
57 | }
58 |
59 | @Override
60 | public void setMaxInactiveInterval(int i) {
61 | this.maxInactiveInterval = i;
62 | }
63 |
64 | @Override
65 | public int getMaxInactiveInterval() {
66 | return maxInactiveInterval;
67 | }
68 |
69 | @Override
70 | public HttpSessionContext getSessionContext() {
71 | return null;
72 | }
73 |
74 |
75 | @Override
76 | public Object getAttribute(String s) {
77 | return attritubes.get(s);
78 | }
79 |
80 | @Override
81 | public Object getValue(String s) {
82 | return null;
83 | }
84 |
85 | @Override
86 | public Enumeration getAttributeNames() {
87 | return Collections.enumeration(attritubes.keySet());
88 | }
89 |
90 | @Override
91 | public String[] getValueNames() {
92 | return new String[0];
93 | }
94 |
95 | @Override
96 | public void setAttribute(String s, Object o) {
97 | attritubes.put(s,o);
98 | }
99 |
100 | @Override
101 | public void putValue(String s, Object o) {
102 |
103 | }
104 |
105 | @Override
106 | public void removeAttribute(String s) {
107 | attritubes.remove(s);
108 | }
109 |
110 | @Override
111 | public void removeValue(String s) {
112 |
113 | }
114 |
115 | @Override
116 | public void invalidate() {
117 | attritubes.clear();
118 | long now = System.currentTimeMillis();
119 | setMaxInactiveInterval(0);
120 | }
121 |
122 | @Override
123 | public boolean isNew() {
124 | return creationTime == lastAccessedTime;
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/com/yzb/core/Engine.java:
--------------------------------------------------------------------------------
1 | package com.yzb.core;
2 |
3 | import com.yzb.common.Container;
4 | import com.yzb.common.ServerContext;
5 | import com.yzb.exception.LifecycleException;
6 | import com.yzb.http.ApplicationContext;
7 |
8 | import java.io.File;
9 | import java.io.FileNotFoundException;
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | /**
14 | * @Description
15 | * @Date 2021/1/30 下午9:34
16 | * @Creater BeckoninGshy
17 | */
18 | public class Engine extends StandardContainer {
19 | private String defaultHostName;
20 |
21 | public String getDefaultHostName() {
22 | return defaultHostName;
23 | }
24 |
25 | public Host getDefaultHost(){
26 | if(defaultHostName == null) return null;
27 | for(Container child : children){
28 | if(child instanceof Host && defaultHostName.equals(child.getName())){
29 | return (Host)child;
30 | }
31 | }
32 | return null;
33 | }
34 |
35 | public void setDefaultHostName(String defaultHostName) {
36 | this.defaultHostName = defaultHostName;
37 |
38 | }
39 |
40 | @Override
41 | public void init() throws LifecycleException {
42 | ApplicationContext defaultContext = null;
43 | try {
44 | defaultContext = loadDefaultContext();
45 | } catch (FileNotFoundException e) {
46 | e.printStackTrace();
47 | stop();
48 | }
49 | loadUserContext(defaultContext);
50 | super.init();
51 | }
52 |
53 | private ApplicationContext loadDefaultContext() throws FileNotFoundException {
54 | String contextsDirectory = ServerContext.webXMLPath;
55 | File contextFile = new File(contextsDirectory);
56 | if(!contextFile.exists())
57 | throw new FileNotFoundException("There is no web.xml in conf dir.");
58 |
59 | ApplicationContext defaultContext = new ApplicationContext(ServerContext.servletLoadClassDir,true);
60 | defaultContext.setPath("");
61 | defaultContext.setParent(getDefaultHost());
62 | getDefaultHost().addChild(defaultContext);
63 | defaultContext.setService(getService());
64 | return defaultContext;
65 | }
66 |
67 |
68 | private void loadUserContext(ApplicationContext defaultContext){
69 | String contextsDirectory = getDefaultHost().getAppBase();
70 | File contexts = new File(contextsDirectory);
71 | if(!contexts.exists() || !contexts.isDirectory()) return;
72 | File[] files = contexts.listFiles();
73 | List contextList = new ArrayList<>();
74 | for(File file : files){
75 | if(file.isDirectory()){
76 | ApplicationContext t = new ApplicationContext(file.getAbsolutePath(),false);
77 | if("ROOT".equals(file.getName())){
78 | t.setPath("/");
79 | }else{
80 | t.setPath("/" + file.getName());
81 | }
82 | t.setName(file.getName());
83 | contextList.add(t);
84 | }
85 | }
86 | for(ApplicationContext servletContext : contextList){
87 | servletContext.setDefaultContext(defaultContext);
88 | servletContext.setParent(getDefaultHost());
89 | getDefaultHost().addChild(servletContext);
90 | servletContext.setService(getService());
91 | }
92 | }
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/src/com/yzb/http/Dispatcher.java:
--------------------------------------------------------------------------------
1 | package com.yzb.http;
2 |
3 | import cn.hutool.core.util.StrUtil;
4 | import com.yzb.classcloader.WebappClassLoader;
5 | import com.yzb.exception.URLMismatchedExpection;
6 |
7 | import javax.servlet.Servlet;
8 | import javax.servlet.ServletException;
9 | import javax.servlet.ServletOutputStream;
10 | import java.io.*;
11 |
12 | /**
13 | * @Description Parse the url and pass it to the appropriate component, such as a servlet or file
14 | * @Date 2021/2/2 下午5:15
15 | * @Creater BeckoninGshy
16 | */
17 | public class Dispatcher {
18 |
19 |
20 | public void dispatch(String url, ApplicationContext appContext, HttpRequest req, HttpResponse resp) throws ClassNotFoundException, IOException, ServletException, IllegalAccessException, InstantiationException, URLMismatchedExpection {
21 | // Reduce context path
22 | if(!appContext.getPath().equals("/"))
23 | url = url.substring(appContext.getPath().length());
24 |
25 | // is app root url, match welcome-file-list config;
26 | if(url.length() == 0 || url.equals("/")){ // like "/test" or "/test/" --> "" test is an app dir
27 | InputStream is = appContext.getWelcomFile();
28 | if(is != null){
29 | writeFileToResponse(is, resp);
30 | return;
31 | }
32 | }
33 |
34 | // is servlet url
35 | if(url.startsWith("/")){
36 | // app context --> default context
37 | ApplicationContext curContext = appContext;
38 | if(appContext.getServletURLToClass(url) == null) {
39 | curContext = curContext.getDefaultContext();
40 | }
41 |
42 | if(curContext.getServletURLToClass(url) != null) {
43 | WebappClassLoader webappClassLoader = curContext.getWebappClassLoader();
44 | Class> clazz = webappClassLoader.loadClass(curContext.getServletURLToClass(url));
45 | //get instance servlet from context
46 | Servlet servlet = curContext.getServlet(clazz);
47 | //execute service method (auto call method like doGet,doPost etc)process request
48 | servlet.service(req,resp);
49 | return;
50 | }
51 | }
52 | // is mime type?
53 | if (StrUtil.contains(url, '.')){
54 | String ext = StrUtil.subAfter(url, '.', false);
55 | String type = appContext.getMimeType(ext);
56 | InputStream is = appContext.getResourceAsStream(url);
57 | if(type != null && is != null) {
58 | //get resource succeeded and server can handle this mimetype
59 | resp.setContentType(type);
60 | //default charset: utf-8
61 | if(type.startsWith("text")) {
62 | resp.setCharacterEncoding("utf-8");
63 | }
64 | writeFileToResponse(is, resp);
65 | return;
66 | }
67 | }
68 | // is mismatched.
69 | throw new URLMismatchedExpection(url);
70 | }
71 |
72 |
73 | private void writeFileToResponse(InputStream is, HttpResponse resp) throws IOException {
74 | ServletOutputStream outputStream = resp.getOutputStream();
75 | byte[] buffer = new byte[1024];
76 | int len = 0;
77 | while((len = is.read(buffer,0,buffer.length)) != -1){
78 | outputStream.write(buffer,0,len);
79 | }
80 | outputStream.close();
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/com/yzb/core/StandardContainer.java:
--------------------------------------------------------------------------------
1 | package com.yzb.core;
2 |
3 | import com.yzb.common.Container;
4 | import com.yzb.common.Service;
5 | import com.yzb.exception.LifecycleException;
6 |
7 | import java.io.FileNotFoundException;
8 |
9 | /**
10 | * @Description
11 | * @Date 2021/1/29 下午1:17
12 | * @Creater BeckoninGshy
13 | */
14 | public class StandardContainer implements Container {
15 | protected String name;
16 | protected Service service;
17 | protected Container parent;
18 | private ClassLoader parentClassLoader;
19 | protected Container[] children = new Container[0];
20 |
21 | @Override
22 | public String getName() {
23 | return name;
24 | }
25 |
26 | @Override
27 | public void setName(String name) {
28 | this.name = name;
29 | }
30 |
31 | @Override
32 | public Service getService() {
33 | return service;
34 | }
35 |
36 | @Override
37 | public void setService(Service service) {
38 | this.service = service;
39 | }
40 |
41 | @Override
42 | public Container getParent() {
43 | return parent;
44 | }
45 |
46 | @Override
47 | public void setParent(Container parent) {
48 | this.parent = parent;
49 | }
50 |
51 | @Override
52 | public ClassLoader getParentClassLoader() {
53 | return parentClassLoader;
54 | }
55 |
56 | @Override
57 | public void setParentClassLoader(ClassLoader classLoader) {
58 | this.parentClassLoader = classLoader;
59 | }
60 |
61 | @Override
62 | public void addChild(Container container) {
63 | int len = children.length;
64 | Container[] newChildren = new Container[len+1];
65 | System.arraycopy(children,0,newChildren,0,len);
66 | newChildren[len] = container;
67 | children = newChildren;
68 | }
69 |
70 | @Override
71 | public Container findChild(String container) {
72 | for (Container value : children) {
73 | if (value.getName().equals(name)) return value;
74 | }
75 | return null;
76 | }
77 |
78 | @Override
79 | public Container[] findChildren() {
80 | return children;
81 | }
82 |
83 | @Override
84 | public void removeChild(Container container) {
85 | int idx = 0, len = children.length;
86 | while(idx < len && children[idx] != container) idx++;
87 | if(idx == len) return;
88 | try {
89 | children[idx].stop();
90 | } catch (LifecycleException e) {
91 | //noting to do.
92 | }
93 | Container[] newContainers = new Container[len-1];
94 | System.arraycopy(children,0,newContainers,0,idx);
95 | System.arraycopy(children,idx+1,newContainers,idx,len-idx-1);
96 | children = newContainers;
97 | }
98 |
99 | @Override
100 | public void init() throws LifecycleException {
101 | for(Container child : children){
102 | child.init();
103 | }
104 | }
105 |
106 | @Override
107 | public void start() throws LifecycleException {
108 | for(Container child : children){
109 | child.start();
110 | }
111 | }
112 |
113 | @Override
114 | public void stop() throws LifecycleException {
115 | for(Container child : children){
116 | child.stop();
117 | }
118 | }
119 |
120 | @Override
121 | public void destroy() throws LifecycleException {
122 | for(Container child : children){
123 | child.destroy();
124 | }
125 | }
126 |
127 | @Override
128 | public String getState() {
129 | return null;
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/src/com/yzb/core/StandardConnector.java:
--------------------------------------------------------------------------------
1 | package com.yzb.core;
2 |
3 | import com.yzb.common.*;
4 | import com.yzb.exception.LifecycleException;
5 |
6 | import java.net.ServerSocket;
7 | import java.net.Socket;
8 | import java.util.Map;
9 |
10 | /**
11 | * @Description
12 | * @Date 2021/1/29 下午1:17
13 | * @Creater BeckoninGshy
14 | */
15 | public class StandardConnector implements Connector {
16 |
17 | protected String name;
18 | protected Map properties;
19 | protected Service service;
20 | protected int port = -1;
21 | protected String protocol;
22 | private String scheme;
23 | protected long connectionTimeout = 0;
24 | private String uriEncoding;
25 | private boolean useBodyEncodingForURI;
26 | protected static ServerSocket serverSocket;
27 |
28 | @Override
29 | public String getName() {
30 | return name;
31 | }
32 |
33 | @Override
34 | public void setName(String name) {
35 | this.name = name;
36 | }
37 |
38 | @Override
39 | public String getProperty(String property) {
40 | return properties.get(property);
41 | }
42 |
43 | @Override
44 | public boolean setProperty(String key, String value) {
45 | properties.put(key,value);
46 | return true;
47 | }
48 |
49 | @Override
50 | public Service getService() {
51 | return service;
52 | }
53 |
54 | @Override
55 | public void setService(Service service) {
56 | this.service = service;
57 | }
58 |
59 | @Override
60 | public int getPort() {
61 | return port;
62 | }
63 |
64 | @Override
65 | public void setPort(int port) {
66 | this.port = port;
67 | }
68 |
69 | @Override
70 | public String getProtocol() {
71 | return protocol;
72 | }
73 |
74 | @Override
75 | public void setProtocol(String protocol) {
76 | this.protocol = protocol;
77 | }
78 |
79 | @Override
80 | public void setConnectionTimeout(long timeout) {
81 | this.connectionTimeout = timeout;
82 | }
83 |
84 | @Override
85 | public long getConnectionTimeout() {
86 | return connectionTimeout;
87 | }
88 |
89 | @Override
90 | public String getScheme() {
91 | return scheme;
92 | }
93 |
94 | @Override
95 | public void setScheme(String scheme) {
96 | this.scheme = scheme;
97 | }
98 |
99 | @Override
100 | public String getURIEncoding() {
101 | return uriEncoding;
102 | }
103 |
104 | @Override
105 | public void setURIEncoding(String uriEncoding) {
106 | this.uriEncoding = uriEncoding;
107 | }
108 |
109 | @Override
110 | public boolean getUseBodyEncodingForURI() {
111 | return useBodyEncodingForURI;
112 | }
113 |
114 | @Override
115 | public void setUseBodyEncodingForURI(boolean isUse) {
116 | this.useBodyEncodingForURI = isUse;
117 | }
118 |
119 | @Override
120 | public Request createRequest(Socket socket, Connector connector) {
121 | return null;
122 | }
123 |
124 | @Override
125 | public Response createResponse(Socket socket) {
126 | return null;
127 | }
128 |
129 | @Override
130 | public Server getServer() {
131 | return service.getServer();
132 | }
133 |
134 | @Override
135 | public void init() throws LifecycleException {
136 |
137 |
138 | }
139 |
140 | @Override
141 | public void start() throws LifecycleException {
142 |
143 | }
144 |
145 | @Override
146 | public void stop() throws LifecycleException {
147 |
148 | }
149 |
150 | @Override
151 | public void destroy() throws LifecycleException {
152 |
153 | }
154 |
155 | @Override
156 | public String getState() {
157 | return null;
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/test/com/yzb/Client.java:
--------------------------------------------------------------------------------
1 | package com.yzb;
2 |
3 | import cn.hutool.core.lang.Console;
4 | import cn.hutool.http.Header;
5 | import cn.hutool.http.HttpRequest;
6 | import org.junit.Before;
7 | import org.junit.Test;
8 |
9 | import java.io.ByteArrayOutputStream;
10 | import java.io.IOException;
11 | import java.io.InputStream;
12 | import java.io.OutputStream;
13 | import java.net.InetSocketAddress;
14 | import java.net.Socket;
15 | import java.nio.charset.StandardCharsets;
16 | import java.util.HashMap;
17 | import java.util.Map;
18 |
19 | public class Client {
20 | Client client;
21 | @Before
22 | public void before(){
23 | client = new Client();
24 | }
25 |
26 | @Test
27 | public void RequestByUtil(){
28 | String body = HttpRequest.get("127.0.0.1:9090")
29 | .header(Header.USER_AGENT, "yzb's client")
30 | .header(Header.REFERER, "http://www.baidu.com")
31 | .body("username=张三")
32 | .body("password=123")
33 | .execute().body();
34 | Console.log(body);
35 | }
36 |
37 | @Test
38 | public void RequestPostByUtil(){
39 | Map formData = new HashMap<>();
40 | formData.put("username","zhangsan");
41 | formData.put("password","password");
42 | String body = HttpRequest.post("127.0.0.1:9090")
43 | .header(Header.USER_AGENT, "yzb's client")
44 | .form(formData)//表单内容
45 | .timeout(20000)//超时,毫秒
46 | .execute().body();
47 | Console.log(body);
48 | }
49 |
50 | @Test
51 | public void RequestBySocket() throws IOException {
52 | InetSocketAddress serverAddress = new InetSocketAddress("127.0.0.1", 9090);
53 | Socket socket = new Socket();
54 | socket.connect(serverAddress);
55 | OutputStream outputStream = socket.getOutputStream();
56 | String sendMessage = "hello Server!";
57 | outputStream.write(sendMessage.getBytes(StandardCharsets.UTF_8));
58 |
59 | InputStream inputStream = socket.getInputStream();
60 | int bufferSize = 1024;
61 | byte[] buffer = new byte[1024];
62 | ByteArrayOutputStream baso = new ByteArrayOutputStream();
63 | while(true){
64 | int len = inputStream.read(buffer);
65 | if(len == -1) break;
66 | baso.write(buffer, 0 , len);
67 | if(len != bufferSize){
68 | break;
69 | }
70 | }
71 | System.out.println(baso.toString());
72 | baso.close();
73 | socket.close();
74 | }
75 |
76 | @Test
77 | public void RequestPostBySocket() throws IOException {
78 | InetSocketAddress serverAddress = new InetSocketAddress("127.0.0.1", 9090);
79 | Socket socket = new Socket();
80 | socket.connect(serverAddress);
81 | OutputStream outputStream = socket.getOutputStream();
82 | String sendMessage = "POST / HTTP/1.1\r\n"
83 | + "User-Agent: yzb's client\r\n"
84 | + "Content-Length: 26\r\n"
85 | + "\r\n"
86 | + "username=张三&password=123";
87 | outputStream.write(sendMessage.getBytes(StandardCharsets.UTF_8));
88 |
89 | InputStream inputStream = socket.getInputStream();
90 | int bufferSize = 1024;
91 | byte[] buffer = new byte[1024];
92 | ByteArrayOutputStream baso = new ByteArrayOutputStream();
93 | while(true){
94 | int len = inputStream.read(buffer);
95 | if(len == -1) break;
96 | baso.write(buffer, 0 , len);
97 | if(len != bufferSize){
98 | break;
99 | }
100 | }
101 | System.out.println(baso.toString());
102 | baso.close();
103 | socket.close();
104 | }
105 |
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/src/com/yzb/common/Response.java:
--------------------------------------------------------------------------------
1 | package com.yzb.common;
2 |
3 | import javax.servlet.ServletOutputStream;
4 | import javax.servlet.http.Cookie;
5 | import javax.servlet.http.HttpServletResponse;
6 | import java.io.IOException;
7 | import java.io.PrintWriter;
8 | import java.util.Collection;
9 | import java.util.Locale;
10 |
11 | public class Response implements HttpServletResponse {
12 | @Override
13 | public void addCookie(Cookie cookie) {
14 |
15 | }
16 |
17 | @Override
18 | public boolean containsHeader(String s) {
19 | return false;
20 | }
21 |
22 | @Override
23 | public String encodeURL(String s) {
24 | return null;
25 | }
26 |
27 | @Override
28 | public String encodeRedirectURL(String s) {
29 | return null;
30 | }
31 |
32 | @Override
33 | public String encodeUrl(String s) {
34 | return null;
35 | }
36 |
37 | @Override
38 | public String encodeRedirectUrl(String s) {
39 | return null;
40 | }
41 |
42 | @Override
43 | public void sendError(int i, String s) throws IOException {
44 |
45 | }
46 |
47 | @Override
48 | public void sendError(int i) throws IOException {
49 |
50 | }
51 |
52 | @Override
53 | public void sendRedirect(String s) throws IOException {
54 |
55 | }
56 |
57 | @Override
58 | public void setDateHeader(String s, long l) {
59 |
60 | }
61 |
62 | @Override
63 | public void addDateHeader(String s, long l) {
64 |
65 | }
66 |
67 | @Override
68 | public void setHeader(String s, String s1) {
69 |
70 | }
71 |
72 | @Override
73 | public void addHeader(String s, String s1) {
74 |
75 | }
76 |
77 | @Override
78 | public void setIntHeader(String s, int i) {
79 |
80 | }
81 |
82 | @Override
83 | public void addIntHeader(String s, int i) {
84 |
85 | }
86 |
87 | @Override
88 | public void setStatus(int i) {
89 |
90 | }
91 |
92 | @Override
93 | public void setStatus(int i, String s) {
94 |
95 | }
96 |
97 | @Override
98 | public int getStatus() {
99 | return 0;
100 | }
101 |
102 | @Override
103 | public String getHeader(String s) {
104 | return null;
105 | }
106 |
107 | @Override
108 | public Collection getHeaders(String s) {
109 | return null;
110 | }
111 |
112 | @Override
113 | public Collection getHeaderNames() {
114 | return null;
115 | }
116 |
117 | @Override
118 | public String getCharacterEncoding() {
119 | return null;
120 | }
121 |
122 | @Override
123 | public String getContentType() {
124 | return null;
125 | }
126 |
127 | @Override
128 | public ServletOutputStream getOutputStream() throws IOException {
129 | return null;
130 | }
131 |
132 | @Override
133 | public PrintWriter getWriter() throws IOException {
134 | return null;
135 | }
136 |
137 | @Override
138 | public void setCharacterEncoding(String s) {
139 |
140 | }
141 |
142 | @Override
143 | public void setContentLength(int i) {
144 |
145 | }
146 |
147 | @Override
148 | public void setContentType(String s) {
149 |
150 | }
151 |
152 | @Override
153 | public void setBufferSize(int i) {
154 |
155 | }
156 |
157 | @Override
158 | public int getBufferSize() {
159 | return 0;
160 | }
161 |
162 | @Override
163 | public void flushBuffer() throws IOException {
164 |
165 | }
166 |
167 | @Override
168 | public void resetBuffer() {
169 |
170 | }
171 |
172 | @Override
173 | public boolean isCommitted() {
174 | return false;
175 | }
176 |
177 | @Override
178 | public void reset() {
179 |
180 | }
181 |
182 | @Override
183 | public void setLocale(Locale locale) {
184 |
185 | }
186 |
187 | @Override
188 | public Locale getLocale() {
189 | return null;
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/src/com/yzb/core/StandardService.java:
--------------------------------------------------------------------------------
1 | package com.yzb.core;
2 |
3 | import cn.hutool.log.LogFactory;
4 | import com.yzb.common.Connector;
5 | import com.yzb.common.Container;
6 | import com.yzb.common.Server;
7 | import com.yzb.common.Service;
8 | import com.yzb.exception.LifecycleException;
9 |
10 | /**
11 | * @Description
12 | * @Date 2021/1/29 下午1:15
13 | * @Creater BeckoninGshy
14 | */
15 | public class StandardService implements Service {
16 |
17 | private Container container;
18 | private String name = "StandardService";
19 | private Server server;
20 | private Connector[] connectors = new Connector[0];
21 | private final Object connectorsLock = new Object();
22 | private ClassLoader parentClassLoader;
23 |
24 | @Override
25 | public void init() throws LifecycleException {
26 | container.init();
27 | synchronized (connectorsLock) {
28 | for(Connector connector : connectors){
29 | connector.init();
30 | }
31 | }
32 | }
33 |
34 | @Override
35 | public void start() throws LifecycleException {
36 | container.start();
37 |
38 | synchronized (connectorsLock) {
39 | for(Connector connector : connectors){
40 | connector.start();
41 | }
42 | }
43 |
44 | LogFactory.get().info("stared service {}", getName());
45 | }
46 |
47 | @Override
48 | public void stop() throws LifecycleException {
49 | for(Connector connector : connectors){
50 | connector.stop();
51 | }
52 | container.stop();
53 | LogFactory.get().info("stopped service {}", getName());
54 | }
55 |
56 | @Override
57 | public void destroy() throws LifecycleException {
58 | for(Connector connector : connectors){
59 | connector.destroy();
60 | }
61 | container.destroy();
62 | LogFactory.get().info("destroyed service {}", getName());
63 | }
64 |
65 | @Override
66 | public String getState() {
67 | return null;
68 | }
69 |
70 | @Override
71 | public Container getContainer() {
72 | return container;
73 | }
74 |
75 | @Override
76 | public void setContainer(Container container) {
77 | this.container = container;
78 | }
79 |
80 | @Override
81 | public String getInfo() {
82 | return null;
83 | }
84 |
85 | @Override
86 | public String getName() {
87 | return name;
88 | }
89 |
90 | @Override
91 | public void setName(String name) {
92 | this.name = name;
93 | }
94 |
95 | @Override
96 | public Server getServer() {
97 | return server;
98 | }
99 |
100 | @Override
101 | public void setServer(Server server) {
102 | this.server = server;
103 | }
104 |
105 | @Override
106 | public void addConnector(Connector connector) {
107 | synchronized (connectorsLock) {
108 | int len = connectors.length;
109 | Connector[] newConnectors = new Connector[len+1];
110 | System.arraycopy(connectors,0,newConnectors,0,len);
111 | newConnectors[len] = connector;
112 | connectors = newConnectors;
113 | }
114 | }
115 |
116 | @Override
117 | public Connector findConnector(String name) {
118 | int len = connectors.length;
119 | for (Connector value : connectors) {
120 | if (value.getName().equals(name)) return value;
121 | }
122 | return null;
123 | }
124 |
125 | @Override
126 | public String[] getConnectorNames() {
127 | int len = connectors.length;
128 | String[] names = new String[len];
129 | for(int i = 0; i < len; i++){
130 | names[i] = connectors[i].getName();
131 | }
132 | return names;
133 | }
134 |
135 | @Override
136 | public Connector[] findConnectors() {
137 | return connectors;
138 | }
139 |
140 | @Override
141 | public void removeConnector(Connector connector) {
142 | synchronized (connectorsLock) {
143 |
144 | int idx = 0, len = connectors.length;
145 | while(idx < len && connectors[idx] != connector) idx++;
146 | if(idx == len) return;
147 | try {
148 | connectors[idx].stop();
149 | } catch (LifecycleException e) {
150 | //noting to do.
151 | }
152 |
153 | Connector[] newConnectors = new Connector[len-1];
154 | System.arraycopy(connectors,0,newConnectors,0,idx);
155 | System.arraycopy(connectors,idx+1,newConnectors,idx,len-idx-1);
156 | connectors = newConnectors;
157 | }
158 | }
159 |
160 | @Override
161 | public ClassLoader getParentClassLoader() {
162 | return parentClassLoader;
163 | }
164 |
165 | @Override
166 | public void setParentClassLoader(ClassLoader classLoader) {
167 | this.parentClassLoader = classLoader;
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/src/com/yzb/http/SessionManager.java:
--------------------------------------------------------------------------------
1 | package com.yzb.http;
2 |
3 | import cn.hutool.core.convert.Convert;
4 | import cn.hutool.core.io.FileUtil;
5 | import cn.hutool.core.thread.ThreadUtil;
6 | import cn.hutool.core.util.RandomUtil;
7 | import cn.hutool.crypto.SecureUtil;
8 | import com.yzb.common.Request;
9 | import com.yzb.common.Response;
10 | import com.yzb.common.ServerContext;
11 | import org.jsoup.Jsoup;
12 | import org.jsoup.nodes.Document;
13 | import org.jsoup.select.Elements;
14 |
15 | import javax.print.Doc;
16 | import javax.servlet.ServletContext;
17 | import javax.servlet.http.Cookie;
18 | import javax.servlet.http.HttpSession;
19 | import java.io.File;
20 | import java.util.ArrayList;
21 | import java.util.List;
22 | import java.util.Map;
23 | import java.util.Set;
24 | import java.util.concurrent.ConcurrentHashMap;
25 |
26 | /**
27 | * @Description
28 | * @Date 2021/2/10 下午4:40
29 | * @Creater BeckoninGshy
30 | */
31 | public class SessionManager {
32 | private static Map sessionMap = new ConcurrentHashMap<>();
33 |
34 | private static int defaultTimeout = getTimeout();
35 | private static Thread checkoutThread;
36 |
37 | static{
38 | startCheckOutSessionOutDateThread();
39 | }
40 |
41 | //启动线程,每隔5s调用一次checkOutDateSession方法
42 | private static void startCheckOutSessionOutDateThread() {
43 | checkoutThread = new Thread(){
44 | public void run(){
45 | while(true){
46 | checkOutDateSession();
47 | ThreadUtil.sleep(1000*5);
48 | }
49 | }
50 | };
51 | checkoutThread.start();
52 | }
53 |
54 | private static void checkOutDateSession() {
55 | Set sessionId = sessionMap.keySet();
56 | List outdateJessionIds = new ArrayList<>();
57 | for(String id : sessionId){
58 | StandardSession standardSession = sessionMap.get(id);
59 | long interval = System.currentTimeMillis() - standardSession.getLastAccessedTime();
60 | if(standardSession.getMaxInactiveInterval() == -1) continue;
61 | //getMaxInactiveInterval --> s
62 | //interval --> ms
63 | if(interval > standardSession.getMaxInactiveInterval() * 1000L){
64 | outdateJessionIds.add(id);
65 | }
66 | }
67 | for(String id : outdateJessionIds){
68 | sessionMap.remove(id);
69 | }
70 | }
71 |
72 |
73 | private static int getTimeout(){
74 | int defautlTime = 30*60;
75 | Document document = Jsoup.parse(FileUtil.readUtf8String(ServerContext.webXMLPath));
76 | Elements select = document.select("session-config session-timeout");
77 | if(select.isEmpty()) return defautlTime;
78 | return Convert.toInt(select.get(0).text())*60;
79 | }
80 |
81 |
82 | public static void destroy(){
83 | checkoutThread.interrupt();
84 | try {
85 | checkoutThread.join();
86 | } catch (InterruptedException e) {
87 | }
88 | }
89 |
90 | // 如果浏览器没有传jsessionid过来,就创建一个新的session
91 | // 如果浏览器传递过来的jsessionid无效,那么也创建一个新的sessionid
92 | // 否则就使用现成的session,并修改他的lastAccessedTime,以及创建对应的cookie
93 | public static HttpSession getSession(String jsessionid, Request request, Response response) {
94 | if (null == jsessionid) {
95 | return newSession(request, response);
96 | } else {
97 | StandardSession currentSession = sessionMap.get(jsessionid);
98 | if (null == currentSession) {
99 | return newSession(request, response);
100 | } else {
101 | currentSession.setLastAccessedTime(System.currentTimeMillis());
102 | // createCookieBySession(currentSession, request, response);
103 | return currentSession;
104 | }
105 | }
106 | }
107 |
108 |
109 | private static void createCookieBySession(HttpSession session, Request request, Response response) {
110 | Cookie cookie = new Cookie("JSESSIONID", session.getId());
111 | cookie.setMaxAge(session.getMaxInactiveInterval());
112 | cookie.setPath(request.getServletContext().getContextPath());
113 | response.addCookie(cookie);
114 | }
115 |
116 |
117 | public static StandardSession newSession(Request request, Response response){
118 | ServletContext servletContext = request.getServletContext();
119 | String sessionId = generateSessionId();
120 | StandardSession standardSession = new StandardSession(sessionId, servletContext);
121 | standardSession.setMaxInactiveInterval(defaultTimeout);
122 | sessionMap.put(sessionId,standardSession);
123 | //create cookie
124 | createCookieBySession(standardSession,request,response);
125 | return standardSession;
126 | }
127 |
128 |
129 | public static synchronized String generateSessionId() {
130 | String result = null;
131 | byte[] bytes = RandomUtil.randomBytes(16);
132 | result = new String(bytes);
133 | result = SecureUtil.md5(result);
134 | result = result.toUpperCase();
135 | return result;
136 | }
137 |
138 | }
139 |
--------------------------------------------------------------------------------
/src/com/yzb/http/HttpConnector.java:
--------------------------------------------------------------------------------
1 | package com.yzb.http;
2 |
3 | import cn.hutool.log.LogFactory;
4 | import com.yzb.common.*;
5 | import com.yzb.core.Engine;
6 | import com.yzb.core.StandardConnector;
7 | import com.yzb.exception.LifecycleException;
8 | import com.yzb.exception.ParseHttpRequestException;
9 |
10 | import java.io.IOException;
11 | import java.net.ServerSocket;
12 | import java.net.Socket;
13 |
14 | /**
15 | * @Description
16 | * @Date 2021/1/29 下午10:13
17 | * @Creater BeckoninGshy
18 | */
19 | public class HttpConnector extends StandardConnector implements Runnable {
20 |
21 | private Thread workThread;
22 |
23 |
24 |
25 |
26 | @Override
27 | public void init() throws LifecycleException {
28 | super.init();
29 | try {
30 | serverSocket = new ServerSocket(port);
31 | } catch (IOException e) {
32 | LogFactory.get().error("init Connector{} failed, port {} is used", getName(), getPort());
33 | throw new LifecycleException();
34 | }
35 | }
36 |
37 | @Override
38 | public void start() throws LifecycleException {
39 | super.start();
40 | // start a thread to handle connecting
41 | workThread = new Thread(this);
42 | workThread.start();
43 | }
44 |
45 | @Override
46 | public void stop() throws LifecycleException {
47 | super.stop();
48 | workThread.interrupt();
49 | try{
50 | workThread.join(1000);
51 | } catch (InterruptedException e) {
52 | }
53 | }
54 |
55 | @Override
56 | public void destroy() throws LifecycleException {
57 | super.destroy();
58 | CommonThreadPool.shutdown();
59 | }
60 |
61 | @Override
62 | public HttpRequest createRequest(Socket socket, Connector connector) {
63 | try {
64 | return new HttpRequest(socket, connector);
65 | } catch (ParseHttpRequestException e) {
66 | //bad request
67 | LogFactory.get().warn("create request failed! maybe is a bad http request");
68 | e.printStackTrace();
69 | }
70 | return null;
71 | }
72 |
73 | @Override
74 | public HttpResponse createResponse(Socket socket) {
75 | return new HttpResponse(socket);
76 | }
77 |
78 | @Override
79 | public void run() {
80 | while(true){
81 | Socket socket = null;
82 | try {
83 | socket = serverSocket.accept();
84 | } catch (IOException e) {
85 | e.printStackTrace();
86 | LogFactory.get().warn("accept failed!");
87 | break;
88 | }
89 |
90 | Socket finalSocket = socket;
91 | HttpConnector cn = new HttpConnector();
92 | Runnable runnable = () -> {
93 | HttpRequest request = createRequest(finalSocket, this);
94 | HttpResponse response = createResponse(finalSocket);
95 |
96 | HttpProcessor httpProcessor = new HttpProcessor();
97 | httpProcessor.execute(finalSocket, request, response);
98 | };
99 | //add connector to thread pool
100 | CommonThreadPool.run(runnable);
101 | }
102 | }
103 |
104 |
105 | // find the ServletContext that matched URI, if all ServletContext mismatched URI, default use "/" context
106 | public ApplicationContext getServletContext(String URI) {
107 | if(URI == null) return null;
108 | Service service = getService();
109 | Container container = service.getContainer();
110 | Container[] servletContexts = null;
111 | Container rootContext = null;
112 | if(container instanceof Engine){
113 | servletContexts = ((Engine) container).getDefaultHost().findChildren();
114 | }
115 | assert servletContexts != null;
116 | for(Container servletContext : servletContexts){
117 | if(! (servletContext instanceof ApplicationContext)) continue;
118 | if(((ApplicationContext) servletContext).isDefaultContext()) continue;
119 | if(((ApplicationContext) servletContext).getPath().equals("/")) rootContext = servletContext;
120 | else if(URI.startsWith(((ApplicationContext) servletContext).getPath())) return (ApplicationContext) servletContext;
121 | }
122 | return (ApplicationContext) rootContext;
123 | }
124 |
125 | public ApplicationContext getDefaultServletContext() {
126 | Service service = getService();
127 | Container container = service.getContainer();
128 | Container[] servletContexts = null;
129 | Container defaultContext = null;
130 | if(container instanceof Engine){
131 | servletContexts = ((Engine) container).getDefaultHost().findChildren();
132 | }
133 | assert servletContexts != null;
134 | for(Container servletContext : servletContexts){
135 | if(! (servletContext instanceof ApplicationContext)) continue;
136 | if(((ApplicationContext) servletContext).isDefaultContext()) {
137 | defaultContext = servletContext;
138 | break;
139 | }
140 | }
141 | return (ApplicationContext) defaultContext;
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/com/yzb/util/ServerXMLParser.java:
--------------------------------------------------------------------------------
1 | package com.yzb.util;
2 |
3 | import cn.hutool.core.convert.Convert;
4 | import cn.hutool.core.io.FileUtil;
5 | import com.yzb.common.*;
6 | import com.yzb.core.*;
7 | import com.yzb.http.HttpConnector;
8 | import org.jsoup.Jsoup;
9 | import org.jsoup.nodes.Document;
10 | import org.jsoup.nodes.Element;
11 | import org.jsoup.select.Elements;
12 |
13 | /**
14 | * @Description
15 | * @Date 2021/1/30 下午9:58
16 | * @Creater BeckoninGshy
17 | */
18 | public class ServerXMLParser {
19 | private static String xml = FileUtil.readUtf8String(ServerContext.serverXMLPath);
20 | public static Server getServer(){
21 | StandardServer server = StandardServer.getServerInstance();
22 | Document document = Jsoup.parse(xml);
23 | Element element = document.select("Server").first();
24 | int port = Convert.toInt(element.attr("port"));
25 | String shutdown = element.attr("shutdown");
26 | server.setPort(port);
27 | server.setShutdown(shutdown);
28 | server.setName(ServerContext.serverName);
29 | return server;
30 | }
31 |
32 | public static Service[] getServices(){
33 | Document document = Jsoup.parse(xml);
34 | Elements elements = document.select("Service");
35 | StandardService[] service = new StandardService[elements.size()];
36 | for(int i = 0; i < elements.size(); i++){
37 | String name = elements.get(i).attr("name");
38 | service[i] = new StandardService();
39 | service[i].setName(name);
40 | }
41 | return service;
42 | }
43 |
44 | public static Connector[] getConnectors(String serviceName){
45 | Document document = Jsoup.parse(xml);
46 | Elements elements = document.select("Service");
47 |
48 | StandardService[] service = new StandardService[elements.size()];
49 | StandardConnector[] connectors = null;
50 | for(Element element : elements){
51 | String name = element.attr("name");
52 | if (name.equals(serviceName)) {
53 | Elements connectorEles = element.select("Connector");
54 | connectors = new StandardConnector[connectorEles.size()];
55 | for(int i = 0; i < connectorEles.size(); i++){
56 |
57 | int port = Convert.toInt(connectorEles.get(i).attr("port"));
58 | String protocol = connectorEles.get(i).attr("protocol");
59 | long connectionTimeout = Convert.toLong(connectorEles.get(i).attr("connectionTimeout"));
60 |
61 | //to do better.
62 | if("HTTP/1.1".equals(protocol)){
63 | connectors[i] = new HttpConnector();
64 | }else{
65 | connectors[i] = new StandardConnector();
66 | }
67 |
68 |
69 | connectors[i].setPort(port);
70 | connectors[i].setProtocol(protocol);
71 | connectors[i].setConnectionTimeout(connectionTimeout);
72 | }
73 | break;
74 | }
75 | }
76 | return connectors;
77 | }
78 | public static Engine getEngine(String serviceName){
79 | Document document = Jsoup.parse(xml);
80 | Elements elements = document.select("Service");
81 | StandardService[] service = new StandardService[elements.size()];
82 | Engine engine = null;
83 | for (Element element : elements) {
84 | String name = element.attr("name");
85 | if (name.equals(serviceName)) {
86 | Element engineEle = element.select("Engine").first();
87 | String engineName = engineEle.attr("name");
88 | String defaultHost = engineEle.attr("defaultHost");
89 | engine = new Engine();
90 | engine.setName(engineName);
91 | engine.setDefaultHostName(defaultHost);
92 | break;
93 | }
94 | }
95 | return engine;
96 | }
97 |
98 |
99 | public static Host[] getHost(String engineName){
100 | Document document = Jsoup.parse(xml);
101 | Elements elements = document.select("Engine");
102 | Host[] hosts = null;
103 | for (Element element: elements){
104 | String name = element.attr("name");
105 | if (name.equals(engineName)) {
106 | Elements hostEles = element.select("Host");
107 | hosts = new Host[hostEles.size()];
108 | for(int i = 0; i < hostEles.size(); i++){
109 | String hostName = hostEles.get(i).attr("name");
110 | String appBase = hostEles.get(i).attr("appBase");
111 | System.out.println(appBase);
112 | hosts[i] = new Host();
113 | hosts[i].setName(hostName);
114 | hosts[i].setAppBase(appBase);
115 | }
116 | break;
117 | }
118 | }
119 | return hosts;
120 | }
121 |
122 |
123 | public static Server getServerWithAutoPack(){
124 | Server server = getServer();
125 | Service[] services = getServices();
126 | for(Service service : services){
127 | service.setServer(server);
128 | server.addService(service);
129 | Connector[] connectors = getConnectors(service.getName());
130 | for(Connector connector : connectors){
131 | connector.setService(service);
132 | service.addConnector(connector);
133 | }
134 | Engine engine = getEngine(service.getName());
135 | engine.setService(service);
136 | service.setContainer(engine);
137 |
138 | Host[] hosts = getHost(engine.getName());
139 | for(Host host : hosts){
140 | host.setService(service);
141 | host.setParent(engine);
142 |
143 | engine.addChild(host);
144 | }
145 | }
146 |
147 | return server;
148 |
149 | }
150 |
151 | }
152 |
--------------------------------------------------------------------------------
/src/com/yzb/core/StandardServletContext.java:
--------------------------------------------------------------------------------
1 | package com.yzb.core;
2 |
3 | import com.yzb.core.Context;
4 |
5 | import javax.servlet.*;
6 | import javax.servlet.descriptor.JspConfigDescriptor;
7 | import java.io.InputStream;
8 | import java.net.MalformedURLException;
9 | import java.net.URL;
10 | import java.util.Enumeration;
11 | import java.util.EventListener;
12 | import java.util.Map;
13 | import java.util.Set;
14 |
15 | /**
16 | * @Description
17 | * @Date 2021/2/1 下午1:29
18 | * @Creater BeckoninGshy
19 | */
20 | public class StandardServletContext extends Context implements ServletContext {
21 |
22 | @Override
23 | public ServletContext getContext(String s) {
24 | return null;
25 | }
26 |
27 | @Override
28 | public String getContextPath() {
29 | return null;
30 | }
31 |
32 | @Override
33 | public int getMajorVersion() {
34 | return 0;
35 | }
36 |
37 | @Override
38 | public int getMinorVersion() {
39 | return 0;
40 | }
41 |
42 | @Override
43 | public int getEffectiveMajorVersion() {
44 | return 0;
45 | }
46 |
47 | @Override
48 | public int getEffectiveMinorVersion() {
49 | return 0;
50 | }
51 |
52 | @Override
53 | public String getMimeType(String s) {
54 | return null;
55 | }
56 |
57 | @Override
58 | public Set getResourcePaths(String s) {
59 | return null;
60 | }
61 |
62 | @Override
63 | public URL getResource(String s) throws MalformedURLException {
64 | return null;
65 | }
66 |
67 | @Override
68 | public InputStream getResourceAsStream(String s) {
69 | return null;
70 | }
71 |
72 | @Override
73 | public RequestDispatcher getRequestDispatcher(String s) {
74 | return null;
75 | }
76 |
77 | @Override
78 | public RequestDispatcher getNamedDispatcher(String s) {
79 | return null;
80 | }
81 |
82 | @Override
83 | public Servlet getServlet(String s) throws ServletException {
84 | return null;
85 | }
86 |
87 | @Override
88 | public Enumeration getServlets() {
89 | return null;
90 | }
91 |
92 | @Override
93 | public Enumeration getServletNames() {
94 | return null;
95 | }
96 |
97 | @Override
98 | public void log(String s) {
99 |
100 | }
101 |
102 | @Override
103 | public void log(Exception e, String s) {
104 |
105 | }
106 |
107 | @Override
108 | public void log(String s, Throwable throwable) {
109 |
110 | }
111 |
112 | @Override
113 | public String getRealPath(String s) {
114 | return null;
115 | }
116 |
117 | @Override
118 | public String getServerInfo() {
119 | return null;
120 | }
121 |
122 | @Override
123 | public String getInitParameter(String s) {
124 | return null;
125 | }
126 |
127 | @Override
128 | public Enumeration getInitParameterNames() {
129 | return null;
130 | }
131 |
132 | @Override
133 | public boolean setInitParameter(String s, String s1) {
134 | return false;
135 | }
136 |
137 | @Override
138 | public Enumeration getAttributeNames() {
139 | return null;
140 | }
141 |
142 | @Override
143 | public String getServletContextName() {
144 | return null;
145 | }
146 |
147 | @Override
148 | public ServletRegistration.Dynamic addServlet(String s, String s1) {
149 | return null;
150 | }
151 |
152 | @Override
153 | public ServletRegistration.Dynamic addServlet(String s, Servlet servlet) {
154 | return null;
155 | }
156 |
157 | @Override
158 | public ServletRegistration.Dynamic addServlet(String s, Class extends Servlet> aClass) {
159 | return null;
160 | }
161 |
162 | @Override
163 | public T createServlet(Class aClass) throws ServletException {
164 | return null;
165 | }
166 |
167 | @Override
168 | public ServletRegistration getServletRegistration(String s) {
169 | return null;
170 | }
171 |
172 | @Override
173 | public Map getServletRegistrations() {
174 | return null;
175 | }
176 |
177 | @Override
178 | public FilterRegistration.Dynamic addFilter(String s, String s1) {
179 | return null;
180 | }
181 |
182 | @Override
183 | public FilterRegistration.Dynamic addFilter(String s, Filter filter) {
184 | return null;
185 | }
186 |
187 | @Override
188 | public FilterRegistration.Dynamic addFilter(String s, Class extends Filter> aClass) {
189 | return null;
190 | }
191 |
192 | @Override
193 | public T createFilter(Class aClass) throws ServletException {
194 | return null;
195 | }
196 |
197 | @Override
198 | public FilterRegistration getFilterRegistration(String s) {
199 | return null;
200 | }
201 |
202 | @Override
203 | public Map getFilterRegistrations() {
204 | return null;
205 | }
206 |
207 | @Override
208 | public SessionCookieConfig getSessionCookieConfig() {
209 | return null;
210 | }
211 |
212 | @Override
213 | public void setSessionTrackingModes(Set set) throws IllegalStateException, IllegalArgumentException {
214 |
215 | }
216 |
217 | @Override
218 | public Set getDefaultSessionTrackingModes() {
219 | return null;
220 | }
221 |
222 | @Override
223 | public Set getEffectiveSessionTrackingModes() {
224 | return null;
225 | }
226 |
227 | @Override
228 | public void addListener(String s) {
229 |
230 | }
231 |
232 | @Override
233 | public void addListener(T t) {
234 |
235 | }
236 |
237 | @Override
238 | public void addListener(Class extends EventListener> aClass) {
239 |
240 | }
241 |
242 | @Override
243 | public T createListener(Class aClass) throws ServletException {
244 | return null;
245 | }
246 |
247 | @Override
248 | public void declareRoles(String... strings) {
249 |
250 | }
251 |
252 | @Override
253 | public ClassLoader getClassLoader() {
254 | return null;
255 | }
256 |
257 | @Override
258 | public JspConfigDescriptor getJspConfigDescriptor() {
259 | return null;
260 | }
261 | }
262 |
--------------------------------------------------------------------------------
/src/com/yzb/core/StandardServer.java:
--------------------------------------------------------------------------------
1 | package com.yzb.core;
2 |
3 | import cn.hutool.core.date.DateUtil;
4 | import cn.hutool.core.date.TimeInterval;
5 | import cn.hutool.log.LogFactory;
6 | import cn.hutool.system.SystemUtil;
7 | import com.yzb.common.Server;
8 | import com.yzb.common.ServerContext;
9 | import com.yzb.common.Service;
10 | import com.yzb.exception.LifecycleException;
11 |
12 | import java.io.*;
13 | import java.net.ServerSocket;
14 | import java.net.Socket;
15 | import java.util.LinkedHashMap;
16 | import java.util.Map;
17 | import java.util.Set;
18 |
19 | public class StandardServer implements Server, Runnable {
20 | private volatile static StandardServer standardServerInstance;
21 | private int port = -1;
22 | private String name = "TinyWebServer";
23 | private String shutdown = "shutdown";
24 | private String address = "localhost";
25 | private ClassLoader parentClassLoader;
26 |
27 | private Service[] services = new Service[0];
28 | private final Object servicesLock = new Object();
29 |
30 | private static ServerSocket serverSocket;
31 |
32 | public StandardServer() {
33 | }
34 |
35 | public static StandardServer getServerInstance() {
36 | if (standardServerInstance == null) {
37 | synchronized (StandardServer.class) {
38 | if (standardServerInstance == null) {
39 | standardServerInstance = new StandardServer();
40 | }
41 | }
42 | }
43 | return standardServerInstance;
44 | }
45 |
46 | @Override
47 | public void setPort(int port) {
48 | this.port = port;
49 | }
50 |
51 | @Override
52 | public int getPort() {
53 | return port;
54 | }
55 |
56 | @Override
57 | public String getAddress() {
58 | return address;
59 | }
60 |
61 | @Override
62 | public void setAddress(String address) {
63 | this.address = address;
64 | }
65 |
66 | @Override
67 | public String getShutdown() {
68 | return shutdown;
69 | }
70 |
71 | @Override
72 | public void setShutdown(String shutdown) {
73 | this.shutdown = shutdown;
74 | }
75 |
76 | @Override
77 | public void addService(Service service) {
78 | int len = services.length;
79 | Service[] newServices = new Service[len+1];
80 | System.arraycopy(services,0,newServices,0,len);
81 | newServices[len] = service;
82 | services = newServices;
83 | }
84 |
85 | @Override
86 | public Service findService(String service) {
87 | int len = services.length;
88 | for (Service value : services) {
89 | if (value.getName().equals(service)) return value;
90 | }
91 | return null;
92 | }
93 |
94 | @Override
95 | public Service[] findServices() {
96 | return services;
97 | }
98 |
99 | @Override
100 | public String[] getServiceNames() {
101 | int len = services.length;
102 | String[] names = new String[len];
103 | for(int i = 0; i < len; i++){
104 | names[i] = services[i].getName();
105 | }
106 | return names;
107 | }
108 |
109 | @Override
110 | public void removeService(Service service) {
111 | int idx = 0, len = services.length;
112 | while(idx < len && services[idx] != service) idx++;
113 | if(idx == len) return;
114 | try {
115 | services[idx].stop();
116 | } catch (LifecycleException e) {
117 | //noting to do.
118 | }
119 | Service[] newServices = new Service[len-1];
120 | System.arraycopy(services,0,newServices,0,idx);
121 | System.arraycopy(services,idx+1,newServices,idx,len-idx-1);
122 | services = newServices;
123 | }
124 |
125 | @Override
126 | public ClassLoader getParentClassLoader() {
127 | return parentClassLoader;
128 | }
129 |
130 | @Override
131 | public void setParentClassLoader(ClassLoader classLoader) {
132 | this.parentClassLoader = classLoader;
133 | }
134 |
135 | @Override
136 | public String getName() {
137 | return name;
138 | }
139 |
140 | @Override
141 | public void setName(String name) {
142 | this.name = name;
143 | }
144 |
145 | @Override
146 | public void start() {
147 | TimeInterval startTimer = DateUtil.timer();
148 | synchronized (servicesLock) {
149 | for(Service service : services){
150 | try {
151 | service.start();
152 | } catch (LifecycleException e) {
153 | e.printStackTrace();
154 | LogFactory.get().error("Start service{} failed", service.getName());
155 | }
156 | }
157 | }
158 | LogFactory.get().info("Server startup in {} ms", startTimer.intervalMs());
159 | }
160 |
161 | @Override
162 | public void stop() throws LifecycleException {
163 | for (Service service : services){
164 | try {
165 | service.stop();
166 | } catch (LifecycleException e) {
167 | }
168 | }
169 | LogFactory.get().info("Stopped server{}", getName());
170 | }
171 |
172 | @Override
173 | public void destroy() throws LifecycleException {
174 | for (Service service : services){
175 | try {
176 | service.destroy();
177 | } catch (LifecycleException e) {
178 | }
179 | }
180 | LogFactory.get().info("destroyed Server{}", getName());
181 | }
182 |
183 | @Override
184 | public String getState() {
185 | return null;
186 | }
187 |
188 | @Override
189 | public void init(){
190 | logJVM();
191 | synchronized (servicesLock) {
192 | for (Service service : services){
193 | try {
194 | service.init();
195 | } catch (LifecycleException e) {
196 | LogFactory.get().error("Init service {} failed",service.getName());
197 | }
198 | }
199 | LogFactory.get().info("Init server {}", getName());
200 | }
201 | }
202 |
203 | private static void logJVM() {
204 | Map infos = new LinkedHashMap<>();
205 | infos.put("Server Name", ServerContext.serverName);
206 | infos.put("Server built", DateUtil.now());
207 | infos.put("Server version", ServerContext.version);
208 | infos.put("OS Name\t", SystemUtil.get("os.name"));
209 | infos.put("OS version", SystemUtil.get("os.version"));
210 | infos.put("Architecture", SystemUtil.get("java.home"));
211 | infos.put("Java Home", SystemUtil.get("java home"));
212 | infos.put("JVM Version", SystemUtil.get("java.runtime.version"));
213 | infos.put("JVM Vendor", SystemUtil.get("java.vm.specification.vendor"));
214 | Set keys = infos.keySet();
215 | for (String key : keys) {
216 | LogFactory.get().info(key + ":\t\t" + infos.get(key));
217 | }
218 | }
219 |
220 | @Override
221 | public void run() {
222 |
223 | try {
224 | serverSocket = new ServerSocket(port);
225 |
226 | while (true) {
227 | Socket socket = serverSocket.accept();
228 |
229 | }
230 | } catch (IOException e) {
231 | // connector failed
232 | e.printStackTrace();
233 | }
234 | }
235 |
236 | }
--------------------------------------------------------------------------------
/src/com/yzb/common/Request.java:
--------------------------------------------------------------------------------
1 | package com.yzb.common;
2 |
3 | import javax.servlet.*;
4 | import javax.servlet.http.*;
5 | import java.io.BufferedReader;
6 | import java.io.IOException;
7 | import java.io.UnsupportedEncodingException;
8 | import java.security.Principal;
9 | import java.util.Collection;
10 | import java.util.Enumeration;
11 | import java.util.Locale;
12 | import java.util.Map;
13 |
14 |
15 | /**
16 | * @description: 实现HttpServletRequest接口的基础类
17 | * @author: BeckoningGshy
18 | * @create: 2021-01-24 16:16
19 | * */
20 | public class Request implements HttpServletRequest {
21 | @Override
22 | public String getAuthType() {
23 | return null;
24 | }
25 |
26 | @Override
27 | public Cookie[] getCookies() {
28 | return new Cookie[0];
29 | }
30 |
31 | @Override
32 | public long getDateHeader(String s) {
33 | return 0;
34 | }
35 |
36 | @Override
37 | public String getHeader(String s) {
38 | return null;
39 | }
40 |
41 | @Override
42 | public Enumeration getHeaders(String s) {
43 | return null;
44 | }
45 |
46 | @Override
47 | public Enumeration getHeaderNames() {
48 | return null;
49 | }
50 |
51 | @Override
52 | public int getIntHeader(String s) {
53 | return 0;
54 | }
55 |
56 | @Override
57 | public String getMethod() {
58 | return null;
59 | }
60 |
61 | @Override
62 | public String getPathInfo() {
63 | return null;
64 | }
65 |
66 | @Override
67 | public String getPathTranslated() {
68 | return null;
69 | }
70 |
71 | @Override
72 | public String getContextPath() {
73 | return null;
74 | }
75 |
76 | @Override
77 | public String getQueryString() {
78 | return null;
79 | }
80 |
81 | @Override
82 | public String getRemoteUser() {
83 | return null;
84 | }
85 |
86 | @Override
87 | public boolean isUserInRole(String s) {
88 | return false;
89 | }
90 |
91 | @Override
92 | public Principal getUserPrincipal() {
93 | return null;
94 | }
95 |
96 | @Override
97 | public String getRequestedSessionId() {
98 | return null;
99 | }
100 |
101 | @Override
102 | public String getRequestURI() {
103 | return null;
104 | }
105 |
106 | @Override
107 | public StringBuffer getRequestURL() {
108 | return null;
109 | }
110 |
111 | @Override
112 | public String getServletPath() {
113 | return null;
114 | }
115 |
116 | @Override
117 | public HttpSession getSession(boolean b) {
118 | return null;
119 | }
120 |
121 | @Override
122 | public HttpSession getSession() {
123 | return null;
124 | }
125 |
126 | @Override
127 | public boolean isRequestedSessionIdValid() {
128 | return false;
129 | }
130 |
131 | @Override
132 | public boolean isRequestedSessionIdFromCookie() {
133 | return false;
134 | }
135 |
136 | @Override
137 | public boolean isRequestedSessionIdFromURL() {
138 | return false;
139 | }
140 |
141 | @Override
142 | public boolean isRequestedSessionIdFromUrl() {
143 | return false;
144 | }
145 |
146 | @Override
147 | public boolean authenticate(HttpServletResponse httpServletResponse) throws IOException, ServletException {
148 | return false;
149 | }
150 |
151 | @Override
152 | public void login(String s, String s1) throws ServletException {
153 |
154 | }
155 |
156 | @Override
157 | public void logout() throws ServletException {
158 |
159 | }
160 |
161 | @Override
162 | public Collection getParts() throws IOException, IllegalStateException, ServletException {
163 | return null;
164 | }
165 |
166 | @Override
167 | public Part getPart(String s) throws IOException, IllegalStateException, ServletException {
168 | return null;
169 | }
170 |
171 | @Override
172 | public Object getAttribute(String s) {
173 | return null;
174 | }
175 |
176 | @Override
177 | public Enumeration getAttributeNames() {
178 | return null;
179 | }
180 |
181 | @Override
182 | public String getCharacterEncoding() {
183 | return null;
184 | }
185 |
186 | @Override
187 | public void setCharacterEncoding(String s) throws UnsupportedEncodingException {
188 |
189 | }
190 |
191 | @Override
192 | public int getContentLength() {
193 | return 0;
194 | }
195 |
196 | @Override
197 | public String getContentType() {
198 | return null;
199 | }
200 |
201 | @Override
202 | public ServletInputStream getInputStream() throws IOException {
203 | return null;
204 | }
205 |
206 | @Override
207 | public String getParameter(String s) {
208 | return null;
209 | }
210 |
211 | @Override
212 | public Enumeration getParameterNames() {
213 | return null;
214 | }
215 |
216 | @Override
217 | public String[] getParameterValues(String s) {
218 | return new String[0];
219 | }
220 |
221 | @Override
222 | public Map getParameterMap() {
223 | return null;
224 | }
225 |
226 | @Override
227 | public String getProtocol() {
228 | return null;
229 | }
230 |
231 | @Override
232 | public String getScheme() {
233 | return null;
234 | }
235 |
236 | @Override
237 | public String getServerName() {
238 | return null;
239 | }
240 |
241 | @Override
242 | public int getServerPort() {
243 | return 0;
244 | }
245 |
246 | @Override
247 | public BufferedReader getReader() throws IOException {
248 | return null;
249 | }
250 |
251 | @Override
252 | public String getRemoteAddr() {
253 | return null;
254 | }
255 |
256 | @Override
257 | public String getRemoteHost() {
258 | return null;
259 | }
260 |
261 | @Override
262 | public void setAttribute(String s, Object o) {
263 |
264 | }
265 |
266 | @Override
267 | public void removeAttribute(String s) {
268 |
269 | }
270 |
271 | @Override
272 | public Locale getLocale() {
273 | return null;
274 | }
275 |
276 | @Override
277 | public Enumeration getLocales() {
278 | return null;
279 | }
280 |
281 | @Override
282 | public boolean isSecure() {
283 | return false;
284 | }
285 |
286 | @Override
287 | public RequestDispatcher getRequestDispatcher(String s) {
288 | return null;
289 | }
290 |
291 | @Override
292 | public String getRealPath(String s) {
293 | return null;
294 | }
295 |
296 | @Override
297 | public int getRemotePort() {
298 | return 0;
299 | }
300 |
301 | @Override
302 | public String getLocalName() {
303 | return null;
304 | }
305 |
306 | @Override
307 | public String getLocalAddr() {
308 | return null;
309 | }
310 |
311 | @Override
312 | public int getLocalPort() {
313 | return 0;
314 | }
315 |
316 | @Override
317 | public ServletContext getServletContext() {
318 | return null;
319 | }
320 |
321 | @Override
322 | public AsyncContext startAsync() {
323 | return null;
324 | }
325 |
326 | @Override
327 | public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) {
328 | return null;
329 | }
330 |
331 | @Override
332 | public boolean isAsyncStarted() {
333 | return false;
334 | }
335 |
336 | @Override
337 | public boolean isAsyncSupported() {
338 | return false;
339 | }
340 |
341 | @Override
342 | public AsyncContext getAsyncContext() {
343 | return null;
344 | }
345 |
346 | @Override
347 | public DispatcherType getDispatcherType() {
348 | return null;
349 | }
350 | }
351 |
--------------------------------------------------------------------------------
/test/com/yzb/CommonTest.java:
--------------------------------------------------------------------------------
1 | package com.yzb;
2 |
3 | import cn.hutool.core.date.DateTime;
4 | import cn.hutool.core.date.DateUtil;
5 | import cn.hutool.core.lang.Console;
6 | import cn.hutool.core.util.StrUtil;
7 | import com.yzb.common.ServerContext;
8 | import com.yzb.exception.ParseHttpRequestException;
9 | import org.junit.Test;
10 |
11 | import java.io.*;
12 | import java.text.SimpleDateFormat;
13 | import java.util.*;
14 |
15 | /**
16 | * @description: use to some common test
17 | * @author: BeckoninGshy
18 | * @create: 2021/1/24 20:41
19 | */
20 | public class CommonTest {
21 | @Test
22 | public void test1(){
23 | String outlineMessage1 = "GET /index.html HTTP/1.1";
24 | String outlineMessage2 = "POST / HTTP/1.1";
25 | String method1 = StrUtil.subBefore(outlineMessage1, " ", false);
26 | String method2 = StrUtil.subBefore(outlineMessage2, " ", false);
27 | Console.log(method1);
28 | Console.log(method2);
29 | }
30 | @Test
31 | public void test2(){
32 | String outlineMessage1 = "GET /index.html HTTP/1.1";
33 | String outlineMessage2 = "POST / HTTP/1.1";
34 | String queryString1 = StrUtil.subBetween(outlineMessage1, " ", " ");
35 | String queryString2 = StrUtil.subBetween(outlineMessage2, " ", " ");
36 | Console.log(queryString1);
37 | Console.log(queryString2);
38 | }
39 |
40 | @Test
41 | public void test3(){
42 | String outlineMessage1 = "GET /index.html HTTP/1.1";
43 | String outlineMessage2 = "POST / HTTP/1.1";
44 | String schema1 = outlineMessage1.substring(outlineMessage1.lastIndexOf(' ')+1);
45 | String schema2 = outlineMessage2.substring(outlineMessage2.lastIndexOf(' ')+1);
46 | Console.log(schema1);
47 | Console.log(schema2);
48 | }
49 |
50 | @Test
51 | public void test4(){
52 | String outlineMessage = "HTTP/1.1";
53 | String schema1 = StrUtil.subBefore(outlineMessage, "/", false);
54 |
55 | Console.log(schema1.toLowerCase(Locale.ROOT));
56 | }
57 |
58 | @Test
59 | public void test5(){
60 | String outlineMessage1 = "/index.html";
61 | String outlineMessage2 = "/index.html?username=lisi&password=123";
62 | String outlineMessage3 = "/";
63 | String schema1 = StrUtil.subAfter(outlineMessage1, "?", false);
64 | String schema2 = StrUtil.subAfter(outlineMessage2, "?", false);
65 | String schema3 = StrUtil.subAfter(outlineMessage3, "?", false);
66 | Console.log(schema1);
67 | Console.log(schema2);
68 | Console.log(schema3);
69 | }
70 |
71 | @Test
72 | public void test6(){
73 | String outlineMessage1 = "/index.html";
74 | String outlineMessage2 = "/index.html?username=lisi&password=123";
75 | String outlineMessage3 = "/";
76 | String schema1 = StrUtil.subBefore(outlineMessage1, "?", false);
77 | String schema2 = StrUtil.subBefore(outlineMessage2, "?", false);
78 | String schema3 = StrUtil.subBefore(outlineMessage3, "?", false);
79 | Console.log(schema1);
80 | Console.log(schema2);
81 | Console.log(schema3);
82 | }
83 | @Test
84 | public void test7() throws ParseHttpRequestException {
85 | String parameterString = "username=zhangsan&password=123&sex=male&vehicle=Bike&vehicle=Car&city=v_shanghai&message=++++++++++++The+cat+was+playing+in+the+garden.%0D%0A++++++++++++";
86 | String[] parameters = parameterString.split("&");
87 | Map> parameterKVs = new HashMap<>();
88 | for(int i = 0, n = parameters.length; i < n; i++){
89 | String[] entries = parameters[i].split("=");
90 | if(entries.length != 2) {
91 | throw new ParseHttpRequestException("解析请求参数错误!");
92 | }
93 | List values = parameterKVs.getOrDefault(entries[0], new ArrayList<>());
94 | values.add(entries[1]);
95 | parameterKVs.put(entries[0], values);
96 | }
97 |
98 | Map ans = new HashMap<>();
99 | ans.put("vehicle",new String[]{"BMW"});
100 | for(Map.Entry> entry : parameterKVs.entrySet()){
101 | String[] origin = ans.getOrDefault(entry.getKey(), new String[0]);
102 | String[] add = entry.getValue().toArray(new String[0]);
103 | int len = origin.length + add.length;
104 | String[] result = Arrays.copyOf(origin, len);
105 | System.arraycopy(add,0,result,origin.length,add.length);
106 | ans.put(entry.getKey(),result);
107 | }
108 | for(Map.Entry entry : ans.entrySet()){
109 | System.out.println(entry.getKey() + "===" + Arrays.toString(entry.getValue()));
110 | }
111 | System.out.println(ans.getOrDefault("123",new String[1])[0]);
112 | }
113 |
114 | @Test
115 | public void test8(){
116 | String sendMessage1 = "POST / HTTP/1.1\r\n"
117 | + "User-Agent: yzb's client\r\n"
118 | + "Content-Length: 26\r\n"
119 | + "\r\n"
120 | + "username=lisi&password=123\r\n";
121 | String sendMessage2 = "POST / HTTP/1.1\r\n"
122 | + "User-Agent: yzb's client\r\n"
123 | + "Content-Length: 26\r\n"
124 | + "\r\n";
125 | // System.out.println(StrUtil.subBefore(sendMessage1, "\r\n\r\n", false));
126 | // System.out.println(StrUtil.subBefore(sendMessage2, "\r\n\r\n", false));
127 | // System.out.println(StrUtil.subAfter(sendMessage1, "\r\n\r\n", false));
128 | System.out.println(StrUtil.subAfter(sendMessage2, "\r\n\r\n", false));
129 | }
130 |
131 | @Test
132 | public void test9(){
133 | String language = "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2";
134 | System.out.println(StrUtil.splitTrim(language, ","));
135 | }
136 |
137 | @Test
138 | public void test10(){
139 | String ifum = "Wd, 21 Oct 2015 07:28:00 GMT";
140 | Date date = null;
141 | try{
142 | date = new Date("-1");
143 | } catch (IllegalArgumentException e){
144 | System.out.println(-1);
145 | }
146 |
147 | System.out.println(date.getTime());
148 | }
149 |
150 | @Test
151 | public void test11(){
152 | // System.out.println(new Date(0).toString());
153 | TimeZone timeZone = TimeZone.getTimeZone("UTC");
154 | Calendar calendar = Calendar.getInstance(timeZone);
155 | calendar.setTimeInMillis(0);
156 | SimpleDateFormat simpleDateFormat =
157 | new SimpleDateFormat("EE MMM dd HH:mm:ss zzz yyyy", Locale.US);
158 | simpleDateFormat.setTimeZone(timeZone);
159 | System.out.println("UTC: " + simpleDateFormat.format(calendar.getTime()));
160 | }
161 |
162 | @Test
163 | public void test12(){
164 | String characterEncoding = "utf-8";
165 | String ContentType = "multipart/form-data; boundary=something";
166 | Map header = new HashMap<>();
167 | header.put("ContentType", ContentType);
168 | String contentType = header.getOrDefault("ContentType", "");
169 | if(StrUtil.indexOfIgnoreCase(contentType,"charset=") == -1) {
170 | System.out.println(-1);
171 | return;
172 | }
173 | String ans = StrUtil.subBefore(contentType,"charset=", false) + "charset=" + characterEncoding;
174 | System.out.println(ans);
175 | }
176 |
177 | @Test
178 | public void test13(){
179 |
180 | String contentType = "multipart/form-data; boundary=something";
181 | String ans = StrUtil.subAfter(contentType,"charset=", false);
182 | System.out.println(ans);
183 | }
184 |
185 | @Test
186 | public void test14() throws IOException {
187 | BufferedReader bufferedReader = new BufferedReader(new FileReader(new File("webapps/form.html")));
188 | System.out.println(bufferedReader.readLine());
189 |
190 | }
191 |
192 | @Test
193 | public void test15(){
194 | System.out.println(ServerContext.serverBasePath);
195 | System.out.println(ServerContext.serverXMLPath);
196 | }
197 |
198 | @Test
199 | public void test16(){
200 | String contextsDirectory = ServerContext.serverBasePath+ File.separator + "webapps";
201 | File contexts = new File(contextsDirectory);
202 | System.out.println(contexts.getName());
203 | }
204 |
205 | }
206 |
--------------------------------------------------------------------------------
/.idea/uiDesigner.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
8 | -
9 |
10 |
11 | -
12 |
13 |
14 | -
15 |
16 |
17 | -
18 |
19 |
20 |
21 |
22 |
23 | -
24 |
25 |
26 |
27 |
28 |
29 | -
30 |
31 |
32 |
33 |
34 |
35 | -
36 |
37 |
38 |
39 |
40 |
41 | -
42 |
43 |
44 |
45 |
46 | -
47 |
48 |
49 |
50 |
51 | -
52 |
53 |
54 |
55 |
56 | -
57 |
58 |
59 |
60 |
61 | -
62 |
63 |
64 |
65 |
66 | -
67 |
68 |
69 |
70 |
71 | -
72 |
73 |
74 | -
75 |
76 |
77 |
78 |
79 | -
80 |
81 |
82 |
83 |
84 | -
85 |
86 |
87 |
88 |
89 | -
90 |
91 |
92 |
93 |
94 | -
95 |
96 |
97 |
98 |
99 | -
100 |
101 |
102 | -
103 |
104 |
105 | -
106 |
107 |
108 | -
109 |
110 |
111 | -
112 |
113 |
114 |
115 |
116 | -
117 |
118 |
119 | -
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/src/com/yzb/http/HttpContant.java:
--------------------------------------------------------------------------------
1 | package com.yzb.http;
2 |
3 | import com.yzb.common.ServerContext;
4 |
5 | /**
6 | * @description:
7 | * @author: BeckoninGshy
8 | * @create: 2021/1/25 12:03
9 | */
10 | public class HttpContant {
11 |
12 | public static final String SEPARATOR_LINES = "\r\n\r\n";
13 | public static final String LINE_TERMINATOR = "\r\n";
14 | public static final String LINE_SEPARATOR = ": ";
15 | public static final String HEADER_SEPARATOR = ",";
16 | public static final String PARAMATERS_SEPARATOR = "&";
17 | public static final String PARAMATERSKV_SEPARATOR = "=";
18 |
19 |
20 | public static final String REQUEST_METHOD_GET = "GET";
21 | public static final String REQUEST_METHOD_POST = "POST";
22 | public static final String REQUEST_METHOD_DELETE = "DELETE";
23 | public static final String REQUEST_METHOD_HEAD = "HEAD";
24 | public static final String REQUEST_METHOD_CONNECT = "CONNECT";
25 | public static final String REQUEST_METHOD_OPTIONS = "OPTIONS";
26 | public static final String REQUEST_METHOD_TRACE = "TRACE";
27 | public static final String REQUEST_METHOD_PATCH = "PATCH";
28 |
29 | public static final String HEADER_CONTENT_LENGTH = "Content-Length";
30 | public static final String HEADER_CONTENT_TYPE = "Content-Type";
31 | public static final String HEADER_HOST = "Host";
32 | public static final String HEADER_SERVER = "Server";
33 |
34 | public static final String DEFAULT_CONTENT_TYPE = "text/html; charset=UTF-8";
35 | public static final String DEFAULT_CHARACTERENCODING = "UTF-8";
36 | public static final String CONTENT_TYPE_SEPARATOR = "charset=";
37 |
38 |
39 | public static final int RESPONSE_SC_ACCEPTED = 202;
40 | public static final int RESPONSE_SC_BAD_GATEWAY = 502;
41 | public static final int RESPONSE_SC_BAD_REQUEST = 400;
42 | public static final int RESPONSE_SC_CONFLICT = 409;
43 | public static final int RESPONSE_SC_CONTINUE = 100;
44 | public static final int RESPONSE_SC_CREATED = 201;
45 | public static final int RESPONSE_SC_EXPECTATION_FAILED = 417;
46 | public static final int RESPONSE_SC_FORBIDDEN = 403;
47 | public static final int RESPONSE_SC_FOUND = 302;
48 | public static final int RESPONSE_SC_GATEWAY_TIMEOUT = 504;
49 | public static final int RESPONSE_SC_GONE = 410;
50 | public static final int RESPONSE_SC_HTTP_VERSION_NOT_SUPPORTED = 505;
51 | public static final int RESPONSE_SC_INTERNAL_SERVER_ERROR = 500;
52 | public static final int RESPONSE_SC_LENGTH_REQUIRED = 411;
53 | public static final int RESPONSE_SC_METHOD_NOT_ALLOWED = 405;
54 | public static final int RESPONSE_SC_MOVED_PERMANENTLY = 301;
55 | public static final int RESPONSE_SC_MOVED_TEMPORARILY = 302;
56 | public static final int RESPONSE_SC_MULTIPLE_CHOICES = 300;
57 | public static final int RESPONSE_SC_NO_CONTENT = 204;
58 | public static final int RESPONSE_SC_NON_AUTHORITATIVE_INFORMATION = 203;
59 | public static final int RESPONSE_SC_NOT_ACCEPTABLE = 406;
60 | public static final int RESPONSE_SC_NOT_FOUND = 404;
61 | public static final int RESPONSE_SC_NOT_IMPLEMENTED = 501;
62 | public static final int RESPONSE_SC_NOT_MODIFIED = 304;
63 | public static final int RESPONSE_SC_OK = 200;
64 | public static final int RESPONSE_SC_PARTIAL_CONTENT = 206;
65 | public static final int RESPONSE_SC_PAYMENT_REQUIRED = 402;
66 | public static final int RESPONSE_SC_PRECONDITION_FAILED = 412;
67 | public static final int RESPONSE_SC_PROXY_AUTHENTICATION_REQUIRED = 407;
68 | public static final int RESPONSE_SC_REQUEST_ENTITY_TOO_LARGE = 413;
69 | public static final int RESPONSE_SC_REQUEST_TIMEOUT = 408;
70 | public static final int RESPONSE_SC_REQUEST_URI_TOO_LONG = 414;
71 | public static final int RESPONSE_SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
72 | public static final int RESPONSE_SC_RESET_CONTENT = 205;
73 | public static final int RESPONSE_SC_SEE_OTHER = 303;
74 | public static final int RESPONSE_SC_SERVICE_UNAVAILABLE = 503;
75 | public static final int RESPONSE_SC_SWITCHING_PROTOCOLS = 101;
76 | public static final int RESPONSE_SC_TEMPORARY_REDIRECT = 307;
77 | public static final int RESPONSE_SC_UNAUTHORIZED = 401;
78 | public static final int RESPONSE_SC_UNSUPPORTED_MEDIA_TYPE = 415;
79 | public static final int RESPONSE_SC_USE_PROXY = 305;
80 |
81 | public static final String textFormat_normal =
82 | "" +ServerContext.serverName+ "/"+ServerContext.version+" - Error report " +
90 | "HTTP Status {} " +
91 | "type Status report
message
description " +
92 | "
"+ ServerContext.serverName +" "+ServerContext.version+" " +
93 | "";
94 |
95 | public static final String textFormat_404 =
96 | "" +ServerContext.serverName+ "/"+ServerContext.version+" - Error report " +
104 | "HTTP Status 404 - {} " +
105 | "type Status report
message {}
description " +
106 | "The requested resource is not available.
"+ ServerContext.serverName +" "+ServerContext.version+" " +
107 | "";
108 |
109 | public static final String textFormat_500 =
110 | "" +ServerContext.serverName+ "/"+ServerContext.version+" - Error report "
118 | + "HTTP Status 500 - An exception occurred processing {} "
119 | + "type Exception report
message An exception occurred processing {}
description "
120 | + "The server encountered an internal error that prevented it from fulfilling this request.
"
121 | + "Stacktrace:
" + "{} " + ""+ ServerContext.serverName +" "+ServerContext.version+" "
122 | + "";
123 |
124 |
125 |
126 | public static final String DEFAULT_PROTOCOL = "HTTP/1.1";
127 |
128 |
129 | public static String getResponseHead(int statusCode){
130 | if(statusCode == 200){
131 | return " OK";
132 | }else if(statusCode == 302){
133 | return " Found";
134 | }else if(statusCode == 404){
135 | return " Not Found";
136 | }else if(statusCode == 500){
137 | return " Internal Server Error";
138 | }
139 | return "";
140 | }
141 |
142 | }
143 |
--------------------------------------------------------------------------------
/src/com/yzb/http/HttpResponse.java:
--------------------------------------------------------------------------------
1 | package com.yzb.http;
2 |
3 | import cn.hutool.core.date.DateField;
4 | import cn.hutool.core.date.DateUtil;
5 | import cn.hutool.core.util.StrUtil;
6 | import cn.hutool.log.LogFactory;
7 | import com.yzb.common.Response;
8 |
9 | import javax.servlet.ServletOutputStream;
10 | import javax.servlet.http.Cookie;
11 | import java.io.ByteArrayOutputStream;
12 | import java.io.IOException;
13 | import java.io.PrintWriter;
14 | import java.net.Socket;
15 | import java.nio.charset.StandardCharsets;
16 | import java.text.SimpleDateFormat;
17 | import java.util.*;
18 |
19 | /**
20 | * @Description Handle HTTP response
21 | * @Date 2021/1/26 上午11:15
22 | * @Creater BeckoninGsy
23 | */
24 | public class HttpResponse extends Response {
25 |
26 | private Socket socket;
27 |
28 | private Map headers;
29 | private List cookieList;
30 | private int statusCode;
31 | private boolean commited;
32 | private boolean callGetOutputStream = false;
33 | private boolean callGetWriter = false;
34 | private ByteArrayOutputStream baos;
35 |
36 |
37 | public HttpResponse(Socket socket){
38 | headers = new HashMap<>();
39 | cookieList = new ArrayList<>();
40 | statusCode = HttpContant.RESPONSE_SC_OK;
41 | commited = false;
42 | this.socket = socket;
43 | baos = new ByteArrayOutputStream(){
44 | @Override
45 | public void flush() throws IOException {
46 | super.flush();
47 | HttpResponse.this.commitResponse();
48 | }
49 |
50 | @Override
51 | public void close() throws IOException {
52 | if(!isCommitted()) flush();
53 | super.close();
54 |
55 | }
56 | };
57 | }
58 |
59 | @Override
60 | public void addCookie(Cookie cookie) {
61 | cookieList.add(cookie);
62 | }
63 |
64 | public List getCookies(){
65 | return this.cookieList;
66 | }
67 |
68 |
69 | private String getCookiesHeader(){
70 | if(cookieList == null || cookieList.size() == 0) return "";
71 | String pattern = "EEE, d MMM yyyy HH:mm:ss 'GMT'";
72 | SimpleDateFormat sdf = new SimpleDateFormat(pattern, Locale.ENGLISH);
73 | StringBuilder sb = new StringBuilder();
74 | for(Cookie cookie : getCookies()){
75 | sb.append("Set-Cookie: ");
76 | sb.append(cookie.getName());
77 | sb.append("=");
78 | sb.append(cookie.getValue());
79 | if (-1 != cookie.getMaxAge()){
80 | sb.append("; Expires=");
81 | Date now = new Date();
82 | Date expire = DateUtil.offset(now, DateField.SECOND,cookie.getMaxAge());
83 | sb.append(sdf.format(expire));
84 | }
85 | if(null != cookie.getPath()){
86 | sb.append("; Path="+cookie.getPath());
87 | }
88 |
89 | sb.append(";");
90 | sb.append(HttpContant.LINE_TERMINATOR);
91 | LogFactory.get().info(cookie.getName()+"="+cookie.getValue()+";");
92 | }
93 | return sb.toString();
94 | }
95 |
96 | @Override
97 | public void setHeader(String s, String s1) {
98 | headers.put(s,s1);
99 | }
100 |
101 | @Override
102 | public void addHeader(String s, String s1) {
103 | setHeader(s,s1);
104 | }
105 |
106 | @Override
107 | public void setIntHeader(String s, int i) {
108 | setHeader(s,Integer.toString(i));
109 | }
110 |
111 | @Override
112 | public void addIntHeader(String s, int i) {
113 | setHeader(s,Integer.toString(i));
114 | }
115 |
116 | @Override
117 | public void setDateHeader(String s, long l) {
118 | TimeZone timeZone = TimeZone.getTimeZone("UTC");
119 | Calendar calendar = Calendar.getInstance(timeZone);
120 | calendar.setTimeInMillis(l);
121 | SimpleDateFormat simpleDateFormat =
122 | new SimpleDateFormat("EE MMM dd HH:mm:ss zzz yyyy", Locale.US);
123 | simpleDateFormat.setTimeZone(timeZone);
124 | setHeader(s,simpleDateFormat.format(calendar.getTime()));
125 | }
126 |
127 | @Override
128 | public void addDateHeader(String s, long l) {
129 | setDateHeader(s, l);
130 | }
131 |
132 | @Override
133 | public boolean containsHeader(String s) {
134 | return headers.containsKey(s);
135 | }
136 |
137 | @Override
138 | public void setStatus(int i) {
139 | statusCode = i;
140 | }
141 |
142 | @Override
143 | public int getStatus() {
144 | return statusCode;
145 | }
146 |
147 | @Override
148 | public String getHeader(String s) {
149 | return headers.get(s);
150 | }
151 |
152 | @Override
153 | public Collection getHeaders(String s) {
154 | return StrUtil.splitTrim(getHeader(s), HttpContant.HEADER_SEPARATOR);
155 | }
156 |
157 | @Override
158 | public Collection getHeaderNames() {
159 | return headers.keySet();
160 | }
161 |
162 |
163 | @Override
164 | public boolean isCommitted() {
165 | return commited;
166 | }
167 |
168 |
169 | @Override
170 | public void setCharacterEncoding(String s) {
171 | String contentType = headers.getOrDefault(HttpContant.HEADER_CONTENT_TYPE, HttpContant.DEFAULT_CONTENT_TYPE);
172 | if(StrUtil.indexOfIgnoreCase(contentType,HttpContant.CONTENT_TYPE_SEPARATOR) == -1) {
173 | setContentType(contentType +"; " + HttpContant.CONTENT_TYPE_SEPARATOR + s);
174 | } else {
175 | setContentType(StrUtil.subBefore(contentType,HttpContant.CONTENT_TYPE_SEPARATOR, false) + HttpContant.CONTENT_TYPE_SEPARATOR + s);
176 | }
177 | }
178 |
179 | @Override
180 | public void setContentLength(int i) {
181 | setHeader(HttpContant.HEADER_CONTENT_LENGTH, Integer.toString(i));
182 | }
183 |
184 | @Override
185 | public void setContentType(String s) {
186 | setHeader(HttpContant.HEADER_CONTENT_TYPE, s);
187 | }
188 |
189 | @Override
190 | public String getCharacterEncoding() {
191 | String contentType = getContentType();
192 | if(contentType == null) return HttpContant.DEFAULT_CHARACTERENCODING;
193 | return StrUtil.subAfter(contentType, HttpContant.CONTENT_TYPE_SEPARATOR, false);
194 | }
195 |
196 | @Override
197 | public String getContentType() {
198 | return getHeader(HttpContant.HEADER_CONTENT_TYPE);
199 | }
200 |
201 |
202 | @Override
203 | public void setBufferSize(int i) {
204 | if(isCommitted()) throw new IllegalStateException("can not set buffer size, because it has been commited!");
205 | baos = new ByteArrayOutputStream(i){
206 | @Override
207 | public void flush() throws IOException {
208 | super.flush();
209 | HttpResponse.this.commitResponse();
210 | }
211 |
212 | @Override
213 | public void close() throws IOException {
214 | if(!isCommitted()) flush();
215 | super.close();
216 |
217 | }
218 | };
219 | }
220 |
221 | @Override
222 | public int getBufferSize() {
223 | return baos.size();
224 | }
225 |
226 | @Override
227 | public void flushBuffer() throws IOException {
228 | baos.flush();
229 | }
230 |
231 | @Override
232 | public void resetBuffer() {
233 | if(isCommitted()) throw new IllegalStateException("can not reset buffer, because it has been commited!");
234 | baos.reset();
235 | }
236 |
237 | @Override
238 | public void reset() {
239 | if(isCommitted()) throw new IllegalStateException("can not reset response, because it has been commited!");
240 | baos.reset();
241 | headers.clear();
242 | statusCode = HttpContant.RESPONSE_SC_OK;
243 | callGetOutputStream = false;
244 | callGetWriter = false;
245 | }
246 |
247 |
248 | @Override
249 | public ServletOutputStream getOutputStream() throws IOException {
250 | if(callGetWriter) {
251 | throw new IllegalStateException("already call getOutputStream!");
252 | }
253 | return new ServletOutputStream() {
254 | @Override
255 | public void write(int b) throws IOException {
256 | baos.write(b);
257 | }
258 |
259 | @Override
260 | public void flush() throws IOException {
261 | baos.flush();
262 | }
263 |
264 | @Override
265 | public void close() throws IOException {
266 | baos.close();
267 | }
268 | } ;
269 | }
270 |
271 | @Override
272 | public PrintWriter getWriter() throws IOException {
273 | if(callGetOutputStream) {
274 | throw new IllegalStateException("already call getOutputStream!");
275 | }
276 | callGetWriter = true;
277 | return new PrintWriter(baos);
278 | }
279 |
280 | @Override
281 | public void sendRedirect(String s) throws IOException {
282 | if (callGetWriter || callGetOutputStream) {
283 | throw new IllegalStateException("already call getWriter or getOutputStream!");
284 | }
285 | setStatus(HttpContant.RESPONSE_SC_MOVED_TEMPORARILY);
286 | setHeader("Location", s);
287 | commitRespnseOnlyWithHeaders();
288 | }
289 |
290 |
291 | @Override
292 | public void sendError(int code, String s) throws IOException {
293 | if(isCommitted()) throw new IllegalStateException("can not send error, because it has been commited!");
294 | setStatus(code);
295 | setContentType(HttpContant.DEFAULT_CONTENT_TYPE);
296 | PrintWriter writer = getWriter();
297 | writer.println(s);
298 | writer.close();
299 | }
300 |
301 | @Override
302 | public void sendError(int code) throws IOException {
303 | sendError(code, StrUtil.format(HttpContant.textFormat_normal, code));
304 | }
305 |
306 |
307 | private String generateResponseHeader(){
308 | StringBuilder sb = new StringBuilder(64);
309 | sb.append(HttpContant.DEFAULT_PROTOCOL);
310 | sb.append(" ");
311 | sb.append(getStatus());
312 | sb.append(HttpContant.getResponseHead(getStatus()));
313 | sb.append(HttpContant.LINE_TERMINATOR);
314 | for (Map.Entry entry : headers.entrySet()){
315 | sb.append(entry.getKey());
316 | sb.append(HttpContant.LINE_SEPARATOR);
317 | sb.append(entry.getValue());
318 | sb.append(HttpContant.LINE_TERMINATOR);
319 | }
320 | sb.append(getCookiesHeader());
321 | sb.append(HttpContant.LINE_TERMINATOR);
322 | return sb.toString();
323 | }
324 |
325 | private void commitRespnseOnlyWithHeaders() throws IOException {
326 | String header = generateResponseHeader();
327 | socket.getOutputStream().write(header.getBytes(StandardCharsets.UTF_8));
328 | socket.getOutputStream().flush();
329 | commited = true;
330 | }
331 |
332 | private void commitResponse() throws IOException {
333 | setContentLength(baos.size());
334 | String header = generateResponseHeader();
335 | socket.getOutputStream().write(header.getBytes(StandardCharsets.UTF_8));
336 | socket.getOutputStream().write(baos.toByteArray());
337 | socket.getOutputStream().flush();
338 | commited = true;
339 | }
340 |
341 |
342 | }
343 |
--------------------------------------------------------------------------------
/src/com/yzb/common/Lifecycle.java:
--------------------------------------------------------------------------------
1 | package com.yzb.common;
2 |
3 | /**
4 | * @Description
5 | * @Date 2021/1/28 下午9:58
6 | * @Creater BeckoninGshy
7 | */
8 |
9 | import com.yzb.exception.LifecycleException;
10 |
11 | import java.io.FileNotFoundException;
12 |
13 | /**
14 | * Common interface for component life cycle methods. Catalina components
15 | * may implement this interface (as well as the appropriate interface(s) for
16 | * the functionality they support) in order to provide a consistent mechanism
17 | * to start and stop the component.
18 | *
19 | * The valid state transitions for components that support {@link Lifecycle}
20 | * are:
21 | *
22 | * start()
23 | * -----------------------------
24 | * | |
25 | * | init() |
26 | * NEW -»-- INITIALIZING |
27 | * | | | | ------------------«-----------------------
28 | * | | |auto | | |
29 | * | | \|/ start() \|/ \|/ auto auto stop() |
30 | * | | INITIALIZED --»-- STARTING_PREP --»- STARTING --»- STARTED --»--- |
31 | * | | | | |
32 | * | |destroy()| | |
33 | * | --»-----«-- ------------------------«-------------------------------- ^
34 | * | | | |
35 | * | | \|/ auto auto start() |
36 | * | | STOPPING_PREP ----»---- STOPPING ------»----- STOPPED -----»-----
37 | * | \|/ ^ | ^
38 | * | | stop() | | |
39 | * | | -------------------------- | |
40 | * | | | | |
41 | * | | | destroy() destroy() | |
42 | * | | FAILED ----»------ DESTROYING ---«----------------- |
43 | * | | ^ | |
44 | * | | destroy() | |auto |
45 | * | --------»----------------- \|/ |
46 | * | DESTROYED |
47 | * | |
48 | * | stop() |
49 | * ----»-----------------------------»------------------------------
50 | *
51 | * Any state can transition to FAILED.
52 | *
53 | * Calling start() while a component is in states STARTING_PREP, STARTING or
54 | * STARTED has no effect.
55 | *
56 | * Calling start() while a component is in state NEW will cause init() to be
57 | * called immediately after the start() method is entered.
58 | *
59 | * Calling stop() while a component is in states STOPPING_PREP, STOPPING or
60 | * STOPPED has no effect.
61 | *
62 | * Calling stop() while a component is in state NEW transitions the component
63 | * to STOPPED. This is typically encountered when a component fails to start and
64 | * does not start all its sub-components. When the component is stopped, it will
65 | * try to stop all sub-components - even those it didn't start.
66 | *
67 | * Attempting any other transition will throw {@link LifecycleException}.
68 | *
69 | *
70 | * The {@link LifecycleEvent}s fired during state changes are defined in the
71 | * methods that trigger the changed. No {@link LifecycleEvent}s are fired if the
72 | * attempted transition is not valid.
73 | *
74 | * @author Craig R. McClanahan
75 | */
76 | public interface Lifecycle {
77 |
78 |
79 | // ----------------------------------------------------- Manifest Constants
80 |
81 |
82 | /**
83 | * The LifecycleEvent type for the "component before init" event.
84 | */
85 | public static final String BEFORE_INIT_EVENT = "before_init";
86 |
87 |
88 | /**
89 | * The LifecycleEvent type for the "component after init" event.
90 | */
91 | public static final String AFTER_INIT_EVENT = "after_init";
92 |
93 |
94 | /**
95 | * The LifecycleEvent type for the "component start" event.
96 | */
97 | public static final String START_EVENT = "start";
98 |
99 |
100 | /**
101 | * The LifecycleEvent type for the "component before start" event.
102 | */
103 | public static final String BEFORE_START_EVENT = "before_start";
104 |
105 |
106 | /**
107 | * The LifecycleEvent type for the "component after start" event.
108 | */
109 | public static final String AFTER_START_EVENT = "after_start";
110 |
111 |
112 | /**
113 | * The LifecycleEvent type for the "component stop" event.
114 | */
115 | public static final String STOP_EVENT = "stop";
116 |
117 |
118 | /**
119 | * The LifecycleEvent type for the "component before stop" event.
120 | */
121 | public static final String BEFORE_STOP_EVENT = "before_stop";
122 |
123 |
124 | /**
125 | * The LifecycleEvent type for the "component after stop" event.
126 | */
127 | public static final String AFTER_STOP_EVENT = "after_stop";
128 |
129 |
130 | /**
131 | * The LifecycleEvent type for the "component after destroy" event.
132 | */
133 | public static final String AFTER_DESTROY_EVENT = "after_destroy";
134 |
135 |
136 | /**
137 | * The LifecycleEvent type for the "component before destroy" event.
138 | */
139 | public static final String BEFORE_DESTROY_EVENT = "before_destroy";
140 |
141 |
142 | /**
143 | * The LifecycleEvent type for the "periodic" event.
144 | */
145 | public static final String PERIODIC_EVENT = "periodic";
146 |
147 |
148 | /**
149 | * The LifecycleEvent type for the "configure_start" event. Used by those
150 | * components that use a separate component to perform configuration and
151 | * need to signal when configuration should be performed - usually after
152 | * {@link #BEFORE_START_EVENT} and before {@link #START_EVENT}.
153 | */
154 | public static final String CONFIGURE_START_EVENT = "configure_start";
155 |
156 |
157 | /**
158 | * The LifecycleEvent type for the "configure_stop" event. Used by those
159 | * components that use a separate component to perform configuration and
160 | * need to signal when de-configuration should be performed - usually after
161 | * {@link #STOP_EVENT} and before {@link #AFTER_STOP_EVENT}.
162 | */
163 | public static final String CONFIGURE_STOP_EVENT = "configure_stop";
164 |
165 |
166 | // --------------------------------------------------------- Public Methods
167 |
168 | /**
169 | * Prepare the component for starting. This method should perform any
170 | * initialization required post object creation. The following
171 | * {@link LifecycleEvent}s will be fired in the following order:
172 | *
173 | * INIT_EVENT: On the successful completion of component
174 | * initialization.
175 | *
176 | *
177 | * @exception LifecycleException if this component detects a fatal error
178 | * that prevents this component from being used
179 | */
180 | public void init() throws LifecycleException;
181 |
182 | /**
183 | * Prepare for the beginning of active use of the public methods other than
184 | * property getters/setters and life cycle methods of this component. This
185 | * method should be called before any of the public methods other than
186 | * property getters/setters and life cycle methods of this component are
187 | * utilized. The following {@link LifecycleEvent}s will be fired in the
188 | * following order:
189 | *
190 | * BEFORE_START_EVENT: At the beginning of the method. It is as this
191 | * point the state transitions to
192 | * {@link LifecycleState#STARTING_PREP}.
193 | * START_EVENT: During the method once it is safe to call start() for
194 | * any child components. It is at this point that the
195 | * state transitions to {@link LifecycleState#STARTING}
196 | * and that the public methods other than property
197 | * getters/setters and life cycle methods may be
198 | * used.
199 | * AFTER_START_EVENT: At the end of the method, immediately before it
200 | * returns. It is at this point that the state
201 | * transitions to {@link LifecycleState#STARTED}.
202 | *
203 | *
204 | *
205 | * @exception LifecycleException if this component detects a fatal error
206 | * that prevents this component from being used
207 | */
208 | public void start() throws LifecycleException;
209 |
210 |
211 | /**
212 | * Gracefully terminate the active use of the public methods other than
213 | * property getters/setters and life cycle methods of this component. Once
214 | * the STOP_EVENT is fired, the public methods other than property
215 | * getters/setters and life cycle methods should not be used. The following
216 | * {@link LifecycleEvent}s will be fired in the following order:
217 | *
218 | * BEFORE_STOP_EVENT: At the beginning of the method. It is at this
219 | * point that the state transitions to
220 | * {@link LifecycleState#STOPPING_PREP}.
221 | * STOP_EVENT: During the method once it is safe to call stop() for
222 | * any child components. It is at this point that the
223 | * state transitions to {@link LifecycleState#STOPPING}
224 | * and that the public methods other than property
225 | * getters/setters and life cycle methods may no longer be
226 | * used.
227 | * AFTER_STOP_EVENT: At the end of the method, immediately before it
228 | * returns. It is at this point that the state
229 | * transitions to {@link LifecycleState#STOPPED}.
230 | *
231 | *
232 | *
233 | * Note that if transitioning from {@link LifecycleState#FAILED} then the
234 | * three events above will be fired but the component will transition
235 | * directly from {@link LifecycleState#FAILED} to
236 | * {@link LifecycleState#STOPPING}, bypassing
237 | * {@link LifecycleState#STOPPING_PREP}
238 | *
239 | * @exception LifecycleException if this component detects a fatal error
240 | * that needs to be reported
241 | */
242 | public void stop() throws LifecycleException;
243 |
244 | /**
245 | * Prepare to discard the object. The following {@link LifecycleEvent}s will
246 | * be fired in the following order:
247 | *
248 | * DESTROY_EVENT: On the successful completion of component
249 | * destruction.
250 | *
251 | *
252 | * @exception LifecycleException if this component detects a fatal error
253 | * that prevents this component from being used
254 | */
255 | public void destroy() throws LifecycleException;
256 |
257 |
258 | /**
259 | * Obtain the current state of the source component.
260 | *
261 | * @return The current state of the source component.
262 | */
263 | public String getState();
264 |
265 | }
266 |
--------------------------------------------------------------------------------
/src/com/yzb/http/ApplicationContext.java:
--------------------------------------------------------------------------------
1 | package com.yzb.http;
2 |
3 | import cn.hutool.core.io.FileUtil;
4 | import cn.hutool.log.Log;
5 | import cn.hutool.log.LogFactory;
6 | import com.yzb.classcloader.WebappClassLoader;
7 | import com.yzb.common.Container;
8 | import com.yzb.common.ServerContext;
9 | import com.yzb.common.Service;
10 | import com.yzb.core.Engine;
11 | import com.yzb.core.StandardServletConfig;
12 | import com.yzb.core.StandardServletContext;
13 | import com.yzb.exception.LifecycleException;
14 | import org.jsoup.Jsoup;
15 | import org.jsoup.nodes.Document;
16 | import org.jsoup.nodes.Element;
17 | import org.jsoup.select.Elements;
18 |
19 | import javax.servlet.*;
20 | import java.io.File;
21 | import java.io.FileInputStream;
22 | import java.io.FileNotFoundException;
23 | import java.io.InputStream;
24 | import java.util.*;
25 |
26 | /**
27 | * @Description
28 | * @Date 2021/2/1 下午8:08
29 | * @Creater BeckoninGshy
30 | */
31 | public class ApplicationContext extends StandardServletContext {
32 |
33 | private Map servletNameToClass = new HashMap<>();
34 | private Map servletClassToName = new HashMap<>();
35 | private Map servletNameToURL = new HashMap<>();
36 | private Map servletURLToName = new HashMap<>();
37 | private Map mimeMapping = new HashMap<>();
38 | private Map initParameters = new HashMap<>();
39 | private Map> servletClassToInitParameters = new HashMap<>();
40 | private List loadOnStartupServletClassNames = new LinkedList<>();
41 | private List welcomeFileNames = new LinkedList<>();
42 | private Map, Servlet> servletPool = new HashMap<>();
43 | private WebappClassLoader webappClassLoader;
44 | private boolean isDefault = false;
45 | private ApplicationContext defaultContext = null;
46 |
47 |
48 |
49 | public ApplicationContext(String docBase, Boolean isDefault){
50 | ClassLoader commonClassLoader = Thread.currentThread().getContextClassLoader();
51 | this.webappClassLoader = new WebappClassLoader(docBase, commonClassLoader);
52 | this.isDefault = isDefault;
53 | }
54 |
55 | public WebappClassLoader getWebappClassLoader() {
56 | return webappClassLoader;
57 | }
58 |
59 | public String getServletURLToName(String url){
60 | return servletURLToName.get(url);
61 | }
62 |
63 | public String getServletNameToClass(String name){
64 | return servletNameToClass.get(name);
65 | }
66 |
67 | public String getServletURLToClass(String url) {
68 | return getServletNameToClass(getServletURLToName(url));
69 | }
70 |
71 | public String getServletClassToName(Class> clazz){
72 | return servletClassToName.get(clazz.getName());
73 | }
74 |
75 | @Override
76 | public ServletContext getContext(String URI) {
77 | Service service = getService();
78 | Container container = service.getContainer();
79 | Container[] servletContexts = null;
80 | Container rootContext = null;
81 | if(container instanceof Engine){
82 | servletContexts = ((Engine) container).getDefaultHost().findChildren();
83 | }
84 | assert servletContexts != null;
85 | for(Container servletContext : servletContexts){
86 | if(! (servletContext instanceof ApplicationContext)) continue;
87 | if(((ApplicationContext) servletContext).isDefaultContext()) continue;
88 | if(((ApplicationContext) servletContext).getPath().equals("/")) rootContext = servletContext;
89 | else if(URI.startsWith(((ApplicationContext) servletContext).getPath())) return (ApplicationContext) servletContext;
90 | }
91 | return (ApplicationContext) rootContext;
92 | }
93 |
94 | @Override
95 | public String getMimeType(String extension){
96 | if(mimeMapping.get(extension) == null) return getDefaultContext().getMimeType(extension);
97 | return mimeMapping.get(extension);
98 | }
99 |
100 | @Override
101 | public String getContextPath() {
102 | return getPath();
103 | }
104 |
105 | @Override
106 | public InputStream getResourceAsStream(String path) {
107 | String filePath = getRealPath() + path;
108 | File file = new File(filePath);
109 | InputStream is;
110 | try {
111 | is = new FileInputStream(file);
112 | } catch (FileNotFoundException e) {
113 | LogFactory.get().warn("get Resource failed, file is not found. path: {} ", path);
114 | return null;
115 | }
116 | return is;
117 | }
118 |
119 | @Override
120 | public String getServletContextName() {
121 | return getName();
122 | }
123 |
124 | @Override
125 | public void init() throws LifecycleException {
126 | try {
127 | File webInf = null;
128 | if(isDefault){
129 | webInf = new File(ServerContext.serverConfigDir);
130 | }else{
131 | webInf = new File(getRealPath(), "WEB-INF");
132 | if(!webInf.exists()) throw new FileNotFoundException("cannot find WEB-INF dir in current application directory");
133 | }
134 |
135 | File webXML = new File(webInf, "web.xml");
136 | String xml = FileUtil.readUtf8String(webXML);
137 | Document document = Jsoup.parse(xml);
138 |
139 | parseServlets(document);
140 | parseMimeAndMapping(document);
141 | parseLoadOnStartupServlet(document);
142 | parseWelcomeFileList(document);
143 |
144 | handleLoadOnStartupServlet();
145 | } catch (FileNotFoundException e) {
146 | LogFactory.get().error(e.getMessage());
147 | stop();
148 | return;
149 | }
150 | super.init();
151 | }
152 |
153 | @Override
154 | public void destroy() throws LifecycleException {
155 | for(Servlet s : servletPool.values()){
156 | s.destroy();
157 | }
158 | super.destroy();
159 | }
160 |
161 | @Override
162 | public Servlet getServlet(String s) throws ServletException {
163 | try {
164 | return getServlet(webappClassLoader.loadClass(getServletNameToClass(s)));
165 | } catch (IllegalAccessException | InstantiationException | ClassNotFoundException e) {
166 | LogFactory.get().error("get servlet {} failed!", s);
167 | }
168 | return null;
169 | }
170 |
171 | public synchronized Servlet getServlet(Class> clazz) throws ServletException, IllegalAccessException, InstantiationException {
172 | Servlet s = servletPool.get(clazz);
173 | if(s == null){
174 | s = (Servlet) clazz.newInstance();
175 | ServletConfig sc = new StandardServletConfig(this, getServletClassToName(clazz), servletClassToInitParameters.getOrDefault(clazz.getName(), new HashMap<>()));
176 | s.init(sc);
177 | servletPool.put(clazz, s);
178 | }
179 | return s;
180 | }
181 |
182 | @Override
183 | public String getInitParameter(String s) {
184 | return initParameters.get(s);
185 | }
186 |
187 | @Override
188 | public Enumeration getInitParameterNames() {
189 | return Collections.enumeration(initParameters.keySet());
190 | }
191 |
192 | @Override
193 | public boolean setInitParameter(String key, String value) {
194 | initParameters.put(key, value);
195 | return true;
196 | }
197 |
198 | @Override
199 | public RequestDispatcher getRequestDispatcher(String uri) {
200 | return new ApplicationRequestDispatcher(uri, this);
201 | }
202 |
203 | public boolean isDefaultContext(){
204 | return isDefault;
205 | }
206 |
207 | public void setDefaultContext(ApplicationContext defaultContext){
208 | this.defaultContext = defaultContext;
209 | }
210 |
211 | public ApplicationContext getDefaultContext(){
212 | return defaultContext;
213 | }
214 |
215 | public List getWelcomeFileNames(){
216 | return welcomeFileNames;
217 | }
218 |
219 | public InputStream getWelcomFile(){
220 | for(String name: welcomeFileNames){
221 | InputStream is = getResourceAsStream("/" + name);
222 | if(is != null) return is;
223 | }
224 | if(!isDefault) {
225 | for(String name: getDefaultContext().getWelcomeFileNames()){
226 | InputStream is = getResourceAsStream("/" + name);
227 | if(is != null) return is;
228 | }
229 | }
230 | return null;
231 | }
232 |
233 |
234 | private void parseServlets(Document document) throws FileNotFoundException {
235 |
236 | Elements servlets = document.select("servlet");
237 | for(Element servlet : servlets){
238 | String name = servlet.select("servlet-name").text();
239 | String clazz = servlet.select("servlet-class").text();
240 | assert name != null;
241 | assert clazz != null;
242 | servletNameToClass.put(name, clazz);
243 | servletClassToName.put(clazz, name);
244 |
245 | // process init-param
246 | Elements initParams = servlet.select("init-param");
247 | if(initParams == null || initParams.size() == 0) continue;
248 | Map inits = new HashMap<>();
249 | for(Element initParam : initParams){
250 | String initParamValue = initParam.select("param-value").text();
251 | String initParamName = initParam.select("param-name").text();
252 | inits.put(initParamName,initParamValue);
253 | }
254 | servletClassToInitParameters.put(clazz, inits);
255 | }
256 |
257 | Elements servletMappings = document.select("servlet-mapping");
258 | for(Element servlet : servletMappings){
259 | String name = servlet.select("servlet-name").text();
260 | String url = servlet.select("url-pattern").text();
261 | assert name != null;
262 | assert url != null;
263 | servletNameToURL.put(name, url);
264 | servletURLToName.put(url, name);
265 | }
266 | }
267 |
268 | private void parseMimeAndMapping(Document document) throws FileNotFoundException {
269 | Elements mimeMappings = document.select("mime-mapping");
270 | for(Element mime : mimeMappings){
271 | String extension = mime.select("extension").text();
272 | String mimeType = mime.select("mime-type").text();
273 | mimeMapping.put(extension, mimeType);
274 | }
275 |
276 | }
277 |
278 | private void parseLoadOnStartupServlet(Document document){
279 | Elements loadOnStartups = document.select("load-on-startup");
280 | for (Element loadOnStartup : loadOnStartups){
281 | Elements servletName = loadOnStartup.parent().select("servlet-name");
282 | loadOnStartupServletClassNames.add(servletName.text());
283 | }
284 | }
285 |
286 | private void parseWelcomeFileList(Document document){
287 | Elements welcomeFiles = document.select("welcome-file-list welcome-file");
288 | for (Element welcomeFile : welcomeFiles){
289 | welcomeFileNames.add(welcomeFile.text());
290 | }
291 | }
292 |
293 | private void handleLoadOnStartupServlet(){
294 | for(String name: loadOnStartupServletClassNames){
295 | try {
296 | getServlet(name);
297 | } catch (ServletException e) {
298 | LogFactory.get().error("get servlet {} failed at load on startup", name);
299 | }
300 | }
301 | }
302 | }
303 |
--------------------------------------------------------------------------------
/src/com/yzb/http/HttpRequest.java:
--------------------------------------------------------------------------------
1 | package com.yzb.http;
2 |
3 | import cn.hutool.core.io.IoUtil;
4 | import cn.hutool.core.util.ArrayUtil;
5 | import cn.hutool.core.util.StrUtil;
6 | import com.yzb.common.Connector;
7 | import com.yzb.exception.ParseHttpRequestException;
8 | import com.yzb.common.Request;
9 |
10 | import javax.servlet.RequestDispatcher;
11 | import javax.servlet.ServletContext;
12 | import javax.servlet.ServletInputStream;
13 | import javax.servlet.http.Cookie;
14 | import javax.servlet.http.HttpSession;
15 | import java.io.*;
16 | import java.net.Socket;
17 | import java.nio.charset.StandardCharsets;
18 | import java.util.*;
19 |
20 | /**
21 | * @description: 处理HTTP请求的实现类
22 | * @author: BeckoninGshy
23 | * @create: 2021/1/24 16:17
24 | */
25 | public class HttpRequest extends Request {
26 |
27 | private String requestContent;
28 | private String outlineMessage;
29 | private String forwardURI;
30 |
31 | private String charsetName = StandardCharsets.UTF_8.name(); //默认编码为utf-8
32 |
33 | private final Map headers;
34 | private final Map parameterMap;
35 | private Cookie[] cookies;
36 | private HttpSession session;
37 |
38 | private final Socket socket;
39 | private final Connector connector;
40 | private ApplicationContext servletContext;
41 |
42 | private boolean forwarding = false;
43 |
44 | public HttpRequest(Socket socket, Connector connector) throws ParseHttpRequestException {
45 | this.socket = socket;
46 | this.connector = connector;
47 |
48 | headers = new HashMap<>();
49 | parameterMap = new HashMap<>();
50 | cookies = new Cookie[0];
51 |
52 | parseHttpRequestContent();
53 | parseHttpRequestHeader();
54 | parseHttpRequestParameter();
55 | parseCookies();
56 |
57 | if(connector instanceof HttpConnector){
58 | this.servletContext = ((HttpConnector) connector).getServletContext(getRequestURI());
59 | if(null == this.servletContext) throw new ParseHttpRequestException("get servlet context failed");
60 | }
61 |
62 | }
63 |
64 | @Override
65 | public Cookie[] getCookies() {
66 | return cookies;
67 | }
68 |
69 | public String getJSessionIdFromCookie(){
70 | if (null==cookies){
71 | return null;
72 | }
73 | for (Cookie cookie:cookies){
74 | if ("JSESSIONID".equals(cookie.getName())){
75 | return cookie.getValue();
76 | }
77 | }
78 | return null;
79 | }
80 |
81 | @Override
82 | public HttpSession getSession() {
83 | return session;
84 | }
85 |
86 | public void setSession(HttpSession session){
87 | this.session = session;
88 | }
89 |
90 | @Override
91 | public long getDateHeader(String s) {
92 | if(headers.containsKey(s)) return -1;
93 | Date date = null;
94 | try{
95 | date = new Date(headers.get(s).trim());
96 | } catch (IllegalArgumentException e){
97 | return -1;
98 | }
99 | return date.getTime();
100 | }
101 |
102 | @Override
103 | public String getHeader(String s) {
104 | return headers.get(s);
105 | }
106 |
107 | @Override
108 | public Enumeration getHeaders(String s) {
109 | return Collections.enumeration(StrUtil.splitTrim(headers.get(s), HttpContant.HEADER_SEPARATOR));
110 | }
111 |
112 | @Override
113 | public Enumeration getHeaderNames() {
114 | return Collections.enumeration(headers.keySet());
115 | }
116 |
117 | @Override
118 | public int getIntHeader(String s) {
119 | return Integer.parseInt(headers.get(s));
120 | }
121 |
122 | public String getRequestContent(){
123 | return requestContent;
124 | }
125 |
126 | public String getOutlineMessage(){
127 | return outlineMessage;
128 | }
129 |
130 | @Override
131 | public String getMethod(){
132 | return StrUtil.subBefore(outlineMessage, " ", false);
133 | }
134 |
135 | @Override
136 | public String getProtocol() {
137 | return outlineMessage.substring(outlineMessage.lastIndexOf(' ')+1);
138 | }
139 |
140 | @Override
141 | public String getScheme() {
142 | return StrUtil.subBefore(getProtocol(), "/", false).toLowerCase();
143 | }
144 |
145 |
146 | private String getWholeRequestURL(){
147 | return StrUtil.subBetween(outlineMessage, " ", " ");
148 | }
149 |
150 | @Override
151 | public String getQueryString() {
152 | return StrUtil.subAfter(getWholeRequestURL(), "?", false);
153 | }
154 |
155 | public void setForwardURI(String uri){
156 | this.forwardURI = servletContext.getContextPath() + uri;
157 | }
158 |
159 | public void setForwarding(){
160 | forwarding = true;
161 | }
162 |
163 | public void setForwarded(){
164 | forwarding = false;
165 | }
166 |
167 | public boolean isForwarding(){
168 | return forwarding;
169 | }
170 |
171 | @Override
172 | public String getRequestURI() {
173 | if(isForwarding()) return forwardURI;
174 | return StrUtil.subBefore(getWholeRequestURL(), "?", false);
175 | }
176 |
177 | @Override
178 | public StringBuffer getRequestURL() {
179 | StringBuffer sb = new StringBuffer();
180 | sb.append(getScheme());
181 | sb.append("://");
182 | sb.append(getLocalName());
183 | sb.append(":");
184 | sb.append(getLocalPort());
185 | sb.append(getRequestURI());
186 | return sb;
187 | }
188 |
189 | @Override
190 | public RequestDispatcher getRequestDispatcher(String s) {
191 | return new ApplicationRequestDispatcher(s);
192 | }
193 |
194 | @Override
195 | public String getParameter(String s) {
196 | if(getParameterValues(s) == null) return null;
197 | return getParameterValues(s)[0];
198 | }
199 |
200 | @Override
201 | public Enumeration getParameterNames() {
202 | return Collections.enumeration(parameterMap.keySet());
203 | }
204 |
205 | @Override
206 | public String[] getParameterValues(String s) {
207 | return parameterMap.getOrDefault(s,null);
208 | }
209 |
210 | @Override
211 | public Map getParameterMap() {
212 | return parameterMap;
213 | }
214 |
215 | @Override
216 | public String getCharacterEncoding() {
217 | return charsetName;
218 | }
219 |
220 | @Override
221 | public void setCharacterEncoding(String s) throws UnsupportedEncodingException {
222 | charsetName = s;
223 | requestContent = new String( s.getBytes(StandardCharsets.UTF_8.name()) , charsetName);
224 | }
225 |
226 | @Override
227 | public int getContentLength() {
228 | String len = null;
229 | if((len = getHeader(HttpContant.HEADER_CONTENT_LENGTH)) == null) return -1;
230 | return Integer.parseInt(len);
231 | }
232 |
233 | @Override
234 | public String getContentType() {
235 | return getHeader(HttpContant.HEADER_CONTENT_TYPE);
236 | }
237 |
238 | @Override
239 | public ServletInputStream getInputStream() throws IOException {
240 | byte[] bytes = getHttpRequestBodyString().getBytes(charsetName);
241 | ByteArrayInputStream byteArrayInputStream = IoUtil.toStream(bytes);
242 | return new ServletInputStream(){
243 | public int read() {
244 | return byteArrayInputStream.read();
245 | }
246 | };
247 | }
248 |
249 | @Override
250 | public BufferedReader getReader() throws IOException {
251 | return new BufferedReader(new InputStreamReader(getInputStream(),charsetName));
252 | }
253 |
254 |
255 | @Override
256 | public String getRemoteAddr() {
257 | return socket.getInetAddress().getHostAddress();
258 | }
259 |
260 | @Override
261 | public String getRemoteHost() {
262 | return socket.getInetAddress().getHostName();
263 | }
264 |
265 | @Override
266 | public int getRemotePort() {
267 | return socket.getPort();
268 | }
269 |
270 | @Override
271 | public String getLocalName() {
272 | return socket.getLocalAddress().getHostName();
273 | }
274 |
275 | @Override
276 | public String getLocalAddr() {
277 | return socket.getLocalAddress().getHostAddress();
278 | }
279 |
280 | @Override
281 | public int getLocalPort() {
282 | return socket.getLocalPort();
283 | }
284 |
285 |
286 | @Override
287 | public String getServerName() {
288 | return connector.getServer().getName();
289 | }
290 |
291 | @Override
292 | public int getServerPort() {
293 | return connector.getPort();
294 | }
295 |
296 | @Override
297 | public ServletContext getServletContext() {
298 | return servletContext;
299 | }
300 |
301 | public void setServletContext(ServletContext servletContext){
302 | this.servletContext = (ApplicationContext) servletContext;
303 | }
304 |
305 |
306 | public Socket getSocket() {
307 | return socket;
308 | }
309 |
310 | private void parseHttpRequestContent() throws ParseHttpRequestException {
311 |
312 | ByteArrayOutputStream baso = null;
313 | try {
314 | InputStream inputStream = socket.getInputStream();
315 | int bufferSize = 1024;
316 | byte[] buffer = new byte[1024];
317 | baso = new ByteArrayOutputStream();
318 | while (true) {
319 | int len = inputStream.read(buffer);
320 | if (len == -1) break;
321 | baso.write(buffer, 0, len);
322 | if (len != bufferSize) break;
323 | }
324 | requestContent = baso.toString(charsetName);
325 | } catch (IOException e) {
326 | throw new ParseHttpRequestException("解析Http请求错误:读取socket输入流错误");
327 | } finally {
328 | try {
329 | baso.close();
330 | } catch (IOException e) {
331 | e.printStackTrace();
332 | }
333 | }
334 | }
335 |
336 | private void parseHttpRequestHeader() throws ParseHttpRequestException {
337 | String[] headerStrings = getHttpRequestHeaderString().split(HttpContant.LINE_TERMINATOR);
338 | outlineMessage = headerStrings[0];
339 | for(int i = 1, n = headerStrings.length; i < n; i++){
340 | String[] line = headerStrings[i].split(HttpContant.LINE_SEPARATOR);
341 | if(line.length != 2)
342 | throw new ParseHttpRequestException("解析请求头部错误!");
343 | headers.put(line[0].trim(), line[1].trim());
344 | }
345 | }
346 |
347 | private void parseHttpRequestParameter() throws ParseHttpRequestException {
348 | String parameterString = "";
349 | if (HttpContant.REQUEST_METHOD_GET.equals(getMethod())) {
350 | parameterString = getQueryString();
351 | }else if(HttpContant.REQUEST_METHOD_POST.equals(getMethod())) { //仅支持单次请求。例如表单提交
352 | parameterString = getHttpRequestBodyString();
353 | }
354 |
355 | if(StrUtil.isEmpty(parameterString)) return;
356 |
357 | String[] parameters = parameterString.split(HttpContant.PARAMATERS_SEPARATOR);
358 |
359 | Map> parameterKVs = new HashMap<>();
360 | for(int i = 0, n = parameters.length; i < n; i++){
361 | String[] entries = parameters[i].split(HttpContant.PARAMATERSKV_SEPARATOR);
362 | if(entries.length != 2)
363 | throw new ParseHttpRequestException("解析请求参数错误!");
364 | List values = parameterKVs.getOrDefault(entries[0], new ArrayList<>());
365 | values.add(entries[1]);
366 | parameterKVs.put(entries[0], values);
367 | }
368 | // add values to origin map
369 | for(Map.Entry> entry : parameterKVs.entrySet()) {
370 | String[] origin = parameterMap.getOrDefault(entry.getKey(), new String[0]);
371 | String[] add = entry.getValue().toArray(new String[0]);
372 | int len = origin.length + add.length;
373 | String[] result = Arrays.copyOf(origin, len);
374 | System.arraycopy(add, 0, result, origin.length, add.length);
375 | parameterMap.put(entry.getKey(), result);
376 | }
377 | }
378 |
379 | private void parseCookies(){
380 | String cookiesStr = headers.get("Cookie");
381 | if(null == cookiesStr) return;
382 | List cookieList = new ArrayList<>();
383 | String[] pairs = StrUtil.split(cookiesStr,";");
384 | for(String pair : pairs){
385 | if(StrUtil.isBlank(pair)){
386 | continue;
387 | }
388 | String[] cookieKeyAndValues = StrUtil.split(pair,"=");
389 | String key = cookieKeyAndValues[0].trim();
390 | String value = cookieKeyAndValues[1].trim();
391 | Cookie cookie = new Cookie(key, value);
392 | cookieList.add(cookie);
393 | }
394 | this.cookies = ArrayUtil.toArray(cookieList, Cookie.class);
395 | }
396 |
397 | private String getHttpRequestHeaderString(){
398 | return StrUtil.subBefore(requestContent, HttpContant.SEPARATOR_LINES,false);
399 | }
400 |
401 | private String getHttpRequestBodyString(){
402 | return StrUtil.subAfter(requestContent, HttpContant.SEPARATOR_LINES,false);
403 | }
404 | }
405 |
--------------------------------------------------------------------------------