├── .gitignore ├── README.md ├── core ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── lws │ │ └── lwebserver │ │ └── core │ │ ├── BootStrap.java │ │ ├── constant │ │ ├── CharConstant.java │ │ ├── CharsetProperties.java │ │ └── Const.java │ │ ├── context │ │ ├── ServletContext.java │ │ ├── WebApplication.java │ │ └── holder │ │ │ ├── FilterHolder.java │ │ │ └── ServletHolder.java │ │ ├── cookie │ │ └── Cookie.java │ │ ├── enumeration │ │ ├── HttpStatus.java │ │ ├── ModelScope.java │ │ └── RequestMethod.java │ │ ├── exception │ │ ├── FilterNotFoundException.java │ │ ├── RequestInvalidException.java │ │ ├── RequestParseException.java │ │ ├── ResourceNotFoundException.java │ │ ├── ServerErrorException.java │ │ ├── ServletNotFoundException.java │ │ ├── TemplateResolveException.java │ │ ├── base │ │ │ └── ServletException.java │ │ └── handler │ │ │ └── ExceptionHandler.java │ │ ├── fliter │ │ ├── Filter.java │ │ └── FilterChain.java │ │ ├── listener │ │ ├── HttpSessionListener.java │ │ ├── ServletContextListener.java │ │ ├── ServletRequestListener.java │ │ └── event │ │ │ ├── HttpSessionEvent.java │ │ │ ├── ServletContextEvent.java │ │ │ └── ServletRequestEvent.java │ │ ├── net │ │ ├── base │ │ │ └── SocketProcessorBase.java │ │ ├── cleaner │ │ │ └── IdleConnectionCleaner.java │ │ ├── dispatcher │ │ │ ├── AbstractDispatcher.java │ │ │ ├── aio │ │ │ │ └── AioDispatcher.java │ │ │ ├── bio │ │ │ │ └── BioDispatcher.java │ │ │ └── nio │ │ │ │ └── NioDispatcher.java │ │ ├── endpoint │ │ │ ├── AbstractEndpoint.java │ │ │ ├── aio │ │ │ │ └── AioEndpoint.java │ │ │ ├── bio │ │ │ │ └── BioEndpoint.java │ │ │ └── nio │ │ │ │ ├── NioEndpoint.java │ │ │ │ └── Poller.java │ │ ├── handler │ │ │ ├── AbstractRequestHandler.java │ │ │ ├── aio │ │ │ │ └── AioRequestHandler.java │ │ │ ├── bio │ │ │ │ └── BioRequestHandler.java │ │ │ └── nio │ │ │ │ └── NioRequestHandler.java │ │ └── wrapper │ │ │ ├── SocketWrapperBase.java │ │ │ ├── aio │ │ │ └── AioSocketWrapper.java │ │ │ ├── bio │ │ │ └── BioSocketWrapper.java │ │ │ └── nio │ │ │ └── NioSocketWrapper.java │ │ ├── request │ │ ├── Request.java │ │ └── dispatcher │ │ │ ├── RequestDispatcher.java │ │ │ └── impl │ │ │ └── ApplicationRequestDispatcher.java │ │ ├── resource │ │ └── ResourceHandler.java │ │ ├── response │ │ ├── Header.java │ │ └── Response.java │ │ ├── servlet │ │ ├── Servlet.java │ │ └── impl │ │ │ ├── DefaultServlet.java │ │ │ └── HttpServlet.java │ │ ├── session │ │ ├── HttpSession.java │ │ └── IdleSessionCleaner.java │ │ ├── template │ │ └── TemplateResolver.java │ │ └── util │ │ ├── IOUtil.java │ │ ├── MimeTypeUtil.java │ │ ├── PropertyUtil.java │ │ ├── SynchronizedQueue.java │ │ ├── UUIDUtil.java │ │ └── XMLUtil.java │ ├── resources │ └── log4j.properties │ └── webapp │ └── errors │ ├── 400.html │ ├── 404.html │ └── 500.html ├── example ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── lws │ │ └── lwebserver │ │ └── example │ │ ├── Main.java │ │ ├── model │ │ └── User.java │ │ ├── service │ │ └── UserService.java │ │ └── web │ │ ├── filter │ │ ├── LogFilter.java │ │ └── LoginFilter.java │ │ ├── listener │ │ ├── MyServletRequestListener.java │ │ └── ServletContextAndSessionListener.java │ │ └── servlet │ │ ├── LoginServlet.java │ │ ├── LogoutServlet.java │ │ ├── UserEditServlet.java │ │ └── UserServlet.java │ ├── resources │ ├── log4j.properties │ └── server.properties │ └── webapp │ ├── WEB-INF │ └── web.xml │ ├── index.html │ ├── static │ └── favicon.ico │ └── views │ ├── login.html │ ├── logout.html │ ├── success.html │ └── user.html └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Maven 3 | target/ 4 | 5 | # IntelliJ 6 | *.iml 7 | *.ipr 8 | *.iws 9 | .idea/ 10 | out/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LWebServer 2 | `简化版Tomcat` ,可以配置BIO,NIO,AIO三种模式,实现Cookie和Session的会话追踪,请求响应的封装,Servlet的调度,长连接等Web服务器基本功能。 3 | 4 | 基于Java BIO/NIO/AIO(NIO2)包、多线程、Socket网络编程、XML解析、log4j/slf4j日志 5 | 6 | NIO的实现参照的是Tomcat的源码 7 | 8 | ## 学习内容: 9 | `IO模式` / `Socket` / `TCP/IP` / `框架源码(Tomcat,Spring,Netty等)` / `高级网络编程` / `线程模型` 10 | 11 | ## 实现功能: 12 | - BIO NIO AIO `NIO使用的是IO多路复用的Select机制` 13 | 14 | - Reactor 模式 15 | 16 | - HTTP Protocol `Keep-Alive` 17 | 18 | - Servlet `ServletContext` `Dispatcher` 19 | 20 | - Request&Response 21 | 22 | - Forward&Redirect 23 | 24 | - session&cookie 25 | 26 | 27 | 28 | ## 解决问题 29 | 30 | - AIO的高错误率,backlog参数的调节[issue](https://github.com/defineYIDA/LWebServer/issues/4) 31 | - NIO下对Socket实现长连接(keep-alive),并设置心跳定时清理 32 | - 分析多余的socket连接[issue](https://github.com/defineYIDA/LWebServer/issues/3) 33 | 34 | ## 准备添加的功能: 35 | 36 | - SSL 37 | - limtlatch 38 | - 辅Selector 39 | ## 博客 40 | [关于专栏-------WebServer二三事](https://blog.csdn.net/define_LIN/article/details/89040929) 41 | 42 | [WebServer二三事(一)Socket编程说起](https://blog.csdn.net/define_LIN/article/details/89304687) 43 | 44 | [WebServer二三事(二)五种网络I/O模式](https://blog.csdn.net/define_LIN/article/details/89705770) 45 | 46 | [同步/异步,阻塞/非阻塞你真的理解了吗?](https://blog.csdn.net/define_LIN/article/details/89724421) 47 | -------------------------------------------------------------------------------- /core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | LWebServer 7 | com.lws 8 | 1.0-SNAPSHOT 9 | 10 | 11 | jar 12 | 4.0.0 13 | 14 | 15 | UTF-8 16 | 1.7.25 17 | 5.0.5.RELEASE 18 | 19 | 20 | core 21 | 22 | 23 | junit 24 | junit 25 | 4.12 26 | test 27 | 28 | 29 | 30 | org.projectlombok 31 | lombok 32 | 1.16.18 33 | provided 34 | 35 | 36 | org.slf4j 37 | slf4j-log4j12 38 | ${slf4j.version} 39 | provided 40 | 41 | 42 | 43 | eu.medsea.mimeutil 44 | mime-util 45 | 2.1.3 46 | 47 | 48 | 49 | dom4j 50 | dom4j 51 | 1.6.1 52 | 53 | 54 | 55 | org.springframework 56 | spring-core 57 | [5.0.7,) 58 | provided 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | src/main/webapp 67 | 68 | **/*.* 69 | 70 | false 71 | 72 | 73 | src/main/resources 74 | 75 | **/*.* 76 | 77 | false 78 | 79 | 80 | 81 | 82 | org.apache.maven.plugins 83 | maven-compiler-plugin 84 | 85 | 1.8 86 | 1.8 87 | UTF-8 88 | 89 | 90 | 91 | org.apache.maven.plugins 92 | maven-surefire-plugin 93 | 2.20 94 | 95 | true 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/BootStrap.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core; 2 | 3 | import com.lws.lwebserver.core.net.endpoint.AbstractEndpoint; 4 | import com.lws.lwebserver.core.util.PropertyUtil; 5 | 6 | import java.util.Scanner; 7 | 8 | /** 9 | * @author ZL 10 | */ 11 | public class BootStrap { 12 | /** 13 | * 服务器入口(启动引导) 14 | */ 15 | public static void run() { 16 | String port = PropertyUtil.getProperty("server.port");//获得服务器端口 17 | if (null == port) { 18 | throw new IllegalArgumentException("server.port 不存在"); 19 | } 20 | //获得当前设置的 IO模式 21 | String connector = PropertyUtil.getProperty("server.connector"); 22 | if (connector == null || 23 | (!connector.equalsIgnoreCase("bio") 24 | && !connector.equalsIgnoreCase("nio") 25 | && !connector.equalsIgnoreCase("aio")) 26 | ) { 27 | throw new IllegalArgumentException("server.network 不存在或不符合规范"); 28 | } 29 | //获得具体io模式对应的endpoint实例 30 | AbstractEndpoint server = AbstractEndpoint.getInstance(connector); 31 | server.start(Integer.parseInt(port)); 32 | Scanner scanner = new Scanner(System.in); 33 | String order; 34 | while (scanner.hasNext()) { 35 | order = scanner.next(); 36 | if (order.equals("EXIT")) { 37 | server.close(); 38 | System.exit(0); 39 | } 40 | } 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/constant/CharConstant.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.constant; 2 | 3 | /** 4 | * Created by zl on 2019/03/01. 5 | */ 6 | public class CharConstant { 7 | public static final String BLANK = " "; 8 | public static final String CRLF = "\r\n"; 9 | } 10 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/constant/CharsetProperties.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.constant; 2 | 3 | import java.nio.charset.Charset; 4 | 5 | /** 6 | * Created by zl on 2019/03/01. 7 | */ 8 | public class CharsetProperties { 9 | public static final Charset UTF_8_CHARSET = Charset.forName("UTF-8"); 10 | public static final String UTF_8 = "UTF-8"; 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/constant/Const.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.constant; 2 | 3 | /** 4 | * Created by zl on 2019/03/01. 5 | */ 6 | public class Const { 7 | public static final String ERROR_PAGE = "/errors/%s.html"; 8 | public static final String DEFAULT_CONTENT_TYPE = "text/html;charset=utf-8"; 9 | public static final String DEFAULT_SERVLET_ALIAS = "DefaultServlet"; 10 | /** 11 | * 300s 5min 过期 12 | */ 13 | public static final int DEFAULT_SESSION_EXPIRE_TIME = 300; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/context/ServletContext.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.context; 2 | 3 | import com.lws.lwebserver.core.context.holder.FilterHolder; 4 | import com.lws.lwebserver.core.context.holder.ServletHolder; 5 | import com.lws.lwebserver.core.cookie.Cookie; 6 | import com.lws.lwebserver.core.exception.ServletNotFoundException; 7 | import com.lws.lwebserver.core.exception.FilterNotFoundException; 8 | import com.lws.lwebserver.core.fliter.Filter; 9 | import com.lws.lwebserver.core.listener.HttpSessionListener; 10 | import com.lws.lwebserver.core.listener.ServletContextListener; 11 | import com.lws.lwebserver.core.listener.ServletRequestListener; 12 | import com.lws.lwebserver.core.listener.event.HttpSessionEvent; 13 | import com.lws.lwebserver.core.listener.event.ServletContextEvent; 14 | import com.lws.lwebserver.core.listener.event.ServletRequestEvent; 15 | import com.lws.lwebserver.core.request.Request; 16 | import com.lws.lwebserver.core.response.Response; 17 | import com.lws.lwebserver.core.servlet.Servlet; 18 | import com.lws.lwebserver.core.session.HttpSession; 19 | import com.lws.lwebserver.core.session.IdleSessionCleaner; 20 | import com.lws.lwebserver.core.util.UUIDUtil; 21 | import com.lws.lwebserver.core.util.XMLUtil; 22 | import lombok.Data; 23 | import lombok.extern.slf4j.Slf4j; 24 | import org.dom4j.Document; 25 | import org.dom4j.Element; 26 | import org.springframework.util.AntPathMatcher; 27 | 28 | import java.time.Duration; 29 | import java.time.Instant; 30 | import java.util.*; 31 | import java.util.concurrent.ConcurrentHashMap; 32 | import java.util.stream.Collectors; 33 | 34 | import static com.lws.lwebserver.core.constant.Const.DEFAULT_SERVLET_ALIAS; 35 | import static com.lws.lwebserver.core.constant.Const.DEFAULT_SESSION_EXPIRE_TIME; 36 | 37 | /** 38 | * @Author: zl 39 | * @Date: 2019/3/16 11:48 40 | */ 41 | @Data 42 | @Slf4j 43 | public class ServletContext { 44 | /** 45 | * 别名->类名 46 | * 一个Servlet类只能有一个Servlet别名,一个Servlet别名只能对应一个Servlet类 47 | */ 48 | private Map servlets; 49 | /** 50 | * 一个Servlet可以对应多个URL,一个URL只能对应一个Servlet 51 | * URL Pattern -> Servlet别名 52 | */ 53 | private Map servletMapping; 54 | 55 | 56 | /** 57 | * 别名->类名 58 | */ 59 | private Map filters; 60 | /** 61 | * URL Pattern -> 别名列表,注意同一个URLPattern可以对应多个Filter,但只能对应一个Servlet 62 | */ 63 | private Map> filterMapping; 64 | 65 | /** 66 | * 监听器们 67 | */ 68 | private List servletContextListeners; 69 | private List httpSessionListeners; 70 | private List servletRequestListeners; 71 | 72 | /** 73 | * 域 74 | */ 75 | private Map attributes; 76 | /** 77 | * 整个应用对应的session们 78 | */ 79 | private Map sessions; 80 | /** 81 | * 路径匹配器,由Spring提供 82 | */ 83 | private AntPathMatcher matcher;//TODO 84 | 85 | private IdleSessionCleaner idleSessionCleaner;//定时清理Session 86 | 87 | 88 | public ServletContext() throws IllegalAccessException, ClassNotFoundException, InstantiationException { 89 | init(); 90 | } 91 | 92 | /** 93 | * 由URL得到对应的一个Servlet实例 94 | * 95 | * @param url 96 | * @return 97 | * @throws ServletNotFoundException 98 | */ 99 | public Servlet mapServlet(String url) throws ServletNotFoundException { 100 | // 1、精确匹配 101 | String servletAlias = servletMapping.get(url); 102 | if (servletAlias != null) { 103 | return initAndGetServlet(servletAlias); 104 | } 105 | // 2、路径匹配 106 | List matchingPatterns = new ArrayList<>(); 107 | Set patterns = servletMapping.keySet(); 108 | for (String pattern : patterns) { 109 | if (matcher.match(pattern, url)) { 110 | matchingPatterns.add(pattern); 111 | } 112 | } 113 | 114 | if (!matchingPatterns.isEmpty()) { 115 | Comparator patternComparator = matcher.getPatternComparator(url); 116 | Collections.sort(matchingPatterns, patternComparator); 117 | String bestMatch = matchingPatterns.get(0); 118 | return initAndGetServlet(bestMatch); 119 | } 120 | return initAndGetServlet(DEFAULT_SERVLET_ALIAS); 121 | } 122 | 123 | /** 124 | * 初始化并获取Servlet实例,如果已经初始化过则直接返回 125 | * 126 | * @param servletAlias 127 | * @return 128 | * @throws ServletNotFoundException 129 | */ 130 | private Servlet initAndGetServlet(String servletAlias) throws ServletNotFoundException { 131 | ServletHolder servletHolder = servlets.get(servletAlias); 132 | if (servletHolder == null) { 133 | throw new ServletNotFoundException(); 134 | } 135 | if (servletHolder.getServlet() == null) { 136 | try { 137 | Servlet servlet = (Servlet) Class.forName(servletHolder.getServletClass()).newInstance(); 138 | servlet.init(); 139 | servletHolder.setServlet(servlet); 140 | } catch (InstantiationException e) { 141 | e.printStackTrace(); 142 | } catch (IllegalAccessException e) { 143 | e.printStackTrace(); 144 | } catch (ClassNotFoundException e) { 145 | e.printStackTrace(); 146 | } 147 | } 148 | return servletHolder.getServlet(); 149 | } 150 | 151 | 152 | /** 153 | * 由URL得到一系列匹配的Filter实例 154 | * 155 | * @param url 156 | * @return 157 | */ 158 | public List mapFilter(String url) throws FilterNotFoundException { 159 | List matchingPatterns = new ArrayList<>(); 160 | Set patterns = filterMapping.keySet(); 161 | for (String pattern : patterns) { 162 | if (matcher.match(pattern, url)) { 163 | matchingPatterns.add(pattern); 164 | } 165 | } 166 | //flatMap将一对多转化为一对一 167 | Set filterAliases = matchingPatterns.stream().flatMap(pattern -> this.filterMapping.get(pattern).stream()).collect(Collectors.toSet()); 168 | List result = new ArrayList<>(); 169 | for (String alias : filterAliases) { 170 | result.add(initAndGetFilter(alias)); 171 | } 172 | return result; 173 | } 174 | 175 | /** 176 | * 初始化并返回Filter实例,如果已经初始化过则直接返回 177 | * 178 | * @param filterAlias 179 | * @return 180 | * @throws FilterNotFoundException 181 | */ 182 | private Filter initAndGetFilter(String filterAlias) throws FilterNotFoundException { 183 | FilterHolder filterHolder = filters.get(filterAlias); 184 | if (filterHolder == null) { 185 | throw new FilterNotFoundException(); 186 | } 187 | if (filterHolder.getFilter() == null) { 188 | try { 189 | Filter filter = (Filter) Class.forName(filterHolder.getFilterClass()).newInstance(); 190 | filter.init(); 191 | filterHolder.setFilter(filter); 192 | } catch (InstantiationException e) { 193 | e.printStackTrace(); 194 | } catch (IllegalAccessException e) { 195 | e.printStackTrace(); 196 | } catch (ClassNotFoundException e) { 197 | e.printStackTrace(); 198 | } 199 | } 200 | return filterHolder.getFilter(); 201 | } 202 | 203 | /** 204 | * 应用初始化 205 | * 206 | * @throws IllegalAccessException 207 | * @throws InstantiationException 208 | * @throws ClassNotFoundException 209 | */ 210 | public void init() throws IllegalAccessException, InstantiationException, ClassNotFoundException { 211 | this.servlets = new HashMap<>(); 212 | this.servletMapping = new HashMap<>(); 213 | this.attributes = new ConcurrentHashMap<>(); 214 | this.sessions = new ConcurrentHashMap<>(); 215 | this.filters = new HashMap<>(); 216 | this.filterMapping = new HashMap<>(); 217 | this.matcher = new AntPathMatcher(); 218 | //TODO 清理session 219 | //this.idleSessionCleaner = new IdleSessionCleaner(); 220 | //this.idleSessionCleaner.start(); 221 | 222 | this.servletContextListeners = new ArrayList<>(); 223 | this.httpSessionListeners = new ArrayList<>(); 224 | this.servletRequestListeners = new ArrayList<>(); 225 | 226 | parseConfig();//解析web.xml 227 | //Listener 228 | ServletContextEvent servletContextEvent = new ServletContextEvent(this); 229 | for (ServletContextListener listener : servletContextListeners) { 230 | listener.contextInitialized(servletContextEvent); 231 | } 232 | } 233 | 234 | /** 235 | * 应用关闭前被调用 236 | */ 237 | public void destroy() { 238 | servlets.values().forEach(servletHolder -> { 239 | if (servletHolder.getServlet() != null) { 240 | servletHolder.getServlet().destroy(); 241 | } 242 | }); 243 | filters.values().forEach(filterHolder -> { 244 | if (filterHolder.getFilter() != null) { 245 | filterHolder.getFilter().destroy(); 246 | } 247 | }); 248 | //Listener 249 | ServletContextEvent servletContextEvent = new ServletContextEvent(this); 250 | for (ServletContextListener listener : servletContextListeners) { 251 | listener.contextDestroyed(servletContextEvent); 252 | } 253 | } 254 | 255 | /** 256 | * web.xml文件解析,比如servlet,filter,listener等 257 | * 258 | * @throws ClassNotFoundException 259 | * @throws IllegalAccessException 260 | * @throws InstantiationException 261 | */ 262 | private void parseConfig() throws ClassNotFoundException, IllegalAccessException, InstantiationException { 263 | Document doc = XMLUtil.getDocument(ServletContext.class.getResourceAsStream("/web.xml")); 264 | Element root = doc.getRootElement(); 265 | // 解析servlet 266 | List servlets = root.elements("servlet"); 267 | for (Element servletEle : servlets) { 268 | String key = servletEle.element("servlet-name").getText(); 269 | String value = servletEle.element("servlet-class").getText(); 270 | this.servlets.put(key, new ServletHolder(value)); 271 | } 272 | //解析Servlet-mapping' 273 | List servletMapping = root.elements("servlet-mapping"); 274 | for (Element mapping : servletMapping) { 275 | List urlPatterns = mapping.elements("url-pattern");//一个servlet可以对应多个url 276 | String value = mapping.element("servlet-name").getText(); 277 | for (Element urlPattern : urlPatterns) { 278 | this.servletMapping.put(urlPattern.getText(), value); 279 | } 280 | } 281 | 282 | // 解析 filter 283 | List filters = root.elements("filter"); 284 | for (Element filterEle : filters) { 285 | String key = filterEle.element("filter-name").getText(); 286 | String value = filterEle.element("filter-class").getText(); 287 | this.filters.put(key, new FilterHolder(value)); 288 | } 289 | //解析 filter-mapping 290 | List filterMapping = root.elements("filter-mapping"); 291 | for (Element mapping : filterMapping) { 292 | List urlPatterns = mapping.elements("url-pattern");//一个filter可以对应多个url,一个url也可以对应不同的filter 293 | String value = mapping.element("filter-name").getText(); 294 | for (Element urlPattern : urlPatterns) { 295 | /** 296 | * 判断该url是否已经存在过,如果存在,即一个url对应多个filter的情况, 297 | * 例如:/** 298 | */ 299 | List values = this.filterMapping.get(urlPattern.getText()); 300 | if (values == null) { 301 | values = new ArrayList<>(); 302 | } 303 | values.add(value); 304 | this.filterMapping.put(urlPattern.getText(), values); 305 | } 306 | } 307 | 308 | // 解析listener 309 | Element listener = root.element("listener"); 310 | List listenerEles = listener.elements("listener-class"); 311 | for (Element listenerEle : listenerEles) { 312 | EventListener eventListener = (EventListener) Class.forName(listenerEle.getText()).newInstance(); 313 | if (eventListener instanceof ServletContextListener) { 314 | servletContextListeners.add((ServletContextListener) eventListener); 315 | } 316 | if (eventListener instanceof HttpSessionListener) { 317 | httpSessionListeners.add((HttpSessionListener) eventListener); 318 | } 319 | if (eventListener instanceof ServletRequestListener) { 320 | servletRequestListeners.add((ServletRequestListener) eventListener); 321 | } 322 | } 323 | } 324 | 325 | /** 326 | * 获取session 327 | * @param JSESSIONID 328 | * @return 329 | */ 330 | public HttpSession getSession(String JSESSIONID) { 331 | return sessions.get(JSESSIONID); 332 | } 333 | 334 | /** 335 | * 创建session 336 | * @param response 337 | * @return 338 | */ 339 | public HttpSession createSession(Response response) { 340 | HttpSession session = new HttpSession(UUIDUtil.uuid()); 341 | sessions.put(session.getId(), session); 342 | response.addCookie(new Cookie("JSESSIONID", session.getId())); 343 | 344 | HttpSessionEvent httpSessionEvent = new HttpSessionEvent(session); 345 | for (HttpSessionListener listener : httpSessionListeners) { 346 | listener.sessionCreated(httpSessionEvent); 347 | } 348 | return session; 349 | } 350 | 351 | /** 352 | * 销毁session 353 | * @param session 354 | */ 355 | public void invalidateSession(HttpSession session) { 356 | sessions.remove(session.getId()); 357 | afterSessionDestroyed(session); 358 | } 359 | 360 | /** 361 | * 清除空闲的session 362 | * 由于ConcurrentHashMap是线程安全的,所以remove不需要进行加锁 363 | */ 364 | public void cleanIdleSessions() { 365 | for (Iterator> it = sessions.entrySet().iterator(); it.hasNext(); ) { 366 | Map.Entry entry = it.next(); 367 | if (Duration.between(entry.getValue().getLastAccessed(), Instant.now()).getSeconds() >= DEFAULT_SESSION_EXPIRE_TIME) { 368 | // log.info("该session {} 已过期", entry.getKey()); 369 | afterSessionDestroyed(entry.getValue()); 370 | it.remove(); 371 | } 372 | } 373 | } 374 | 375 | private void afterSessionDestroyed(HttpSession session) { 376 | HttpSessionEvent httpSessionEvent = new HttpSessionEvent(session); 377 | for (HttpSessionListener listener : httpSessionListeners) { 378 | listener.sessionDestroyed(httpSessionEvent); 379 | } 380 | } 381 | 382 | public void afterRequestCreated(Request request) { 383 | ServletRequestEvent servletRequestEvent = new ServletRequestEvent(this, request); 384 | for (ServletRequestListener listener : servletRequestListeners) { 385 | listener.requestInitialized(servletRequestEvent); 386 | } 387 | } 388 | 389 | public void afterRequestDestroyed(Request request) { 390 | ServletRequestEvent servletRequestEvent = new ServletRequestEvent(this, request); 391 | for (ServletRequestListener listener : servletRequestListeners) { 392 | listener.requestDestroyed(servletRequestEvent); 393 | } 394 | } 395 | 396 | public Object getAttribute(String key) { 397 | return attributes.get(key); 398 | } 399 | 400 | public void setAttribute(String key, Object value) { 401 | attributes.put(key, value); 402 | } 403 | } 404 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/context/WebApplication.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.context; 2 | 3 | /** 4 | * @Author: zl 5 | * @Date: 2019/3/16 11:48 6 | */ 7 | public class WebApplication { 8 | private static ServletContext servletContext; 9 | 10 | static { 11 | try { 12 | servletContext = new ServletContext(); 13 | } catch (IllegalAccessException e) { 14 | e.printStackTrace(); 15 | } catch (ClassNotFoundException e) { 16 | e.printStackTrace(); 17 | } catch (InstantiationException e) { 18 | e.printStackTrace(); 19 | } 20 | } 21 | 22 | public static ServletContext getServletContext() { 23 | return servletContext; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/context/holder/FilterHolder.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.context.holder; 2 | 3 | import com.lws.lwebserver.core.fliter.Filter; 4 | import lombok.Data; 5 | 6 | /** 7 | * @Author: zl 8 | * @Date: 2019/3/16 11:50 9 | */ 10 | @Data 11 | public class FilterHolder { 12 | private Filter filter; 13 | private String filterClass; 14 | 15 | public FilterHolder(String filterClass) { 16 | this.filterClass = filterClass; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/context/holder/ServletHolder.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.context.holder; 2 | 3 | import com.lws.lwebserver.core.servlet.Servlet; 4 | import lombok.Data; 5 | 6 | /** 7 | * @Author: zl 8 | * @Date: 2019/3/16 11:51 9 | */ 10 | @Data 11 | public class ServletHolder { 12 | private Servlet servlet; 13 | private String servletClass; 14 | 15 | public ServletHolder(String servletClass) { 16 | this.servletClass = servletClass; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/cookie/Cookie.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.cookie; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @Author: zl 9 | * @Date: 2019/3/16 11:52 10 | */ 11 | @Data 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | public class Cookie { 15 | private String key; 16 | private String value; 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/enumeration/HttpStatus.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.enumeration; 2 | 3 | /** 4 | * Created by zl on 2019/03/01. 5 | */ 6 | public enum HttpStatus { 7 | OK(200),NOT_FOUND(404),INTERNAL_SERVER_ERROR(500),BAD_REQUEST(400),MOVED_TEMPORARILY(302); 8 | private int code; 9 | HttpStatus(int code){ 10 | this.code = code; 11 | } 12 | public int getCode(){ 13 | return code; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/enumeration/ModelScope.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.enumeration; 2 | 3 | /** 4 | * Created by zl on 2019/03/01. 5 | */ 6 | public enum ModelScope { 7 | REQUEST,SESSION,APPLICATION; 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/enumeration/RequestMethod.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.enumeration; 2 | 3 | /** 4 | * Created by zl on 2019/03/01. 5 | */ 6 | public enum RequestMethod { 7 | GET,POST,PUT,DELETE; 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/exception/FilterNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.exception; 2 | 3 | import com.lws.lwebserver.core.exception.base.ServletException; 4 | import com.lws.lwebserver.core.enumeration.HttpStatus; 5 | /** 6 | * @Author: zl 7 | * @Date: 2019/3/16 16:36 8 | */ 9 | public class FilterNotFoundException extends ServletException { 10 | private static final HttpStatus status = HttpStatus.NOT_FOUND; 11 | public FilterNotFoundException() { 12 | super(status); 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/exception/RequestInvalidException.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.exception; 2 | 3 | import com.lws.lwebserver.core.enumeration.HttpStatus; 4 | import com.lws.lwebserver.core.exception.base.ServletException; 5 | 6 | /** 7 | * Created by zl on 2019/03/01. 8 | */ 9 | public class RequestInvalidException extends ServletException { 10 | private static final HttpStatus status = HttpStatus.BAD_REQUEST; 11 | public RequestInvalidException() { 12 | super(status); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/exception/RequestParseException.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.exception; 2 | 3 | import com.lws.lwebserver.core.enumeration.HttpStatus; 4 | import com.lws.lwebserver.core.exception.base.ServletException; 5 | 6 | /** 7 | * Created by zl on 2019/03/01. 8 | */ 9 | public class RequestParseException extends ServletException { 10 | private static final HttpStatus status = HttpStatus.BAD_REQUEST; 11 | public RequestParseException() { 12 | super(status); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/exception/ResourceNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.exception; 2 | 3 | import com.lws.lwebserver.core.enumeration.HttpStatus; 4 | import com.lws.lwebserver.core.exception.base.ServletException; 5 | 6 | /** 7 | * Created by zl on 2019/03/01. 8 | */ 9 | public class ResourceNotFoundException extends ServletException { 10 | private static final HttpStatus status = HttpStatus.NOT_FOUND; 11 | public ResourceNotFoundException() { 12 | super(status); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/exception/ServerErrorException.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.exception; 2 | 3 | import com.lws.lwebserver.core.enumeration.HttpStatus; 4 | import com.lws.lwebserver.core.exception.base.ServletException; 5 | 6 | /** 7 | * Created by zl on 2019/03/01. 8 | */ 9 | public class ServerErrorException extends ServletException{ 10 | private static final HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR; 11 | public ServerErrorException() { 12 | super(status); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/exception/ServletNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.exception; 2 | 3 | import com.lws.lwebserver.core.enumeration.HttpStatus; 4 | import com.lws.lwebserver.core.exception.base.ServletException; 5 | 6 | /** 7 | * Created by zl on 2019/03/01. 8 | */ 9 | public class ServletNotFoundException extends ServletException { 10 | private static final HttpStatus status = HttpStatus.NOT_FOUND; 11 | public ServletNotFoundException() { 12 | super(status); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/exception/TemplateResolveException.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.exception; 2 | 3 | import com.lws.lwebserver.core.enumeration.HttpStatus; 4 | import com.lws.lwebserver.core.exception.base.ServletException; 5 | 6 | /** 7 | * Created by zl on 2019/03/01. 8 | */ 9 | public class TemplateResolveException extends ServletException { 10 | private static final HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR; 11 | public TemplateResolveException() { 12 | super(status); 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/exception/base/ServletException.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.exception.base; 2 | 3 | import com.lws.lwebserver.core.enumeration.HttpStatus; 4 | import lombok.Getter; 5 | 6 | /** 7 | * Created by zl on 2019/03/01. 8 | */ 9 | @Getter 10 | public class ServletException extends Exception { 11 | private HttpStatus status; 12 | public ServletException(HttpStatus status){ 13 | this.status = status; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/exception/handler/ExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.exception.handler; 2 | 3 | import com.lws.lwebserver.core.exception.RequestInvalidException; 4 | import com.lws.lwebserver.core.exception.base.ServletException; 5 | import com.lws.lwebserver.core.net.wrapper.SocketWrapperBase; 6 | import com.lws.lwebserver.core.response.Header; 7 | import com.lws.lwebserver.core.response.Response; 8 | import lombok.extern.slf4j.Slf4j; 9 | import com.lws.lwebserver.core.util.IOUtil; 10 | 11 | import java.io.IOException; 12 | import java.net.Socket; 13 | 14 | import static com.lws.lwebserver.core.constant.Const.ERROR_PAGE; 15 | 16 | /** 17 | * Created by zl on 2019/03/01. 18 | */ 19 | @Slf4j 20 | public class ExceptionHandler { 21 | 22 | public void handle(ServletException e, Response response, SocketWrapperBase socketWrapper) { 23 | try { 24 | if (e instanceof RequestInvalidException) { 25 | //log.info("请求无法读取,丢弃"); 26 | socketWrapper.close(); 27 | } else { 28 | log.info("抛出异常:{}", e.getClass().getName()); 29 | e.printStackTrace(); 30 | response.addHeader(new Header("Connection", "close")); 31 | response.setStatus(e.getStatus()); 32 | response.setBody(IOUtil.getBytesFromFile( 33 | String.format(ERROR_PAGE, String.valueOf(e.getStatus().getCode())))); 34 | } 35 | } catch (IOException e1) { 36 | e1.printStackTrace(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/fliter/Filter.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.fliter; 2 | 3 | import com.lws.lwebserver.core.request.Request; 4 | import com.lws.lwebserver.core.response.Response; 5 | 6 | /** 7 | * @Author: zl 8 | * @Date: 2019/3/16 16:13 9 | */ 10 | public interface Filter { 11 | /** 12 | * 过滤器初始化 13 | */ 14 | void init(); 15 | 16 | /** 17 | * 过滤 18 | * @param request 19 | * @param response 20 | * @param filterChain 21 | */ 22 | void doFilter(Request request, Response response, FilterChain filterChain) ; 23 | 24 | /** 25 | * 过滤器销毁 26 | */ 27 | void destroy(); 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/fliter/FilterChain.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.fliter; 2 | 3 | import com.lws.lwebserver.core.request.Request; 4 | import com.lws.lwebserver.core.response.Response; 5 | 6 | /** 7 | * @Author: zl 8 | * @Date: 2019/3/16 16:13 9 | */ 10 | public interface FilterChain { 11 | /** 12 | * 当前filter放行,由后续的filter继续进行过滤 13 | * @param request 14 | * @param response 15 | */ 16 | void doFilter(Request request, Response response) ; 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/listener/HttpSessionListener.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.listener; 2 | 3 | import com.lws.lwebserver.core.listener.event.HttpSessionEvent; 4 | 5 | import java.util.EventListener; 6 | 7 | /** 8 | * @Author: zl 9 | * @Date: 2019/3/16 16:14 10 | */ 11 | public interface HttpSessionListener extends EventListener { 12 | /** 13 | * session创建 14 | * @param se 15 | */ 16 | void sessionCreated(HttpSessionEvent se); 17 | 18 | /** 19 | * session销毁 20 | * @param se 21 | */ 22 | void sessionDestroyed(HttpSessionEvent se); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/listener/ServletContextListener.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.listener; 2 | 3 | import com.lws.lwebserver.core.listener.event.ServletContextEvent; 4 | 5 | import java.util.EventListener; 6 | 7 | /** 8 | * @Author: zl 9 | * @Date: 2019/3/16 16:15 10 | */ 11 | public interface ServletContextListener extends EventListener { 12 | /** 13 | * 应用启动 14 | * @param sce 15 | */ 16 | void contextInitialized(ServletContextEvent sce); 17 | 18 | /** 19 | * 应用关闭 20 | * @param sce 21 | */ 22 | void contextDestroyed(ServletContextEvent sce); 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/listener/ServletRequestListener.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.listener; 2 | 3 | import com.lws.lwebserver.core.listener.event.ServletRequestEvent; 4 | 5 | import java.util.EventListener; 6 | 7 | /** 8 | * @Author: zl 9 | * @Date: 2019/3/16 16:15 10 | */ 11 | public interface ServletRequestListener extends EventListener { 12 | /** 13 | * 请求初始化 14 | * @param sre 15 | */ 16 | void requestInitialized(ServletRequestEvent sre); 17 | 18 | /** 19 | * 请求销毁 20 | * @param sre 21 | */ 22 | void requestDestroyed(ServletRequestEvent sre); 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/listener/event/HttpSessionEvent.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.listener.event; 2 | 3 | import com.lws.lwebserver.core.session.HttpSession; 4 | 5 | /** 6 | * @Author: zl 7 | * @Date: 2019/3/16 16:17 8 | */ 9 | public class HttpSessionEvent extends java.util.EventObject { 10 | 11 | private static final long serialVersionUID = -7622791603672342895L; 12 | 13 | 14 | public HttpSessionEvent(HttpSession source) { 15 | super(source); 16 | } 17 | 18 | public HttpSession getSession () { 19 | return (HttpSession) super.getSource(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/listener/event/ServletContextEvent.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.listener.event; 2 | 3 | import com.lws.lwebserver.core.context.ServletContext; 4 | 5 | /** 6 | * @Author: zl 7 | * @Date: 2019/3/16 16:17 8 | */ 9 | public class ServletContextEvent extends java.util.EventObject { 10 | 11 | 12 | public ServletContextEvent(ServletContext source) { 13 | super(source); 14 | } 15 | 16 | public ServletContext getServletContext () { 17 | return (ServletContext) super.getSource(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/listener/event/ServletRequestEvent.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.listener.event; 2 | 3 | import com.lws.lwebserver.core.context.ServletContext; 4 | import com.lws.lwebserver.core.request.Request; 5 | 6 | /** 7 | * @Author: zl 8 | * @Date: 2019/3/16 16:18 9 | */ 10 | public class ServletRequestEvent extends java.util.EventObject { 11 | 12 | private static final long serialVersionUID = -7467864054698729101L; 13 | 14 | private final transient Request request; 15 | 16 | 17 | public ServletRequestEvent(ServletContext sc, Request request) { 18 | super(sc); 19 | this.request = request; 20 | } 21 | 22 | 23 | public Request getServletRequest () { 24 | return this.request; 25 | } 26 | 27 | 28 | public ServletContext getServletContext () { 29 | return (ServletContext) super.getSource(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/net/base/SocketProcessorBase.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.net.base; 2 | 3 | import com.lws.lwebserver.core.net.wrapper.SocketWrapperBase; 4 | 5 | /** 6 | * @Author: zl 7 | * @Date: 2019/3/16 8:54 8 | * 处理socket请求的基类 9 | */ 10 | public abstract class SocketProcessorBaseimplements Runnable { 11 | protected SocketWrapperBase socketWrapper; 12 | 13 | public SocketProcessorBase(SocketWrapperBase socketWrapper) { 14 | reset(socketWrapper); 15 | } 16 | 17 | public void reset(SocketWrapperBase socketWrapper){ 18 | this.socketWrapper = socketWrapper; 19 | } 20 | 21 | @Override 22 | public final void run() { 23 | synchronized (socketWrapper){ 24 | if (socketWrapper.isClosed()){ 25 | return; 26 | } 27 | doRun(); 28 | } 29 | } 30 | 31 | protected abstract void doRun(); 32 | } 33 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/net/cleaner/IdleConnectionCleaner.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.net.cleaner; 2 | 3 | import com.lws.lwebserver.core.net.endpoint.nio.Poller; 4 | import lombok.extern.slf4j.Slf4j; 5 | 6 | import java.util.List; 7 | import java.util.concurrent.Executors; 8 | import java.util.concurrent.ScheduledExecutorService; 9 | import java.util.concurrent.ThreadFactory; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | /** 13 | * @Author: zl 14 | * @Date: 2019/3/27 15:23 15 | */ 16 | @Slf4j 17 | public class IdleConnectionCleaner implements Runnable { 18 | private ScheduledExecutorService executor; 19 | private Poller[] nioPollers; 20 | 21 | public IdleConnectionCleaner(Poller[] nioPollers) { 22 | this.nioPollers = nioPollers; 23 | } 24 | 25 | public void start() { 26 | ThreadFactory threadFactory = new ThreadFactory() { 27 | @Override 28 | public Thread newThread(Runnable r) { 29 | return new Thread(r, "IdleConnectionCleaner"); 30 | } 31 | }; 32 | executor = Executors.newSingleThreadScheduledExecutor(threadFactory); 33 | executor.scheduleWithFixedDelay(this, 0, 10, TimeUnit.SECONDS); 34 | } 35 | @Override 36 | public void run() { 37 | for (Poller nioPoller : nioPollers) { 38 | nioPoller.cleanTimeoutSockets(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/net/dispatcher/AbstractDispatcher.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.net.dispatcher; 2 | 3 | import com.lws.lwebserver.core.context.ServletContext; 4 | import com.lws.lwebserver.core.context.WebApplication; 5 | import com.lws.lwebserver.core.exception.handler.ExceptionHandler; 6 | import com.lws.lwebserver.core.net.wrapper.SocketWrapperBase; 7 | import com.lws.lwebserver.core.resource.ResourceHandler; 8 | 9 | import java.util.concurrent.ArrayBlockingQueue; 10 | import java.util.concurrent.ThreadFactory; 11 | import java.util.concurrent.ThreadPoolExecutor; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | /** 15 | * @Author: zl 16 | * @Date: 2019/3/16 10:25 17 | */ 18 | public abstract class AbstractDispatcher { 19 | protected ResourceHandler resourceHandler; 20 | protected ExceptionHandler exceptionHandler; 21 | protected ThreadPoolExecutor pool; 22 | protected ServletContext servletContext; 23 | 24 | public AbstractDispatcher() { 25 | this.servletContext = WebApplication.getServletContext(); 26 | this.exceptionHandler = new ExceptionHandler(); 27 | this.resourceHandler = new ResourceHandler(exceptionHandler); 28 | ThreadFactory threadFactory = new ThreadFactory() { 29 | private int count; 30 | 31 | @Override 32 | public Thread newThread(Runnable r) { 33 | return new Thread(r, "Worker Pool-" + count++); 34 | } 35 | }; 36 | 37 | //TODO 线程池的设置对不对? 38 | this.pool = new ThreadPoolExecutor(100, 100, 1, 39 | TimeUnit.SECONDS, 40 | new ArrayBlockingQueue<>(200), 41 | threadFactory, new ThreadPoolExecutor.CallerRunsPolicy()); 42 | } 43 | 44 | /** 45 | * 关闭 46 | */ 47 | public void shutdown() { 48 | pool.shutdown(); 49 | servletContext.destroy(); 50 | } 51 | 52 | /** 53 | * 分发请求 54 | * @param socketWrapper 55 | */ 56 | public abstract void doDispatch(SocketWrapperBase socketWrapper); 57 | } 58 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/net/dispatcher/aio/AioDispatcher.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.net.dispatcher.aio; 2 | 3 | import com.lws.lwebserver.core.exception.base.ServletException; 4 | import com.lws.lwebserver.core.net.dispatcher.AbstractDispatcher; 5 | import com.lws.lwebserver.core.net.handler.aio.AioRequestHandler; 6 | import com.lws.lwebserver.core.net.wrapper.SocketWrapperBase; 7 | import com.lws.lwebserver.core.net.wrapper.aio.AioSocketWrapper; 8 | import com.lws.lwebserver.core.request.Request; 9 | import com.lws.lwebserver.core.response.Response; 10 | import lombok.extern.slf4j.Slf4j; 11 | 12 | import java.io.IOException; 13 | import java.nio.ByteBuffer; 14 | import java.nio.channels.CompletionHandler; 15 | 16 | /** 17 | * @Author: zl 18 | * @Date: 2019/3/27 15:00 19 | */ 20 | @Slf4j 21 | public class AioDispatcher extends AbstractDispatcher { 22 | public AioDispatcher() { 23 | super(); 24 | } 25 | 26 | @Override 27 | public void shutdown() { 28 | super.shutdown(); 29 | } 30 | 31 | @Override 32 | public void doDispatch(SocketWrapperBase socketWrapper) { 33 | AioSocketWrapper aioSocketWrapper = (AioSocketWrapper) socketWrapper; 34 | ByteBuffer buffer = ByteBuffer.allocate(1024); 35 | aioSocketWrapper.getSocket().read(buffer, buffer, new CompletionHandler() { 36 | @Override 37 | public void completed(Integer result, ByteBuffer attachment) { 38 | Request request = null; 39 | Response response = null; 40 | try { 41 | //解析请求 42 | request = new Request(attachment.array()); 43 | response = new Response(); 44 | pool.execute(new AioRequestHandler(aioSocketWrapper, servletContext, exceptionHandler, resourceHandler, request, response,this)); 45 | } catch (ServletException e) { 46 | exceptionHandler.handle(e, response, aioSocketWrapper); 47 | } catch (IOException e) { 48 | e.printStackTrace(); 49 | } 50 | } 51 | 52 | @Override 53 | public void failed(Throwable e, ByteBuffer attachment) { 54 | log.error("read failed"); 55 | e.printStackTrace(); 56 | } 57 | }); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/net/dispatcher/bio/BioDispatcher.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.net.dispatcher.bio; 2 | 3 | import com.lws.lwebserver.core.exception.RequestInvalidException; 4 | import com.lws.lwebserver.core.exception.base.ServletException; 5 | import com.lws.lwebserver.core.net.dispatcher.AbstractDispatcher; 6 | import com.lws.lwebserver.core.net.handler.bio.BioRequestHandler; 7 | import com.lws.lwebserver.core.net.wrapper.SocketWrapperBase; 8 | import com.lws.lwebserver.core.net.wrapper.bio.BioSocketWrapper; 9 | import com.lws.lwebserver.core.request.Request; 10 | import com.lws.lwebserver.core.response.Response; 11 | import lombok.extern.slf4j.Slf4j; 12 | 13 | import java.io.BufferedInputStream; 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.net.Socket; 17 | 18 | /** 19 | * @Author: zl 20 | * @Date: 2019/3/20 0:14 21 | */ 22 | @Slf4j 23 | public class BioDispatcher extends AbstractDispatcher { 24 | public BioDispatcher() { 25 | super(); 26 | } 27 | 28 | @Override 29 | public void shutdown() { 30 | super.shutdown(); 31 | } 32 | 33 | @Override 34 | public void doDispatch(SocketWrapperBase socketWrapper) { 35 | BioSocketWrapper bioSocketWrapper = (BioSocketWrapper) socketWrapper; 36 | Socket socket = bioSocketWrapper.getSocket(); 37 | Request request = null; 38 | Response response = null; 39 | try { 40 | InputStream is=socket.getInputStream(); 41 | BufferedInputStream bin = new BufferedInputStream(is); 42 | byte[] buf = null; 43 | try { 44 | buf = new byte[bin.available()]; 45 | int len = bin.read(buf); 46 | if (len <= 0) { 47 | throw new RequestInvalidException(); 48 | } 49 | } catch (IOException e) { 50 | e.printStackTrace(); 51 | } 52 | // 这里这里不要把in关掉,把in关掉就等同于把socket关掉 53 | //解析请求 54 | response = new Response(); 55 | request = new Request(buf); 56 | pool.execute(new BioRequestHandler(socketWrapper, servletContext, exceptionHandler, resourceHandler, request, response)); 57 | } catch (ServletException e) { 58 | exceptionHandler.handle(e, response, socketWrapper); 59 | } catch (IOException e) { 60 | e.printStackTrace(); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/net/dispatcher/nio/NioDispatcher.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.net.dispatcher.nio; 2 | 3 | 4 | import com.lws.lwebserver.core.exception.ServerErrorException; 5 | import com.lws.lwebserver.core.exception.base.ServletException; 6 | import com.lws.lwebserver.core.net.dispatcher.AbstractDispatcher; 7 | import com.lws.lwebserver.core.net.handler.nio.NioRequestHandler; 8 | import com.lws.lwebserver.core.net.wrapper.SocketWrapperBase; 9 | import com.lws.lwebserver.core.net.wrapper.nio.NioSocketWrapper; 10 | import com.lws.lwebserver.core.request.Request; 11 | import com.lws.lwebserver.core.response.Response; 12 | import lombok.Data; 13 | import lombok.extern.slf4j.Slf4j; 14 | 15 | import java.io.ByteArrayOutputStream; 16 | import java.io.IOException; 17 | import java.nio.ByteBuffer; 18 | 19 | /** 20 | * @Author: zl 21 | * @Date: 2019/3/16 10:25 22 | */ 23 | @Data 24 | @Slf4j 25 | public class NioDispatcher extends AbstractDispatcher { 26 | /** 27 | * 分发请求,注意IO读取必须放在IO线程中进行,不能放到线程池中,否则会出现多个线程同时读同一个socket数据的情况 28 | * 1、读取数据 29 | * 2、构造request,response 30 | * 3、将业务放入到线程池中处理 31 | * 32 | * @param socketWrapper 33 | */ 34 | @Override 35 | public void doDispatch(SocketWrapperBase socketWrapper) { 36 | NioSocketWrapper nioSocketWrapper = (NioSocketWrapper) socketWrapper; 37 | log.info("已经将请求放入worker线程池中"); 38 | //定义缓存 39 | ByteBuffer buffer = ByteBuffer.allocate(1024); 40 | log.info("开始读取Request"); 41 | Request request = null; 42 | Response response = null; 43 | try { 44 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 45 | //将通道中的数据读到缓存中,通道中的数据就是clint发送给服务端的数据 46 | //int readLength=nioSocketWrapper.getSocketChannel().read(buffer); 47 | while (nioSocketWrapper.getSocket().read(buffer) > 0) { 48 | /** 49 | * NIO中最复杂的操作就是buffer的控制 50 | * buffer中有一个游标,游标信息在操作后不会归零,如果直接访问buffer会有数据不一致的可能 51 | * filp重置游标,nio中filp是常用方法 52 | *TODO buffer应用(写)的固定逻辑: 53 | * 1,clear(); 54 | * 2, put() ->写操作 55 | * 3,flip() ->重置游标 56 | * 4,scocketchannel.write(buffer) ->将缓存数据发送到网络的另一端 57 | * 5,clear() 58 | * TODO 读 59 | * 1,clear(); 60 | * 2,scocketchannel.read(buffer) ->从网络中读取数据 61 | * 3,flip() ->重置游标 62 | * 4,clear() 63 | */ 64 | buffer.flip(); 65 | //buffer.remaining获得字节长度 66 | //将bytebuffer保存到字节数组中, 67 | baos.write(buffer.array()); 68 | } 69 | baos.close();//关闭通道 70 | request = new Request(baos.toByteArray());//实例request对象 71 | response = new Response();//实例response对象,区别与1.0 72 | pool.execute(new NioRequestHandler(nioSocketWrapper, servletContext, exceptionHandler, resourceHandler, request, response)); 73 | } catch (IOException e) { 74 | e.printStackTrace(); 75 | exceptionHandler.handle(new ServerErrorException(), response, nioSocketWrapper); 76 | } catch (ServletException e) { 77 | exceptionHandler.handle(e, response, nioSocketWrapper); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/net/endpoint/AbstractEndpoint.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.net.endpoint; 2 | 3 | import com.lws.lwebserver.core.net.base.SocketProcessorBase; 4 | import com.lws.lwebserver.core.net.dispatcher.AbstractDispatcher; 5 | import com.lws.lwebserver.core.net.dispatcher.nio.NioDispatcher; 6 | import com.lws.lwebserver.core.net.wrapper.SocketWrapperBase; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.util.StringUtils; 9 | 10 | import java.net.Socket; 11 | import java.util.concurrent.Executor; 12 | import java.util.concurrent.RejectedExecutionException; 13 | import java.util.concurrent.ThreadPoolExecutor; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | /** 17 | * @author zl 18 | */ 19 | @Slf4j 20 | public abstract class AbstractEndpoint { 21 | 22 | /** 23 | * endpoint run status 24 | */ 25 | protected volatile boolean running = false; 26 | /** 27 | * 阻塞状态 28 | */ 29 | protected volatile boolean paused = false; 30 | /** 31 | * 调度者 32 | */ 33 | protected AbstractDispatcher dispatcher; 34 | /** 35 | * 接收者 36 | */ 37 | protected Acceptor[] acceptors; 38 | 39 | /** 40 | * 启动服务器 41 | * 42 | * @param port 43 | */ 44 | public abstract void start(int port); 45 | 46 | /** 47 | * 关闭服务器 48 | */ 49 | public abstract void close(); 50 | 51 | /** 52 | * Hook to allow Endpoints to provide a specific Acceptor implementation. 53 | * 创建监听 54 | * 55 | * @return acceptor 56 | */ 57 | protected abstract Acceptor createAcceptor(); 58 | 59 | /** 60 | * 创建调度者 61 | * 62 | * @return 63 | */ 64 | protected abstract void initDispatcher(); 65 | 66 | /** 67 | * 根据传入的bio、nio、aio获取相应的Endpoint实例 68 | * 69 | * @param connector 70 | * @return 71 | */ 72 | public static AbstractEndpoint getInstance(String connector) { 73 | StringBuilder sb = new StringBuilder(); 74 | sb.append("com.lws.lwebserver.core.net.endpoint") 75 | .append(".") 76 | .append(connector) 77 | .append(".") 78 | .append(StringUtils.capitalize(connector)) 79 | .append("Endpoint"); 80 | try { 81 | return (AbstractEndpoint) Class.forName(sb.toString()).newInstance(); 82 | } catch (InstantiationException e) { 83 | e.printStackTrace(); 84 | } catch (IllegalAccessException e) { 85 | e.printStackTrace(); 86 | } catch (ClassNotFoundException e) { 87 | e.printStackTrace(); 88 | } 89 | throw new IllegalArgumentException(connector); 90 | } 91 | 92 | /** 93 | * External Executor based thread pool. 94 | */ 95 | /* private Executor executor = null;//内部线程池 96 | 97 | protected volatile boolean internalExecutor = true;//线程池是否被初始化 98 | 99 | public void setExecutor(Executor executor) { 100 | this.executor = executor; 101 | this.internalExecutor = (executor == null); 102 | } 103 | public Executor getExecutor() { return executor; } 104 | 105 | public void createExecutor(){ 106 | internalExecutor=true; 107 | TaskQueue taskqueue = new TaskQueue(); 108 | TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority()); 109 | executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf); 110 | taskqueue.setParent( (ThreadPoolExecutor) executor); 111 | }*/ 112 | /** 113 | * backlog 队列大小,定义是已连接但未进行accept处理的(accept队列)SOCKET队列大小 114 | */ 115 | private int acceptCount = 100; 116 | 117 | public void setAcceptCount(int acceptCount) { 118 | if (acceptCount > 0) this.acceptCount = acceptCount; 119 | } 120 | 121 | public int getAcceptCount() { 122 | return acceptCount; 123 | } 124 | 125 | /** 126 | * Acceptor thread count. 127 | */ 128 | protected int acceptorThreadCount = 1; 129 | 130 | public void setAcceptorThreadCount(int acceptorThreadCount) { 131 | this.acceptorThreadCount = acceptorThreadCount; 132 | } 133 | 134 | public int getAcceptorThreadCount() { 135 | return acceptorThreadCount; 136 | } 137 | 138 | protected final void startAcceptorThreads(String acceptorName) { 139 | int count = getAcceptorThreadCount(); 140 | acceptors = new Acceptor[count]; 141 | 142 | for (int i = 0; i < count; i++) { 143 | acceptors[i] = createAcceptor(); 144 | String threadName = acceptorName + i; 145 | acceptors[i].setThreadName(threadName); 146 | Thread t = new Thread(acceptors[i], threadName); 147 | t.setPriority(Thread.NORM_PRIORITY); 148 | t.setDaemon(true); 149 | t.start(); 150 | } 151 | } 152 | 153 | /** 154 | * acceptor基类 155 | */ 156 | public abstract static class Acceptor implements Runnable { 157 | //监听状态 158 | public enum AcceptorState { 159 | NEW, RUNNING, PAUSED, ENDED 160 | } 161 | 162 | protected volatile AcceptorState state = AcceptorState.NEW; 163 | 164 | public final AcceptorState getState() { 165 | return state; 166 | } 167 | 168 | private String threadName; 169 | 170 | protected final void setThreadName(final String threadName) { 171 | this.threadName = threadName; 172 | } 173 | 174 | protected final String getThreadName() { 175 | return threadName; 176 | } 177 | } 178 | 179 | /** 180 | * @param socketWrapper 181 | * @return 182 | */ 183 | public boolean processSocket(S socketWrapper) { 184 | try { 185 | if (socketWrapper == null) { 186 | return false; 187 | } 188 | //TODO SocketProcessorBase 189 | //SocketProcessorBase sc=createSocketProcessor(socketWrapper); 190 | socketWrapper.setWorking(true);//设置当前socket为工作状态 191 | //调度 192 | dispatcher.doDispatch(socketWrapper); 193 | } catch (RejectedExecutionException ree) { 194 | log.warn("endpoint.executor.fail", ree); 195 | return false; 196 | } catch (Throwable t) { 197 | log.error("endpoint.process.fail", t); 198 | return false; 199 | } 200 | return true; 201 | } 202 | 203 | protected abstract SocketProcessorBase createSocketProcessor( 204 | SocketWrapperBase socketWrapper); 205 | 206 | } 207 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/net/endpoint/aio/AioEndpoint.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.net.endpoint.aio; 2 | 3 | import com.lws.lwebserver.core.net.base.SocketProcessorBase; 4 | import com.lws.lwebserver.core.net.dispatcher.aio.AioDispatcher; 5 | import com.lws.lwebserver.core.net.endpoint.AbstractEndpoint; 6 | import com.lws.lwebserver.core.net.wrapper.SocketWrapperBase; 7 | import com.lws.lwebserver.core.net.wrapper.aio.AioSocketWrapper; 8 | import lombok.extern.slf4j.Slf4j; 9 | 10 | import java.io.IOException; 11 | import java.net.InetSocketAddress; 12 | import java.nio.channels.AsynchronousChannelGroup; 13 | import java.nio.channels.AsynchronousServerSocketChannel; 14 | import java.nio.channels.AsynchronousSocketChannel; 15 | import java.util.concurrent.*; 16 | 17 | /** 18 | * @Author: zl 19 | * @Date: 2019/3/27 15:00 20 | */ 21 | @Slf4j 22 | public class AioEndpoint extends AbstractEndpoint { 23 | 24 | private AsynchronousServerSocketChannel serverSocket;//异步的主socket通道 25 | //private AioAcceptor aioAcceptor; 26 | private ThreadPoolExecutor pool; 27 | @Override 28 | public void start(int port) { 29 | running=true;//run endpoint 30 | paused=false; 31 | try { 32 | initDispatcher(); 33 | initServerSocket(port); 34 | log.info("服务器启动"); 35 | } catch (Exception e) { 36 | e.printStackTrace(); 37 | log.info("初始化服务器失败"); 38 | close(); 39 | } 40 | } 41 | 42 | private void initServerSocket(int port) throws IOException { 43 | ThreadFactory threadFactory = new ThreadFactory() { 44 | private int count; 45 | 46 | @Override 47 | public Thread newThread(Runnable r) { 48 | return new Thread(r, "Endpoint Pool-" + count++); 49 | } 50 | }; 51 | int processors = Runtime.getRuntime().availableProcessors(); 52 | pool = new ThreadPoolExecutor(processors, processors, 1, TimeUnit.SECONDS, new ArrayBlockingQueue<>(200), threadFactory, new ThreadPoolExecutor.CallerRunsPolicy()); 53 | // 以指定线程池来创建一个AsynchronousChannelGroup 54 | AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup 55 | .withThreadPool(pool); 56 | // 以指定线程池来创建一个AsynchronousServerSocketChannel 57 | serverSocket = AsynchronousServerSocketChannel.open(channelGroup) 58 | // 指定监听本机的PORT端口 59 | .bind(new InetSocketAddress(port),getAcceptCount()); 60 | // Initialize thread count defaults for acceptor, poller 61 | if (acceptorThreadCount != 1) { 62 | // NIO2 does not allow any form of IO concurrency 63 | acceptorThreadCount = 1; 64 | } 65 | // 使用CompletionHandler接受来自客户端的连接请求 66 | //aioAcceptor = new AioAcceptor(); 67 | // 开始接收客户端连接 68 | accept(); 69 | } 70 | @Override 71 | protected void initDispatcher() { 72 | dispatcher=new AioDispatcher(); 73 | } 74 | 75 | @Override 76 | protected SocketProcessorBase createSocketProcessor(SocketWrapperBase socketWrapper) { 77 | return null; 78 | } 79 | 80 | @Override 81 | public void close() { 82 | running=false; 83 | dispatcher.shutdown(); 84 | try { 85 | serverSocket.close(); 86 | } catch (IOException e) { 87 | e.printStackTrace(); 88 | } 89 | } 90 | /** 91 | * 接收一个客户端连接 92 | */ 93 | public void accept() { 94 | /** 95 | * 调用accept并且注册回调,这里方式有两种: 96 | * 1)挂起的 • Future accept(); 97 | *2)注册回调的 • void accept(Aattachment,CompletionHandlerhandler); 98 | * 并且两种方法都会产生一定的错误率,相对来说挂起的方式错误率低 99 | */ 100 | //serverSocket.accept(null, aioAcceptor); 101 | startAcceptorThreads("AIO-Acceptor"); 102 | } 103 | //-----------------------------------------------------acceptor start 104 | @Override 105 | protected Acceptor createAcceptor() { 106 | return new Acceptor(); 107 | } 108 | 109 | protected class Acceptor extends AbstractEndpoint.Acceptor{ 110 | @Override 111 | public void run() { 112 | log.info("NIO Acceptor 开始监听"); 113 | while (running){ 114 | //endpoint阻塞 115 | while (paused && running) { 116 | state = AcceptorState.PAUSED; 117 | try { 118 | Thread.sleep(50); 119 | } catch (InterruptedException e) { 120 | } 121 | } 122 | if(!running){ 123 | break; 124 | } 125 | state=AcceptorState.RUNNING; 126 | //TODO 待添加最大连接数的判断 LimitLatch 127 | AsynchronousSocketChannel clientSocket =null; 128 | try { 129 | //调用阻塞 130 | clientSocket=serverSocket.accept().get(); 131 | //Future future=serverSocket.accept(); 132 | 133 | }catch (Exception e){ 134 | e.printStackTrace(); 135 | } 136 | if(null==clientSocket){ 137 | continue; 138 | } 139 | if(running&&!paused){ 140 | if(!setSocketOptions(clientSocket)){ 141 | closeSocket(clientSocket); 142 | } 143 | }else { 144 | closeSocket(clientSocket); 145 | } 146 | 147 | } 148 | } 149 | 150 | } 151 | private boolean setSocketOptions(AsynchronousSocketChannel socket) { 152 | return processSocket(new AioSocketWrapper(socket,this)); 153 | } 154 | private void closeSocket(AsynchronousSocketChannel socket) { 155 | try { 156 | socket.close(); 157 | } catch (IOException ioe) { 158 | if (log.isDebugEnabled()) { 159 | log.debug("endpoint.err.close", ioe); 160 | } 161 | } 162 | } 163 | /* *//** 164 | * 执行读已就绪的客户端连接的请求 165 | * @param client 166 | *//* 167 | public void execute(AsynchronousSocketChannel client) { 168 | 169 | dispatcher.doDispatch(new AioSocketWrapper(client,this)); 170 | }*/ 171 | /* *//** 172 | * 回调的方式,注销掉,尝试挂起的方式 173 | *//* 174 | protected class AioAcceptor implements CompletionHandler { 175 | @Override 176 | public void completed(AsynchronousSocketChannel client, Void attachment) { 177 | accept(); 178 | execute(client); 179 | } 180 | 181 | @Override 182 | public void failed(Throwable exc, Void attachment) { 183 | log.info("accept failed..."); 184 | exc.printStackTrace(); 185 | } 186 | }*/ 187 | //-----------------------------------------------------acceptor end 188 | } 189 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/net/endpoint/bio/BioEndpoint.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.net.endpoint.bio; 2 | 3 | import com.lws.lwebserver.core.net.base.SocketProcessorBase; 4 | import com.lws.lwebserver.core.net.dispatcher.bio.BioDispatcher; 5 | import com.lws.lwebserver.core.net.endpoint.AbstractEndpoint; 6 | import com.lws.lwebserver.core.net.wrapper.SocketWrapperBase; 7 | import com.lws.lwebserver.core.net.wrapper.bio.BioSocketWrapper; 8 | import lombok.extern.slf4j.Slf4j; 9 | 10 | import java.io.IOException; 11 | import java.net.ServerSocket; 12 | import java.net.Socket; 13 | @Slf4j 14 | public class BioEndpoint extends AbstractEndpoint { 15 | private ServerSocket serverSocket;//主socket 16 | 17 | @Override 18 | public void start(int port) { 19 | try { 20 | running=true; 21 | paused=false; 22 | initDispatcher();//初始化调度者 23 | serverSocket = new ServerSocket(port); 24 | startAcceptorThreads("BIO-Acceptor");//开始监听 25 | log.info("服务器启动"); 26 | } catch (Exception e) { 27 | e.printStackTrace(); 28 | log.info("初始化服务器失败"); 29 | close(); 30 | } 31 | } 32 | 33 | 34 | @Override 35 | public void close() { 36 | running = false; 37 | dispatcher.shutdown(); 38 | try { 39 | serverSocket.close(); 40 | } catch (IOException e) { 41 | e.printStackTrace(); 42 | } 43 | } 44 | 45 | @Override 46 | protected void initDispatcher() { 47 | dispatcher = new BioDispatcher(); 48 | } 49 | 50 | @Override 51 | protected SocketProcessorBase createSocketProcessor(SocketWrapperBase socketWrapper) { 52 | //TODO 53 | return null; 54 | } 55 | /** 56 | * 包装socket,并处理 57 | * @param socket 58 | * @return 59 | */ 60 | protected boolean setSocketOptions(Socket socket) { 61 | try { 62 | return processSocket(new BioSocketWrapper(socket,this)); 63 | }catch (Exception e){ 64 | log.error("处理clientSocket错误:"+e); 65 | return false; 66 | } 67 | } 68 | //-----------------------------------------------------acceptor start 69 | 70 | @Override 71 | protected Acceptor createAcceptor() { 72 | return new Acceptor(); 73 | } 74 | 75 | /** 76 | * 后台线程监听客户机的TCP连接 77 | */ 78 | protected class Acceptor extends AbstractEndpoint.Acceptor{ 79 | @Override 80 | public void run() { 81 | log.info("BIO Acceptor 开始监听"); 82 | while (running) { 83 | //endpoint阻塞 84 | while (paused && running) { 85 | state = AcceptorState.PAUSED; 86 | try { 87 | Thread.sleep(50); 88 | } catch (InterruptedException e) { 89 | } 90 | } 91 | if(!running){ 92 | break; 93 | } 94 | state=AcceptorState.RUNNING; 95 | Socket clientSocket=null; 96 | try { 97 | //调用阻塞 98 | clientSocket=serverSocket.accept(); 99 | }catch (IOException e){ 100 | e.printStackTrace(); 101 | } 102 | if(null==clientSocket){ 103 | continue; 104 | } 105 | if(running&&!paused){ 106 | if(!setSocketOptions(clientSocket)){ 107 | try { 108 | clientSocket.close(); 109 | } catch (IOException e) { 110 | e.printStackTrace(); 111 | } 112 | } 113 | }else { 114 | try { 115 | clientSocket.close(); 116 | } catch (IOException e) { 117 | e.printStackTrace(); 118 | } 119 | } 120 | /*try { 121 | //TCP的短连接,请求处理完即关闭 122 | client = serverSocket.accept(); 123 | log.info("client:{}", client); 124 | processSocket(new BioSocketWrapper(client,)); 125 | //TODO 调度 126 | } catch (IOException e) { 127 | e.printStackTrace(); 128 | }*/ 129 | } 130 | } 131 | } 132 | //-----------------------------------------------------acceptor end 133 | } 134 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/net/endpoint/nio/NioEndpoint.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.net.endpoint.nio; 2 | 3 | import com.lws.lwebserver.core.net.base.SocketProcessorBase; 4 | import com.lws.lwebserver.core.net.cleaner.IdleConnectionCleaner; 5 | import com.lws.lwebserver.core.net.dispatcher.nio.NioDispatcher; 6 | import com.lws.lwebserver.core.net.endpoint.AbstractEndpoint; 7 | import com.lws.lwebserver.core.net.wrapper.SocketWrapperBase; 8 | import com.lws.lwebserver.core.net.wrapper.nio.NioSocketWrapper; 9 | import lombok.extern.slf4j.Slf4j; 10 | 11 | import java.io.IOException; 12 | import java.net.InetSocketAddress; 13 | import java.nio.channels.ServerSocketChannel; 14 | import java.nio.channels.SocketChannel; 15 | 16 | import java.util.concurrent.atomic.AtomicInteger; 17 | 18 | /** 19 | * @author zl 20 | * nio 模式切入点 21 | */ 22 | @Slf4j 23 | public class NioEndpoint extends AbstractEndpoint { 24 | 25 | private ServerSocketChannel serverSocket;//服务通道 26 | /** 27 | * 针对keep-alive连接,定时清理poller中的socket 28 | */ 29 | private IdleConnectionCleaner cleaner; 30 | /** 31 | * 长连接超时时间 32 | */ 33 | private int keepAliveTimeout = 6 * 1000; 34 | 35 | @Override 36 | public void start(int port) { 37 | try { 38 | running = true;//run endpoint 39 | paused = false; 40 | initDispatcher(); 41 | initServerSocket(port); 42 | initPoller(); 43 | startAcceptorThreads("NIO-Acceptor"); 44 | initIdleSocketCleaner();//定时清理 45 | } catch (Exception e) { 46 | e.printStackTrace(); 47 | log.info("初始化服务器失败"); 48 | close(); 49 | } 50 | } 51 | 52 | @Override 53 | public void close() { 54 | running = false; 55 | for (Poller nioPoller : nioPollers) { 56 | try { 57 | nioPoller.destroy(); 58 | } catch (IOException e) { 59 | e.printStackTrace(); 60 | } 61 | } 62 | dispatcher.shutdown(); 63 | try { 64 | serverSocket.close(); 65 | } catch (IOException e) { 66 | e.printStackTrace(); 67 | } 68 | } 69 | 70 | private void initServerSocket(int port) throws IOException { 71 | serverSocket = ServerSocketChannel.open();//开启服务通道 72 | serverSocket.bind(new InetSocketAddress(port), getAcceptCount());//绑定端口号,使用到InetSocketAddress对象 73 | serverSocket.configureBlocking(true);//阻塞型io 74 | } 75 | 76 | @Override 77 | protected void initDispatcher() { 78 | dispatcher = new NioDispatcher(); 79 | } 80 | 81 | /** 82 | * 初始化IdleSocketCleaner 83 | */ 84 | private void initIdleSocketCleaner() { 85 | cleaner = new IdleConnectionCleaner(nioPollers); 86 | cleaner.start(); 87 | } 88 | 89 | /** 90 | * 处理获得客户机连接 91 | * 设置阻塞类型; 92 | * 注册到poller 93 | * 94 | * @param socket 95 | * @return 96 | */ 97 | protected boolean setSocketOptions(SocketChannel socket) { 98 | try { 99 | //注意监听serversocketchannel为阻塞,客户机连接为非阻塞 100 | socket.configureBlocking(false); 101 | //TODO SecureNioChannel SSL 对buffer进行加密 102 | getPoller().register(socket, true);//注册到pollerevent 103 | } catch (IOException e) { 104 | e.printStackTrace(); 105 | return false; 106 | } 107 | return true; 108 | } 109 | 110 | /** 111 | * 关闭通道 112 | * 113 | * @param socket 114 | */ 115 | private void closeSocket(SocketChannel socket) { 116 | try { 117 | socket.socket().close(); 118 | } catch (IOException ioe) { 119 | if (log.isDebugEnabled()) { 120 | log.error("endpoint.err.close"); 121 | } 122 | } 123 | try { 124 | socket.close(); 125 | } catch (IOException ioe) { 126 | if (log.isDebugEnabled()) { 127 | log.error("endpoint.err.close"); 128 | } 129 | } 130 | } 131 | 132 | /** 133 | * @param socketWrapper 134 | */ 135 | public void execute(NioSocketWrapper socketWrapper) { 136 | //TODO 137 | } 138 | 139 | public int getKeepAliveTimeout() { 140 | return keepAliveTimeout; 141 | } 142 | 143 | @Override 144 | protected SocketProcessorBase createSocketProcessor(SocketWrapperBase socketWrapper) { 145 | return new SocketProcessor(socketWrapper); 146 | } 147 | 148 | //-----------------------------------------------------Poller start 149 | /** 150 | * The socket poller. 151 | */ 152 | private int pollerThreadCount = Math.min(2, Runtime.getRuntime().availableProcessors());//轮询池的数量,一般为cpu个数 153 | private Poller[] nioPollers = null; 154 | private AtomicInteger pollerRotater = new AtomicInteger(0); 155 | 156 | /** 157 | * 返回可用selector 158 | * 159 | * @return The next poller in sequence 160 | */ 161 | public Poller getPoller() { 162 | int idx = Math.abs(pollerRotater.incrementAndGet()) % nioPollers.length; 163 | return nioPollers[idx]; 164 | } 165 | 166 | /** 167 | * Init poller 168 | * 169 | * @throws IOException 170 | */ 171 | private void initPoller() throws IOException { 172 | nioPollers = new Poller[pollerThreadCount]; 173 | for (int i = 0; i < pollerThreadCount; i++) { 174 | String pollName = "NIOPoller-" + i; 175 | nioPollers[i] = new Poller(this, pollName); 176 | Thread pollerThread = new Thread(nioPollers[i], "LWS-ClientPoller-" + i); 177 | pollerThread.setPriority(Thread.NORM_PRIORITY);//设置优先级 178 | pollerThread.setDaemon(true); 179 | pollerThread.start(); 180 | } 181 | } 182 | //-----------------------------------------------------Poller end 183 | 184 | //-----------------------------------------------------acceptor start 185 | @Override 186 | protected Acceptor createAcceptor() { 187 | return new Acceptor(); 188 | } 189 | 190 | /** 191 | * 后台线程监听客户机的TCP连接 192 | */ 193 | protected class Acceptor extends AbstractEndpoint.Acceptor { 194 | @Override 195 | public void run() { 196 | log.info("NIO Acceptor 开始监听"); 197 | while (running) { 198 | //endpoint阻塞 199 | while (paused && running) { 200 | state = AcceptorState.PAUSED; 201 | try { 202 | Thread.sleep(50); 203 | } catch (InterruptedException e) { 204 | } 205 | } 206 | if (!running) { 207 | break; 208 | } 209 | state = AcceptorState.RUNNING; 210 | //TODO 待添加最大连接数的判断 LimitLatch 211 | SocketChannel clientSocket = null; 212 | try { 213 | //调用阻塞 214 | clientSocket = serverSocket.accept(); 215 | } catch (IOException e) { 216 | e.printStackTrace(); 217 | } 218 | if (null == clientSocket) { 219 | continue; 220 | } 221 | if (running && !paused) { 222 | if (!setSocketOptions(clientSocket)) { 223 | closeSocket(clientSocket); 224 | } 225 | } else { 226 | closeSocket(clientSocket); 227 | } 228 | 229 | } 230 | } 231 | } 232 | //-----------------------------------------------------acceptor end 233 | 234 | //-----------------------------------------------------SocketProcessor start 235 | protected class SocketProcessor extends SocketProcessorBase { 236 | public SocketProcessor(SocketWrapperBase socketWrapper) { 237 | super(socketWrapper); 238 | } 239 | 240 | @Override 241 | public void reset(SocketWrapperBase socketWrapper) { 242 | super.reset(socketWrapper); 243 | } 244 | 245 | @Override 246 | protected void doRun() { 247 | //TODO 248 | } 249 | } 250 | //-----------------------------------------------------SocketProcessor end 251 | 252 | 253 | } 254 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/net/endpoint/nio/Poller.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.net.endpoint.nio; 2 | 3 | import com.lws.lwebserver.core.net.wrapper.SocketWrapperBase; 4 | import com.lws.lwebserver.core.net.wrapper.nio.NioSocketWrapper; 5 | import com.lws.lwebserver.core.util.SynchronizedQueue; 6 | import lombok.extern.slf4j.Slf4j; 7 | 8 | import java.io.IOException; 9 | import java.nio.channels.*; 10 | import java.util.HashMap; 11 | import java.util.Iterator; 12 | import java.util.Map; 13 | import java.util.concurrent.ConcurrentHashMap; 14 | import java.util.concurrent.atomic.AtomicLong; 15 | 16 | /** 17 | * @author zl 18 | * Poller class 轮询线程 19 | * 主要维护多路复用器和PollerEvent队列 20 | */ 21 | @Slf4j 22 | public class Poller implements Runnable { 23 | 24 | private String pollerName; 25 | private Selector selector; 26 | private NioEndpoint endpoint; 27 | 28 | private final SynchronizedQueue events = 29 | new SynchronizedQueue<>(); 30 | 31 | private volatile boolean close = false; 32 | 33 | private Map sockets;//所有活跃socket 34 | /** 35 | * 用来标识selector的状态,这里的设计方式极其巧妙! 36 | * -1-------阻塞状态(被系统调用select()阻塞) 37 | * 0--------初始状态(select()返回后,回到初始状态) 38 | * >0--------events存在没有被注册的socket, 39 | */ 40 | private AtomicLong wakeupCounter = new AtomicLong(0); 41 | 42 | private volatile int keyCount = 0; 43 | 44 | private long selectorTimeout = 1000; 45 | 46 | public Selector getSelector() { 47 | return selector; 48 | } 49 | 50 | public Poller(NioEndpoint nioEndpoint, String pollerName) throws IOException { 51 | this.selector = Selector.open();//开启多路复用器 52 | this.endpoint = nioEndpoint; 53 | this.pollerName = pollerName; 54 | sockets = new ConcurrentHashMap<>(); 55 | } 56 | 57 | /** 58 | * 结束Poller 59 | */ 60 | protected void destroy() throws IOException { 61 | for (NioSocketWrapper wrapper : sockets.values()) { 62 | wrapper.close(); 63 | } 64 | events.clear(); 65 | close = true;//停止轮询 66 | selector.wakeup(); 67 | } 68 | 69 | public String getPollerName() { 70 | return pollerName; 71 | } 72 | 73 | /** 74 | * 向selector中添加socket,后台线程检查poller中的触发事件, 75 | * 并将socket交给合适的进程进行处理。 76 | */ 77 | @Override 78 | public void run() { 79 | //Loop until destroy() 80 | while (true) { 81 | boolean hasEvents = false; 82 | try { 83 | if (!close) {// 未关闭 84 | events(); 85 | if (wakeupCounter.getAndSet(-1) > 0) { 86 | //代表此时events存在未注册的值,立即返回(执行一个non blocking select) 87 | keyCount = selector.selectNow(); 88 | } else { 89 | keyCount = selector.select(selectorTimeout);//设置阻塞超时时间 90 | } 91 | wakeupCounter.set(0); 92 | } 93 | if (close) {//关闭 94 | try { 95 | selector.close(); 96 | } catch (IOException ioe) { 97 | log.error("endpoint.nio.selectorCloseFail", ioe); 98 | } 99 | break; 100 | } 101 | } catch (Throwable e) { 102 | log.error("", e); 103 | continue; 104 | } 105 | Iterator iterator = 106 | keyCount > 0 ? selector.selectedKeys().iterator() : null; 107 | while (iterator != null && iterator.hasNext()) { 108 | SelectionKey sk = iterator.next(); 109 | /** 110 | * 获得可操作对象 111 | */ 112 | NioSocketWrapper attachment = (NioSocketWrapper) sk.attachment(); 113 | if (!sk.isReadable()) { 114 | iterator.remove(); 115 | } 116 | if (null == attachment) { 117 | iterator.remove(); 118 | } else { 119 | iterator.remove(); 120 | 121 | /**粗略概况一下接下来的流程(Tomcat源码的流程,但是接下来我采用的是简化的流程) 122 | * 123 | * |__交由processKey(SelectionKey sk, NioSocketWrapper attachment),判断endponit,attachment的各种状态,判断出对socket的操作SocketEvent, 124 | * 这里还有对数据拷贝优化系统调用sendfile的支持; 125 | * |__processSocket(),创建处理的线程SocketProcess,放入线程池并且触发线程; 126 | * |__SocketProcessor.run,判断是否是SSL/TLS的请求: |___1) No TLS handshaking required. Let the handler process this socket / event combination. 127 | * |___2) Unable to complete the TLS handshake. Treat it as if the handshake failed. 128 | * |___3) The handshake process reads/writes from/to the socket. status may therefore be OPEN_WRITE once the handshake completes. 129 | * However, the handshake happens when the socket is opened so the status must always be OPEN_READ after it completes. 130 | * It is OK to always set this as it is only used if the handshake completes. 131 | * 上面状态1)请求不是TLS握手,则正常由Handler.process处理。 132 | * |__ process,针对不同协议做相应的一系列处理(还包括对keep-alive的处理)(这里头皮发麻_(:з)∠)_) 133 | * wrapper.registerReadInterest();//此方法对socket再次注册到poller中 134 | * 135 | */ 136 | processKey(sk, attachment); 137 | } 138 | } 139 | } 140 | } 141 | 142 | /** 143 | * 返回可处理的key 144 | * 145 | * @param sk 146 | * @param attachment 147 | */ 148 | protected void processKey(SelectionKey sk, NioSocketWrapper attachment) { 149 | try { 150 | if (close) { 151 | cancelledKey(sk); 152 | } else if (sk.isValid() && attachment != null) { 153 | boolean closeSocket = false; 154 | if (!endpoint.processSocket(attachment)) { 155 | closeSocket = true; 156 | } 157 | if (closeSocket) { 158 | cancelledKey(sk); 159 | } 160 | } else { 161 | cancelledKey(sk); 162 | } 163 | } catch (CancelledKeyException c) { 164 | cancelledKey(sk); 165 | } catch (Throwable t) { 166 | log.error("", t); 167 | } 168 | } 169 | 170 | /** 171 | * 注册到PollerEvent 172 | * 173 | * @param socketChannel 174 | * @param isNew 175 | */ 176 | public void register(SocketChannel socketChannel, boolean isNew) { 177 | NioSocketWrapper socketWrapper; 178 | if (isNew) {//is new socket 179 | socketWrapper = new NioSocketWrapper(socketChannel, endpoint, this);//包装socketchannel 180 | sockets.put(socketChannel, socketWrapper);//缓存住,用于管理socket 181 | } else {//keep-alive 长连接 182 | socketWrapper = sockets.get(socketChannel); 183 | socketWrapper.setWorking(false); 184 | } 185 | socketWrapper.setWaitBegin(System.currentTimeMillis()); 186 | addEvent(new PollerEvent(socketWrapper));//注册到PollerEvent 187 | } 188 | 189 | private void addEvent(PollerEvent event) { 190 | events.offer(event); 191 | /** 192 | *某个线程调用select()方法后阻塞了,即使没有通道已经就绪,也有办法让其从select()方法返回。 193 | * 只要让其它线程在第一个线程调用select()方法的那个对象上调用Selector.wakeup()方法即可。 194 | * 阻塞在select()方法上的线程会立马返回,然后注册evens中的PollerEvent 195 | */ 196 | if (wakeupCounter.incrementAndGet() == 0) selector.wakeup(); 197 | } 198 | 199 | /** 200 | * 将队列中的注册事件全部执行(注册到selector),并且清空队列 201 | * 202 | * @return 203 | */ 204 | private boolean events() { 205 | boolean result = false; 206 | PollerEvent pe = null; 207 | /** 208 | * pop() 从此列表所表示的堆栈处弹出一个元素; 209 | * poll() 获取并移除此列表的头(第一个元素); 210 | * 将队列中的注册事件全部执行,并且清空队列 211 | */ 212 | for (int i = 0, size = events.size(); i < size && (pe = events.poll()) != null; i++) { 213 | result = true; 214 | pe.run(); 215 | pe.reset(); 216 | //TODO 这里tomcat里做了个缓存操作 eventCache 217 | } 218 | return result; 219 | } 220 | 221 | public NioSocketWrapper cancelledKey(SelectionKey key) { 222 | NioSocketWrapper ka = null; 223 | try { 224 | if (null == key) { 225 | return null; 226 | } 227 | ka = (NioSocketWrapper) key.attach(null); 228 | /* if(null!=ka){ 229 | 230 | }*/ 231 | if (key.isValid()) { 232 | key.cancel(); 233 | } 234 | if (null != ka) { 235 | try { 236 | ka.getSocket().close(); 237 | } catch (Exception e) { 238 | log.info("endpoint.debug.socketCloseFail" + e); 239 | } 240 | } 241 | if (key.channel().isOpen()) { 242 | try { 243 | key.channel().close(); 244 | } catch (Exception e) { 245 | log.info("endpoint.debug.channelCloseFail" + e); 246 | } 247 | } 248 | } catch (Throwable e) { 249 | if (log.isDebugEnabled()) log.error("", e); 250 | } 251 | return ka; 252 | } 253 | 254 | 255 | //-----------------------------------------------------PollerEvent start 256 | 257 | /** 258 | * Cacheable object for poller events to avoid GC 259 | * 缓存轮询事件,避免GC 260 | * 并将事件在合适时机注册到Selector中 261 | * 注意这里涉及两种注册: 262 | * 1)accept()到客户机的socketchannel后注册到PollerEvent队列(events); 263 | * 2)然后轮询线程(Poller),在每一次轮询之前,调用events(),将所有PollerEvent,注册到Selector. 264 | */ 265 | private static class PollerEvent implements Runnable { 266 | 267 | private NioSocketWrapper socketWrapper; 268 | 269 | public PollerEvent(NioSocketWrapper socketWrapper) { 270 | reset(socketWrapper); 271 | } 272 | 273 | public void reset(NioSocketWrapper w) { 274 | socketWrapper = w; 275 | } 276 | 277 | public void reset() { 278 | reset(null); 279 | } 280 | 281 | @Override 282 | public void run() { 283 | log.info("将SocketChannel的读事件注册到Poller的selector中"); 284 | try { 285 | if (socketWrapper.getSocket().isOpen()) { 286 | /**注册并且标记当前服务的通道状态 287 | * register(Selector,int) 288 | * int---状态编码 289 | * OP_READ: 可读标记位 290 | * OP_WRITE: 可写标记位 291 | * OP_CONNECT: 连接建立后的标记 292 | * OP_ACCEPT: 连接成功的标记位 293 | */ 294 | socketWrapper.getSocket().register(socketWrapper.getPoller().getSelector(), SelectionKey.OP_READ, socketWrapper); 295 | } else { 296 | log.error("socket已经被关闭,无法注册到Poller", socketWrapper.getSocket()); 297 | } 298 | } catch (ClosedChannelException e) { 299 | e.printStackTrace(); 300 | } 301 | } 302 | } 303 | //-----------------------------------------------------PollerEvent end 304 | 305 | /** 306 | * 清理长连接的socket 307 | */ 308 | public void cleanTimeoutSockets() { 309 | for (Iterator> it = sockets.entrySet().iterator(); it.hasNext(); ) { 310 | NioSocketWrapper wrapper = it.next().getValue(); 311 | log.info("缓存中的socket:{}", wrapper); 312 | if (!wrapper.getSocket().isConnected()) { 313 | log.info("该socket已被关闭"); 314 | it.remove(); 315 | continue; 316 | } 317 | if (wrapper.isWorking()) { 318 | log.info("该socket正在工作中,不予关闭"); 319 | continue; 320 | } 321 | if (System.currentTimeMillis() - wrapper.getWaitBegin() > endpoint.getKeepAliveTimeout()) { 322 | // 反注册 323 | log.info("{} keepAlive已过期", wrapper.getSocket()); 324 | try { 325 | wrapper.close(); 326 | } catch (IOException e) { 327 | e.printStackTrace(); 328 | } 329 | it.remove(); 330 | } 331 | } 332 | } 333 | 334 | } 335 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/net/handler/AbstractRequestHandler.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.net.handler; 2 | 3 | import com.lws.lwebserver.core.context.ServletContext; 4 | import com.lws.lwebserver.core.exception.ServerErrorException; 5 | import com.lws.lwebserver.core.exception.ServletNotFoundException; 6 | import com.lws.lwebserver.core.exception.base.ServletException; 7 | import com.lws.lwebserver.core.exception.handler.ExceptionHandler; 8 | import com.lws.lwebserver.core.exception.FilterNotFoundException; 9 | import com.lws.lwebserver.core.fliter.Filter; 10 | import com.lws.lwebserver.core.fliter.FilterChain; 11 | import com.lws.lwebserver.core.net.wrapper.SocketWrapperBase; 12 | import com.lws.lwebserver.core.request.Request; 13 | import com.lws.lwebserver.core.resource.ResourceHandler; 14 | import com.lws.lwebserver.core.response.Response; 15 | import com.lws.lwebserver.core.servlet.Servlet; 16 | import lombok.Getter; 17 | import lombok.extern.slf4j.Slf4j; 18 | 19 | import java.util.List; 20 | 21 | /** 22 | * @Author: zl 23 | * @Date: 2019/3/16 10:29 24 | */ 25 | @Slf4j 26 | @Getter 27 | public abstract class AbstractRequestHandler implements FilterChain, Runnable { 28 | 29 | protected Request request; 30 | protected Response response; 31 | protected SocketWrapperBase socketWrapper; 32 | protected ServletContext servletContext; 33 | protected ExceptionHandler exceptionHandler; 34 | protected ResourceHandler resourceHandler; 35 | protected boolean isFinished; 36 | protected Servlet servlet; 37 | protected List filters; 38 | private int filterIndex = 0; 39 | 40 | public AbstractRequestHandler(SocketWrapperBase socketWrapper, ServletContext servletContext, 41 | ExceptionHandler exceptionHandler, 42 | ResourceHandler resourceHandler, 43 | Request request, Response response) throws ServletNotFoundException, FilterNotFoundException { 44 | this.socketWrapper = socketWrapper; 45 | this.servletContext = servletContext; 46 | this.exceptionHandler = exceptionHandler; 47 | this.resourceHandler = resourceHandler; 48 | this.isFinished = false; 49 | this.request = request; 50 | this.response = response; 51 | request.setServletContext(servletContext); 52 | request.setRequestHandler(this); 53 | response.setRequestHandler(this); 54 | // 根据url查询匹配的servlet,结果是0个或1个 55 | servlet = servletContext.mapServlet(request.getUrl()); 56 | // 根据url查询匹配的filter,结果是0个或多个 57 | filters = servletContext.mapFilter(request.getUrl()); 58 | } 59 | 60 | /** 61 | * 入口 62 | */ 63 | @Override 64 | public void run() { 65 | // 如果没有filter,则直接执行servlet 66 | if (filters.isEmpty()) { 67 | service(); 68 | } else { 69 | // 先执行filter 70 | doFilter(request, response); 71 | } 72 | } 73 | 74 | /** 75 | * 递归执行,自定义filter中如果同意放行,那么会调用filterChain(也就是requestHandler)的doiFilter方法, 76 | * 此时会执行下一个filter的doFilter方法; 77 | * 如果不放行,那么会在sendRedirect之后将响应数据写回客户端,结束; 78 | * 如果所有Filter都执行完毕,那么会调用service方法,执行servlet逻辑 79 | * @param request 80 | * @param response 81 | */ 82 | @Override 83 | public void doFilter(Request request, Response response) { 84 | if (filterIndex < filters.size()) { 85 | filters.get(filterIndex++).doFilter(request, response, this); 86 | } else { 87 | service(); 88 | } 89 | } 90 | 91 | /** 92 | * 调用servlet 93 | */ 94 | private void service() { 95 | try { 96 | //处理动态资源,交由某个Servlet执行 97 | //Servlet是单例多线程 98 | //Servlet在RequestHandler中执行 99 | servlet.service(request, response); 100 | } catch (ServletException e) { 101 | log.error("ServletException "+e); 102 | exceptionHandler.handle(e, response, socketWrapper); 103 | } catch (Exception e) { 104 | //其他未知异常 105 | e.printStackTrace(); 106 | exceptionHandler.handle(new ServerErrorException(), response, socketWrapper); 107 | } finally { 108 | if (!isFinished) { 109 | flushResponse(); 110 | } 111 | } 112 | log.info("请求处理完毕"); 113 | } 114 | 115 | /** 116 | * 响应数据写回到客户端 117 | */ 118 | public abstract void flushResponse(); 119 | } 120 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/net/handler/aio/AioRequestHandler.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.net.handler.aio; 2 | 3 | import com.lws.lwebserver.core.context.ServletContext; 4 | import com.lws.lwebserver.core.context.WebApplication; 5 | import com.lws.lwebserver.core.exception.FilterNotFoundException; 6 | import com.lws.lwebserver.core.exception.ServletNotFoundException; 7 | import com.lws.lwebserver.core.exception.handler.ExceptionHandler; 8 | import com.lws.lwebserver.core.net.handler.AbstractRequestHandler; 9 | import com.lws.lwebserver.core.net.wrapper.SocketWrapperBase; 10 | import com.lws.lwebserver.core.net.wrapper.aio.AioSocketWrapper; 11 | import com.lws.lwebserver.core.request.Request; 12 | import com.lws.lwebserver.core.resource.ResourceHandler; 13 | import com.lws.lwebserver.core.response.Response; 14 | import lombok.extern.slf4j.Slf4j; 15 | 16 | import java.nio.ByteBuffer; 17 | import java.nio.channels.AsynchronousSocketChannel; 18 | import java.nio.channels.CompletionHandler; 19 | import java.util.concurrent.TimeUnit; 20 | 21 | /** 22 | * @Author: zl 23 | * @Date: 2019/3/27 15:01 24 | */ 25 | @Slf4j 26 | public class AioRequestHandler extends AbstractRequestHandler { 27 | private CompletionHandler readHandler; 28 | public AioRequestHandler(SocketWrapperBase socketWrapper, 29 | ServletContext servletContext, 30 | ExceptionHandler exceptionHandler, 31 | ResourceHandler resourceHandler, 32 | Request request, Response response, 33 | CompletionHandler readHandler) throws ServletNotFoundException, FilterNotFoundException 34 | { 35 | super(socketWrapper, servletContext, exceptionHandler, resourceHandler, request, response); 36 | this.readHandler=readHandler; 37 | } 38 | 39 | @Override 40 | public void flushResponse() { 41 | isFinished = true; 42 | ByteBuffer[] responseData = response.getResponseByteBuffer(); 43 | AioSocketWrapper aioSocketWrapper = (AioSocketWrapper) socketWrapper; 44 | AsynchronousSocketChannel socketChannel = aioSocketWrapper.getSocket(); 45 | /** 46 | * TODO 含义 47 | */ 48 | socketChannel.write(responseData, 0, 2, 0L, TimeUnit.MILLISECONDS, null, new CompletionHandler() { 49 | 50 | @Override 51 | public void completed(Long result, Object attachment) { 52 | log.info("写入完毕..."); 53 | ByteBuffer byteBuffer = ByteBuffer.allocate(1024); 54 | socketChannel.read(byteBuffer, byteBuffer, readHandler); 55 | } 56 | 57 | @Override 58 | public void failed(Throwable e, Object attachment) { 59 | log.info("写入失败..."); 60 | e.printStackTrace(); 61 | } 62 | }); 63 | WebApplication.getServletContext().afterRequestDestroyed(request); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/net/handler/bio/BioRequestHandler.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.net.handler.bio; 2 | 3 | import com.lws.lwebserver.core.context.ServletContext; 4 | import com.lws.lwebserver.core.context.WebApplication; 5 | import com.lws.lwebserver.core.exception.ServletNotFoundException; 6 | import com.lws.lwebserver.core.exception.handler.ExceptionHandler; 7 | import com.lws.lwebserver.core.exception.FilterNotFoundException; 8 | import com.lws.lwebserver.core.net.handler.AbstractRequestHandler; 9 | import com.lws.lwebserver.core.net.wrapper.SocketWrapperBase; 10 | import com.lws.lwebserver.core.net.wrapper.bio.BioSocketWrapper; 11 | import com.lws.lwebserver.core.request.Request; 12 | import com.lws.lwebserver.core.resource.ResourceHandler; 13 | import com.lws.lwebserver.core.response.Response; 14 | import lombok.extern.slf4j.Slf4j; 15 | 16 | import java.io.IOException; 17 | import java.io.OutputStream; 18 | 19 | /** 20 | * @Author: zl 21 | * @Date: 2019/3/20 0:18 22 | */ 23 | @Slf4j 24 | public class BioRequestHandler extends AbstractRequestHandler { 25 | public BioRequestHandler(SocketWrapperBase socketWrapper, ServletContext servletContext, ExceptionHandler exceptionHandler, ResourceHandler resourceHandler, Request request, Response response) throws ServletNotFoundException, FilterNotFoundException { 26 | super(socketWrapper, servletContext, exceptionHandler, resourceHandler, request, response); 27 | } 28 | 29 | @Override 30 | public void flushResponse() { 31 | isFinished = true; 32 | BioSocketWrapper bioSocketWrapper = (BioSocketWrapper) socketWrapper; 33 | byte[] bytes = response.getResponseBytes(); 34 | OutputStream os = null; 35 | try { 36 | os = bioSocketWrapper.getSocket().getOutputStream(); 37 | os.write(bytes); 38 | os.flush(); 39 | } catch (IOException e) { 40 | e.printStackTrace(); 41 | log.error("socket closed"); 42 | } finally { 43 | try { 44 | if(os!=null){ 45 | os.close(); 46 | } 47 | bioSocketWrapper.close(); 48 | } catch (IOException e) { 49 | e.printStackTrace(); 50 | } 51 | } 52 | WebApplication.getServletContext().afterRequestDestroyed(request); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/net/handler/nio/NioRequestHandler.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.net.handler.nio; 2 | 3 | import com.lws.lwebserver.core.context.ServletContext; 4 | import com.lws.lwebserver.core.context.WebApplication; 5 | import com.lws.lwebserver.core.exception.ServletNotFoundException; 6 | import com.lws.lwebserver.core.exception.handler.ExceptionHandler; 7 | import com.lws.lwebserver.core.exception.FilterNotFoundException; 8 | import com.lws.lwebserver.core.net.handler.AbstractRequestHandler; 9 | import com.lws.lwebserver.core.net.wrapper.SocketWrapperBase; 10 | import com.lws.lwebserver.core.net.wrapper.nio.NioSocketWrapper; 11 | import com.lws.lwebserver.core.request.Request; 12 | import com.lws.lwebserver.core.resource.ResourceHandler; 13 | import com.lws.lwebserver.core.response.Response; 14 | import lombok.Getter; 15 | import lombok.Setter; 16 | import lombok.extern.slf4j.Slf4j; 17 | 18 | import java.io.IOException; 19 | import java.nio.ByteBuffer; 20 | import java.util.List; 21 | 22 | /** 23 | * @Author: zl 24 | * @Date: 2019/3/17 23:42 25 | */ 26 | @Setter 27 | @Getter 28 | @Slf4j 29 | public class NioRequestHandler extends AbstractRequestHandler { 30 | 31 | public NioRequestHandler(SocketWrapperBase socketWrapper, ServletContext servletContext, ExceptionHandler exceptionHandler, ResourceHandler resourceHandler, Request request, Response response) throws ServletNotFoundException, FilterNotFoundException { 32 | super(socketWrapper, servletContext, exceptionHandler, resourceHandler, request, response); 33 | } 34 | 35 | /** 36 | * 写入后会根据请求头Connection来判断是关闭连接还是重新将连接放回Poller,实现保活 37 | */ 38 | @Override 39 | public void flushResponse() { 40 | isFinished = true; 41 | NioSocketWrapper nioSocketWrapper = (NioSocketWrapper) socketWrapper; 42 | ByteBuffer[] responseData = response.getResponseByteBuffer(); 43 | try { 44 | nioSocketWrapper.getSocket().write(responseData); 45 | List connection = request.getHeaders().get("Connection"); 46 | if (connection != null && connection.get(0).equals("close")) { 47 | log.info("CLOSE: 客户端连接{} 已关闭", nioSocketWrapper.getSocket()); 48 | nioSocketWrapper.close(); 49 | } else { 50 | // keep-alive 重新注册到Poller中 51 | log.info("KEEP-ALIVE: 客户端连接{} 重新注册到Poller中", nioSocketWrapper.getSocket()); 52 | nioSocketWrapper.getPoller().register(nioSocketWrapper.getSocket(), false); 53 | } 54 | } catch (IOException e) { 55 | e.printStackTrace(); 56 | } 57 | WebApplication.getServletContext().afterRequestDestroyed(request); 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/net/wrapper/SocketWrapperBase.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.net.wrapper; 2 | 3 | import com.lws.lwebserver.core.net.endpoint.AbstractEndpoint; 4 | import lombok.Data; 5 | import lombok.extern.slf4j.Slf4j; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * @author zl 11 | */ 12 | @Slf4j 13 | @Data 14 | public abstract class SocketWrapperBase { 15 | 16 | protected final E socket; 17 | protected final AbstractEndpoint endpoint; 18 | /** 19 | * 当前socket状态,用来判断长连接是否应该被清除 20 | * 如果缓存在poller中的socket的isWorking==false,且超过了 21 | * 最长保活时间,则将被清理 22 | */ 23 | public volatile boolean isWorking; 24 | 25 | public SocketWrapperBase(E socket, AbstractEndpoint endpoint) { 26 | this.socket = socket; 27 | this.endpoint = endpoint; 28 | isWorking=false; 29 | } 30 | 31 | 32 | public abstract void close() throws IOException; 33 | 34 | public E getSocket() { 35 | return socket; 36 | } 37 | public abstract boolean isClosed(); 38 | 39 | } 40 | 41 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/net/wrapper/aio/AioSocketWrapper.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.net.wrapper.aio; 2 | 3 | import com.lws.lwebserver.core.net.endpoint.AbstractEndpoint; 4 | import com.lws.lwebserver.core.net.wrapper.SocketWrapperBase; 5 | 6 | import java.io.IOException; 7 | import java.nio.channels.AsynchronousSocketChannel; 8 | 9 | /** 10 | * @Author: zl 11 | * @Date: 2019/3/27 15:02 12 | */ 13 | public class AioSocketWrapper extends SocketWrapperBase { 14 | 15 | public AioSocketWrapper(AsynchronousSocketChannel socket, AbstractEndpoint endpoint) { 16 | super(socket, endpoint); 17 | } 18 | 19 | @Override 20 | public void close() throws IOException { 21 | socket.close(); 22 | } 23 | 24 | @Override 25 | public AsynchronousSocketChannel getSocket() { 26 | return super.getSocket(); 27 | } 28 | 29 | @Override 30 | public boolean isClosed() { 31 | return !socket.isOpen(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/net/wrapper/bio/BioSocketWrapper.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.net.wrapper.bio; 2 | 3 | import com.lws.lwebserver.core.net.endpoint.AbstractEndpoint; 4 | import com.lws.lwebserver.core.net.wrapper.SocketWrapperBase; 5 | 6 | import java.io.IOException; 7 | import java.net.Socket; 8 | 9 | /** 10 | * @Author: zl 11 | * @Date: 2019/3/19 23:38 12 | */ 13 | public class BioSocketWrapper extends SocketWrapperBase { 14 | 15 | public BioSocketWrapper(Socket socket, AbstractEndpoint endpoint) { 16 | super(socket, endpoint); 17 | } 18 | 19 | @Override 20 | public void close() throws IOException { 21 | socket.close(); 22 | } 23 | 24 | @Override 25 | public Socket getSocket() { 26 | return super.getSocket(); 27 | } 28 | 29 | @Override 30 | public boolean isClosed() { 31 | return socket.isClosed(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/net/wrapper/nio/NioSocketWrapper.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.net.wrapper.nio; 2 | 3 | import com.lws.lwebserver.core.net.endpoint.AbstractEndpoint; 4 | import com.lws.lwebserver.core.net.endpoint.nio.NioEndpoint; 5 | import com.lws.lwebserver.core.net.endpoint.nio.Poller; 6 | import com.lws.lwebserver.core.net.wrapper.SocketWrapperBase; 7 | import lombok.Data; 8 | 9 | import java.io.IOException; 10 | import java.nio.channels.SocketChannel; 11 | 12 | 13 | /** 14 | * @Author: zl 15 | * @Date: 2019/3/12 21:18 16 | */ 17 | @Data 18 | public class NioSocketWrapper extends SocketWrapperBase { 19 | 20 | 21 | 22 | private Poller poller = null; 23 | private volatile long waitBegin;//在建立连接/keep-alive时设置waitBegin 24 | 25 | 26 | public NioSocketWrapper(SocketChannel socket, NioEndpoint endpoint, Poller poller) { 27 | super(socket, endpoint); 28 | this.poller=poller; 29 | } 30 | 31 | @Override 32 | public void close() throws IOException { 33 | socket.keyFor(poller.getSelector()).cancel();//关闭客户通道 34 | socket.close(); 35 | } 36 | 37 | @Override 38 | public boolean isClosed() { 39 | return !getSocket().isOpen(); 40 | } 41 | 42 | @Override 43 | public SocketChannel getSocket() { 44 | return super.getSocket(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/request/Request.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.request; 2 | 3 | import com.lws.lwebserver.core.constant.CharConstant; 4 | import com.lws.lwebserver.core.constant.CharsetProperties; 5 | import com.lws.lwebserver.core.context.ServletContext; 6 | import com.lws.lwebserver.core.context.WebApplication; 7 | import com.lws.lwebserver.core.cookie.Cookie; 8 | import com.lws.lwebserver.core.enumeration.RequestMethod; 9 | import com.lws.lwebserver.core.exception.RequestInvalidException; 10 | import com.lws.lwebserver.core.exception.RequestParseException; 11 | 12 | import com.lws.lwebserver.core.net.handler.AbstractRequestHandler; 13 | import com.lws.lwebserver.core.request.dispatcher.RequestDispatcher; 14 | import com.lws.lwebserver.core.request.dispatcher.impl.ApplicationRequestDispatcher; 15 | 16 | import com.lws.lwebserver.core.session.HttpSession; 17 | import lombok.Data; 18 | import lombok.extern.slf4j.Slf4j; 19 | 20 | import java.io.BufferedInputStream; 21 | import java.io.IOException; 22 | import java.io.InputStream; 23 | import java.io.UnsupportedEncodingException; 24 | import java.net.URLDecoder; 25 | import java.util.Arrays; 26 | import java.util.HashMap; 27 | import java.util.List; 28 | import java.util.Map; 29 | 30 | /** 31 | * Created by zl on 2019/03/01. 32 | *

33 | * GET /search?hl=zh-CN&source=hp&q=domety&aq=f&oq= HTTP/1.1 34 | * Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, 35 | * application/msword, application/x-silverlight 36 | * Referer: http://www.google.cn/ 37 | * Accept-Language: zh-cn 38 | * Accept-Encoding: gzip, deflate 39 | * User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; TheWorld) 40 | * Host: www.google.cn 41 | * Connection: Keep-Alive 42 | * Cookie: PREF=ID=80a06da87be9ae3c:U=f7167333e2c3b714:NW=1:TM=1261551909:LM=1261551917:S=ybYcq2wpfefs4V9g; 43 | * NID=31=ojj8d-IygaEtSxLgaJmqSjVhCspkviJrB6omjamNrSm8lZhKy_yMfO2M4QMRKcH1g0iQv9u-2hfBW7bUFwVh7pGaRUb0RnHcJU37y- 44 | * FxlRugatx63JLv7CWMD6UB_O_r 45 | * 46 | * POST /search HTTP/1.1 47 | * Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, 48 | * application/msword, application/x-silverlight 49 | * Referer: http://www.google.cn/ 50 | * Accept-Language: zh-cn 51 | * Accept-Encoding: gzip,deflate 52 | * User-Agent: Mozilla/4.0(compatible;MSIE6.0;Windows NT5.1;SV1;.NET CLR2.0.50727;TheWorld) 53 | * Host: www.google.cn 54 | * Connection: Keep-Alive 55 | * Cookie: PREF=ID=80a06da87be9ae3c:U=f7167333e2c3b714:NW=1:TM=1261551909:LM=1261551917:S=ybYcq2wpfefs4V9g; 56 | * NID=31=ojj8d-IygaEtSxLgaJmqSjVhCspkviJrB6omjamNrSm8lZhKy_yMfO2M4QMRKcH1g0iQv9u-2hfBW7bUFwVh7pGaRUb0RnHcJU37y- 57 | * FxlRugatx63JLv7CWMD6UB_O_r 58 | *

59 | * hl=zh-CN&source=hp&q=domety 60 | */ 61 | 62 | @Data 63 | @Slf4j 64 | public class Request { 65 | 66 | private AbstractRequestHandler requestHandler; 67 | private RequestMethod method; 68 | private String url; 69 | private Map> params; 70 | private Map> headers; 71 | private Map attributes; 72 | private ServletContext servletContext; 73 | private Cookie[] cookies; 74 | private HttpSession session; 75 | 76 | /** 77 | * 获取queryString或者body(表单格式)的键值类型的数据 78 | * @param key 79 | * @return 80 | */ 81 | public String getParameter(String key) { 82 | List params = this.params.get(key); 83 | if(params == null) { 84 | return null; 85 | } 86 | return params.get(0); 87 | } 88 | 89 | 90 | /** 91 | * 解析HTTP请求 92 | * 读取请求体只能使用字节流,使用字符流读不到 93 | * @param data 94 | * @throws RequestParseException 95 | */ 96 | public Request(byte[] data) throws RequestParseException, RequestInvalidException, IOException { 97 | this.attributes = new HashMap<>(); 98 | String[] lines = null; 99 | try { 100 | //支持中文,对中文进行URL解码 101 | lines = URLDecoder.decode(new String(data, CharsetProperties.UTF_8_CHARSET), CharsetProperties.UTF_8).split(CharConstant.CRLF); 102 | } catch (UnsupportedEncodingException e) { 103 | e.printStackTrace(); 104 | } 105 | log.info("Request读取完毕"); 106 | log.info("请求行: {}", Arrays.toString(lines)); 107 | if (lines.length <= 1) { 108 | throw new RequestInvalidException(); 109 | } 110 | try { 111 | parseHeaders(lines); 112 | if (headers.containsKey("Content-Length") && !headers.get("Content-Length").get(0).equals("0")) { 113 | parseBody(lines[lines.length - 1]); 114 | } 115 | } catch (Throwable e) { 116 | e.printStackTrace(); 117 | throw new RequestParseException(); 118 | } 119 | 120 | WebApplication.getServletContext().afterRequestCreated(this); 121 | } 122 | 123 | public void setAttribute(String key, Object value) { 124 | attributes.put(key, value); 125 | } 126 | 127 | public Object getAttribute(String key) { 128 | return attributes.get(key); 129 | } 130 | 131 | public RequestDispatcher getRequestDispatcher(String url) { 132 | return new ApplicationRequestDispatcher(url); 133 | } 134 | 135 | /** 136 | * 如果请求报文中携带JSESSIONID这个Cookie,那么取出对应的session 137 | * 否则创建一个Session,并在响应报文中添加一个响应头Set-Cookie: JSESSIONID=D5A5C79F3C8E8653BC8B4F0860BFDBCD 138 | * 所有从请求报文中得到的Cookie,都会在响应报文中返回 139 | * 服务器只会在客户端第一次请求响应的时候,在响应头上添加Set-Cookie:“JSESSIONID=XXXXXXX”信息, 140 | * 接下来在同一个会话的第二第三次响应头里,是不会添加Set-Cookie:“JSESSIONID=XXXXXXX”信息的; 141 | * 即,如果在Cookie中读到的JSESSIONID,那么不会创建新的Session,也不会在响应头中加入Set-Cookie:“JSESSIONID=XXXXXXX” 142 | * 如果没有读到,那么会创建新的Session,并在响应头中加入Set-Cookie:“JSESSIONID=XXXXXXX” 143 | * 如果没有调用getSession,那么不会创建新的Session 144 | * 145 | * @param createIfNotExists 如果为true,那么在不存在session时会创建一个新的session;否则会直接返回null 146 | * @return HttpSession 147 | */ 148 | public HttpSession getSession(boolean createIfNotExists) { 149 | if (session != null) { 150 | return session; 151 | } 152 | for (Cookie cookie : cookies) { 153 | if (cookie.getKey().equals("JSESSIONID")) { 154 | HttpSession currentSession = servletContext.getSession(cookie.getValue()); 155 | if (currentSession != null) { 156 | session = currentSession; 157 | return session; 158 | } 159 | } 160 | } 161 | if (!createIfNotExists) { 162 | return null; 163 | } 164 | session = servletContext.createSession(requestHandler.getResponse()); 165 | return session; 166 | } 167 | 168 | public HttpSession getSession() { 169 | return getSession(true); 170 | } 171 | 172 | public String getServletPath() { 173 | return url; 174 | } 175 | 176 | /** 177 | * GET /index?size=1 HTTP/1.1 178 | * Host: www.enjoytoday.cn 179 | * Connection: keep-alive 180 | * Upgrade-Insecure-Requests: 1 181 | * User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.84 Safari/537.36 182 | * Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp, 183 | * @param lines 184 | */ 185 | private void parseHeaders(String[] lines) { 186 | log.info("解析请求头"); 187 | String firstLine = lines[0]; 188 | //解析方法 189 | String[] firstLineSlices = firstLine.split(CharConstant.BLANK); 190 | this.method = RequestMethod.valueOf(firstLineSlices[0]); 191 | log.debug("method:{}", this.method); 192 | 193 | //解析URL 194 | String rawURL = firstLineSlices[1]; 195 | String[] urlSlices = rawURL.split("\\?"); 196 | this.url = urlSlices[0]; 197 | log.debug("url:{}", this.url); 198 | 199 | //解析URL参数 200 | if (urlSlices.length > 1) { 201 | parseParams(urlSlices[1]); 202 | } 203 | log.debug("params:{}", this.params); 204 | 205 | 206 | //解析请求头 207 | String header; 208 | this.headers = new HashMap<>(); 209 | for (int i = 1; i < lines.length; i++) { 210 | header = lines[i]; 211 | if (header.equals("")) { 212 | break; 213 | } 214 | int colonIndex = header.indexOf(':'); 215 | String key = header.substring(0, colonIndex); 216 | String[] values = header.substring(colonIndex + 2).split(","); 217 | headers.put(key, Arrays.asList(values)); 218 | } 219 | log.debug("headers:{}", this.headers); 220 | 221 | //解析Cookie 222 | 223 | if (headers.containsKey("Cookie")) { 224 | String[] rawCookies = headers.get("Cookie").get(0).split("; "); 225 | this.cookies = new Cookie[rawCookies.length]; 226 | for (int i = 0; i < rawCookies.length; i++) { 227 | String[] kv = rawCookies[i].split("="); 228 | this.cookies[i] = new Cookie(kv[0], kv[1]); 229 | } 230 | headers.remove("Cookie"); 231 | } else { 232 | this.cookies = new Cookie[0]; 233 | } 234 | log.info("Cookies:{}", Arrays.toString(cookies)); 235 | } 236 | 237 | private void parseBody(String body) { 238 | log.info("解析请求体"); 239 | byte[] bytes = body.getBytes(CharsetProperties.UTF_8_CHARSET); 240 | List lengths = this.headers.get("Content-Length"); 241 | if (lengths != null) { 242 | int length = Integer.parseInt(lengths.get(0)); 243 | log.info("length:{}", length); 244 | parseParams(new String(bytes, 0, Math.min(length,bytes.length), CharsetProperties.UTF_8_CHARSET).trim()); 245 | } else { 246 | parseParams(body.trim()); 247 | } 248 | if (this.params == null) { 249 | this.params = new HashMap<>(); 250 | } 251 | } 252 | 253 | private void parseParams(String params) { 254 | String[] urlParams = params.split("&"); 255 | if (this.params == null) { 256 | this.params = new HashMap<>(); 257 | } 258 | for (String param : urlParams) { 259 | String[] kv = param.split("="); 260 | String key = kv[0]; 261 | String[] values = kv[1].split(","); 262 | 263 | this.params.put(key, Arrays.asList(values)); 264 | } 265 | } 266 | } -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/request/dispatcher/RequestDispatcher.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.request.dispatcher; 2 | 3 | import com.lws.lwebserver.core.exception.base.ServletException; 4 | import com.lws.lwebserver.core.request.Request; 5 | import com.lws.lwebserver.core.response.Response; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * Created by zl on 2019/03/01. 11 | */ 12 | public interface RequestDispatcher { 13 | 14 | void forward(Request request, Response response) throws ServletException, IOException; 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/request/dispatcher/impl/ApplicationRequestDispatcher.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.request.dispatcher.impl; 2 | 3 | import com.lws.lwebserver.core.constant.CharsetProperties; 4 | import com.lws.lwebserver.core.exception.ResourceNotFoundException; 5 | import com.lws.lwebserver.core.exception.base.ServletException; 6 | import com.lws.lwebserver.core.request.Request; 7 | import com.lws.lwebserver.core.request.dispatcher.RequestDispatcher; 8 | import com.lws.lwebserver.core.resource.ResourceHandler; 9 | import com.lws.lwebserver.core.response.Response; 10 | import com.lws.lwebserver.core.template.TemplateResolver; 11 | import lombok.AllArgsConstructor; 12 | import lombok.Data; 13 | import lombok.NoArgsConstructor; 14 | import com.lws.lwebserver.core.util.IOUtil; 15 | import com.lws.lwebserver.core.util.MimeTypeUtil; 16 | import lombok.extern.slf4j.Slf4j; 17 | 18 | import java.io.IOException; 19 | 20 | /** 21 | * Created by zl on 2019/03/01. 22 | */ 23 | @Data 24 | @NoArgsConstructor 25 | @AllArgsConstructor 26 | @Slf4j 27 | public class ApplicationRequestDispatcher implements RequestDispatcher { 28 | private String url; 29 | 30 | @Override 31 | public void forward(Request request, Response response) throws ServletException, IOException { 32 | if (ResourceHandler.class.getResource(url) == null) { 33 | throw new ResourceNotFoundException(); 34 | } 35 | log.info("forward至 {} 页面",url); 36 | String body = TemplateResolver.resolve(new String(IOUtil.getBytesFromFile(url), CharsetProperties.UTF_8_CHARSET),request); 37 | response.setContentType(MimeTypeUtil.getTypes(url)); 38 | response.setBody(body.getBytes(CharsetProperties.UTF_8_CHARSET)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/resource/ResourceHandler.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.resource; 2 | 3 | import com.lws.lwebserver.core.constant.CharsetProperties; 4 | import com.lws.lwebserver.core.exception.RequestParseException; 5 | import com.lws.lwebserver.core.exception.ResourceNotFoundException; 6 | import com.lws.lwebserver.core.exception.base.ServletException; 7 | import com.lws.lwebserver.core.exception.handler.ExceptionHandler; 8 | import com.lws.lwebserver.core.net.wrapper.nio.NioSocketWrapper; 9 | import com.lws.lwebserver.core.request.Request; 10 | import com.lws.lwebserver.core.response.Response; 11 | import com.lws.lwebserver.core.template.TemplateResolver; 12 | import lombok.extern.slf4j.Slf4j; 13 | import com.lws.lwebserver.core.util.IOUtil; 14 | import com.lws.lwebserver.core.util.MimeTypeUtil; 15 | 16 | import java.io.IOException; 17 | 18 | /** 19 | * Created by zl on 2019/03/01. 20 | * 处理静态资源 21 | */ 22 | @Slf4j 23 | public class ResourceHandler { 24 | private ExceptionHandler exceptionHandler; 25 | 26 | public ResourceHandler(ExceptionHandler exceptionHandler) { 27 | this.exceptionHandler = exceptionHandler; 28 | } 29 | 30 | public void handle(Request request, Response response, NioSocketWrapper socketWrapper) { 31 | String url = request.getUrl(); 32 | try { 33 | if (ResourceHandler.class.getResource(url) == null) { 34 | log.info("找不到该资源:{}", url); 35 | throw new ResourceNotFoundException(); 36 | } 37 | byte[] body = IOUtil.getBytesFromFile(url); 38 | if (url.endsWith(".html")) { 39 | body = TemplateResolver 40 | .resolve(new String(body, CharsetProperties.UTF_8_CHARSET), request) 41 | .getBytes(CharsetProperties.UTF_8_CHARSET); 42 | } 43 | response.setContentType(MimeTypeUtil.getTypes(url)); 44 | response.setBody(body); 45 | } catch (IOException e) { 46 | exceptionHandler.handle(new RequestParseException(), response, socketWrapper); 47 | } catch (ServletException e) { 48 | exceptionHandler.handle(e, response, socketWrapper); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/response/Header.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * Created by zl on 2019/03/01. 9 | */ 10 | @Data 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | public class Header { 14 | private String key; 15 | private String value; 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/response/Response.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.response; 2 | 3 | import com.lws.lwebserver.core.cookie.Cookie; 4 | import com.lws.lwebserver.core.enumeration.HttpStatus; 5 | 6 | import com.lws.lwebserver.core.net.handler.AbstractRequestHandler; 7 | import lombok.extern.slf4j.Slf4j; 8 | 9 | import java.nio.ByteBuffer; 10 | import java.util.ArrayList; 11 | import java.util.Date; 12 | import java.util.List; 13 | 14 | import static com.lws.lwebserver.core.constant.CharConstant.BLANK; 15 | import static com.lws.lwebserver.core.constant.CharConstant.CRLF; 16 | import static com.lws.lwebserver.core.constant.CharsetProperties.UTF_8_CHARSET; 17 | import static com.lws.lwebserver.core.constant.Const.DEFAULT_CONTENT_TYPE; 18 | 19 | /** 20 | * Created by zl on 2019/03/01. 21 | * HTTP/1.1 200 OK 22 | * Date: Sat, 31 Dec 2005 23:59:59 GMT 23 | * Content-Type: text/html;constant=ISO-8859-1 24 | * Content-Length: 122 25 | *

26 | * <html> 27 | * <head> 28 | * <title>Wrox Homepage</title> 29 | * </head> 30 | * <body> 31 | * <!-- body goes here --> 32 | * </body> 33 | * </html> 34 | */ 35 | @Slf4j 36 | public class Response { 37 | private StringBuilder headerAppender; 38 | private List cookies; 39 | private List

headers; 40 | private HttpStatus status = HttpStatus.OK; 41 | private String contentType = DEFAULT_CONTENT_TYPE; 42 | private byte[] body = new byte[0]; 43 | private AbstractRequestHandler requestHandler; 44 | 45 | public Response() { 46 | this.headerAppender = new StringBuilder(); 47 | this.cookies = new ArrayList<>(); 48 | this.headers = new ArrayList<>(); 49 | } 50 | 51 | /** 52 | * 设置HTTP Status 53 | * @param status 54 | */ 55 | public void setStatus(HttpStatus status) { 56 | this.status = status; 57 | } 58 | 59 | public void setContentType(String contentType) { 60 | this.contentType = contentType; 61 | } 62 | 63 | public void setBody(byte[] body) { 64 | this.body = body; 65 | } 66 | 67 | 68 | public void addCookie(Cookie cookie) { 69 | cookies.add(cookie); 70 | } 71 | 72 | public void addHeader(Header header) { 73 | headers.add(header); 74 | } 75 | 76 | 77 | private void buildHeader() { 78 | //HTTP/1.1 200 OK 79 | headerAppender.append("HTTP/1.1").append(BLANK).append(status.getCode()).append(BLANK).append(status).append(CRLF); 80 | //Date: Sat, 31 Dec 2005 23:59:59 GMT 81 | headerAppender.append("Date:").append(BLANK).append(new Date()).append(CRLF); 82 | headerAppender.append("Content-Type:").append(BLANK).append(contentType).append(CRLF); 83 | if (headers != null) { 84 | for (Header header : headers) { 85 | headerAppender.append(header.getKey()).append(":").append(BLANK).append(header.getValue()).append(CRLF); 86 | } 87 | } 88 | if (cookies.size() > 0) { 89 | for (Cookie cookie : cookies) { 90 | headerAppender.append("Set-Cookie:").append(BLANK).append(cookie.getKey()).append("=").append(cookie.getValue()).append(CRLF); 91 | } 92 | } 93 | headerAppender.append("Content-Length:").append(BLANK); 94 | } 95 | 96 | //一次性传入响应体 97 | private void buildBody() { 98 | this.headerAppender.append(body.length).append(CRLF).append(CRLF); 99 | } 100 | 101 | /** 102 | * response构建的最后一步,将header和body转为字节数组 103 | */ 104 | private void buildResponse() { 105 | buildHeader(); 106 | buildBody(); 107 | } 108 | 109 | /** 110 | * 返回Response构建后的数据,用于NIO/AIO 111 | * @return 112 | */ 113 | public ByteBuffer[] getResponseByteBuffer() { 114 | buildResponse(); 115 | byte[] header = this.headerAppender.toString().getBytes(UTF_8_CHARSET); 116 | ByteBuffer[] response = {ByteBuffer.wrap(header), ByteBuffer.wrap(body)}; 117 | return response; 118 | } 119 | 120 | /** 121 | * 返回Response构建后的数据,用于BIO 122 | * @return 123 | */ 124 | public byte[] getResponseBytes() { 125 | buildResponse(); 126 | byte[] header = this.headerAppender.toString().getBytes(UTF_8_CHARSET); 127 | byte[] response = new byte[header.length + body.length]; 128 | System.arraycopy(header, 0, response, 0, header.length); 129 | System.arraycopy(body, 0, response, header.length, body.length); 130 | return response; 131 | } 132 | 133 | /** 134 | * 重定向,注意重定向后会立即写数据至socket中 135 | * @param url 136 | */ 137 | public void sendRedirect(String url) { 138 | log.info("重定向至{}", url); 139 | addHeader(new Header("Location", url)); 140 | setStatus(HttpStatus.MOVED_TEMPORARILY); 141 | buildResponse(); 142 | // 刷新至客户端 143 | requestHandler.flushResponse(); 144 | } 145 | 146 | /** 147 | * 用于调用不同RequestHandler的写刷新(将response写入到客户端) 148 | * @param requestHandler 149 | */ 150 | public void setRequestHandler(AbstractRequestHandler requestHandler) { 151 | this.requestHandler = requestHandler; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/servlet/Servlet.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.servlet; 2 | 3 | import com.lws.lwebserver.core.exception.base.ServletException; 4 | import com.lws.lwebserver.core.request.Request; 5 | import com.lws.lwebserver.core.response.Response; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * @Author: zl 11 | * @Date: 2019/3/16 16:24 12 | */ 13 | public interface Servlet { 14 | void init(); 15 | 16 | void destroy(); 17 | 18 | void service(Request request, Response response) throws ServletException, IOException; 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/servlet/impl/DefaultServlet.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.servlet.impl; 2 | 3 | import com.lws.lwebserver.core.enumeration.RequestMethod; 4 | import com.lws.lwebserver.core.exception.base.ServletException; 5 | import com.lws.lwebserver.core.request.Request; 6 | import com.lws.lwebserver.core.response.Response; 7 | import lombok.extern.slf4j.Slf4j; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * @Author: zl 13 | * @Date: 2019/3/16 16:25 14 | */@Slf4j 15 | public class DefaultServlet extends HttpServlet { 16 | 17 | @Override 18 | public void service(Request request, Response response) throws ServletException, IOException { 19 | if (request.getMethod() == RequestMethod.GET) { 20 | //首页 21 | if (request.getUrl().equals("/")) { 22 | request.setUrl("/index.html"); 23 | } 24 | request.getRequestDispatcher(request.getUrl()).forward(request, response); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/servlet/impl/HttpServlet.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.servlet.impl; 2 | 3 | import com.lws.lwebserver.core.enumeration.RequestMethod; 4 | import com.lws.lwebserver.core.exception.base.ServletException; 5 | import com.lws.lwebserver.core.request.Request; 6 | import com.lws.lwebserver.core.response.Response; 7 | import com.lws.lwebserver.core.servlet.Servlet; 8 | import lombok.extern.slf4j.Slf4j; 9 | 10 | import java.io.IOException; 11 | 12 | /** 13 | * @Author: zl 14 | * @Date: 2019/3/16 16:26 15 | */ 16 | @Slf4j 17 | public abstract class HttpServlet implements Servlet { 18 | 19 | @Override 20 | public void init() { 21 | 22 | } 23 | 24 | @Override 25 | public void destroy() { 26 | 27 | } 28 | 29 | public void service(Request request, Response response) throws ServletException, IOException { 30 | if (request.getMethod() == RequestMethod.GET) { 31 | doGet(request, response); 32 | } else if (request.getMethod() == RequestMethod.POST) { 33 | doPost(request, response); 34 | } else if (request.getMethod() == RequestMethod.PUT) { 35 | doPut(request, response); 36 | } else if (request.getMethod() == RequestMethod.DELETE) { 37 | doDelete(request, response); 38 | } 39 | } 40 | 41 | public void doGet(Request request, Response response) throws ServletException, IOException { 42 | } 43 | 44 | public void doPost(Request request, Response response) throws ServletException, IOException { 45 | } 46 | 47 | public void doPut(Request request, Response response) throws ServletException, IOException { 48 | } 49 | 50 | public void doDelete(Request request, Response response) throws ServletException, IOException { 51 | } 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/session/HttpSession.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.session; 2 | 3 | import com.lws.lwebserver.core.context.WebApplication; 4 | 5 | import java.time.Instant; 6 | import java.util.Map; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | 9 | /** 10 | * @Author: zl 11 | * @Date: 2019/3/16 16:27 12 | */ 13 | public class HttpSession { 14 | private String id; 15 | private Map attributes; 16 | private boolean isValid; 17 | /** 18 | * 用于判断sessiion是否过期,标准为当前时间-上次访问时间 >= 阈值 19 | */ 20 | private Instant lastAccessed; 21 | 22 | 23 | public HttpSession(String id) { 24 | this.id = id; 25 | this.attributes = new ConcurrentHashMap<>(); 26 | this.isValid = true; 27 | this.lastAccessed = Instant.now(); 28 | } 29 | 30 | /** 31 | * 使当前session失效,之后就无法读写当前session了 32 | * 并会清除session数据,并且在servletContext中删除此session 33 | */ 34 | public void invalidate() { 35 | this.isValid = false; 36 | this.attributes.clear(); 37 | WebApplication.getServletContext().invalidateSession(this); 38 | } 39 | 40 | public Object getAttribute(String key) { 41 | if (isValid) { 42 | this.lastAccessed = Instant.now(); 43 | return attributes.get(key); 44 | } 45 | throw new IllegalStateException("session has invalidated"); 46 | } 47 | 48 | public void setAttribute(String key, Object value) { 49 | if (isValid) { 50 | this.lastAccessed = Instant.now(); 51 | attributes.put(key, value); 52 | } else { 53 | throw new IllegalStateException("session has invalidated"); 54 | } 55 | } 56 | 57 | public String getId() { 58 | return id; 59 | } 60 | 61 | public Instant getLastAccessed() { 62 | return lastAccessed; 63 | } 64 | 65 | 66 | public void removeAttribute(String key) { 67 | attributes.remove(key); 68 | } 69 | 70 | 71 | } 72 | 73 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/session/IdleSessionCleaner.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.session; 2 | 3 | import com.lws.lwebserver.core.context.WebApplication; 4 | import lombok.extern.slf4j.Slf4j; 5 | 6 | import java.util.concurrent.Executors; 7 | import java.util.concurrent.ScheduledExecutorService; 8 | import java.util.concurrent.ThreadFactory; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | /** 12 | * @Author: zl 13 | * @Date: 2019/3/16 16:28 14 | */ 15 | @Slf4j 16 | public class IdleSessionCleaner implements Runnable { 17 | 18 | private ScheduledExecutorService executor; 19 | 20 | public IdleSessionCleaner() { 21 | ThreadFactory threadFactory = new ThreadFactory() { 22 | @Override 23 | public Thread newThread(Runnable r) { 24 | return new Thread(r, "IdleSessionCleaner"); 25 | } 26 | }; 27 | this.executor = Executors.newSingleThreadScheduledExecutor(threadFactory); 28 | } 29 | 30 | public void start() { 31 | executor.scheduleAtFixedRate(this, 5, 5, TimeUnit.SECONDS); 32 | } 33 | 34 | @Override 35 | public void run() { 36 | log.info("开始扫描过期session..."); 37 | WebApplication.getServletContext().cleanIdleSessions(); 38 | log.info("扫描结束..."); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/template/TemplateResolver.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.template; 2 | 3 | import com.lws.lwebserver.core.enumeration.ModelScope; 4 | import com.lws.lwebserver.core.exception.TemplateResolveException; 5 | import com.lws.lwebserver.core.request.Request; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.util.StringUtils; 8 | 9 | import java.lang.reflect.InvocationTargetException; 10 | import java.lang.reflect.Method; 11 | import java.util.Arrays; 12 | import java.util.regex.Matcher; 13 | import java.util.regex.Pattern; 14 | 15 | /** 16 | * Created by zl on 2019/03/01. 17 | */ 18 | @Slf4j 19 | public class TemplateResolver { 20 | public static final Pattern regex = Pattern.compile("\\$\\{(.*?)}"); 21 | 22 | public static String resolve(String content, Request request) throws TemplateResolveException { 23 | Matcher matcher = regex.matcher(content); 24 | StringBuffer sb = new StringBuffer(); 25 | while (matcher.find()) { 26 | log.info("{}", matcher.group(1)); 27 | // placeHolder 格式为scope.x.y.z 28 | // scope值为requestScope,sessionScope,applicationScope 29 | String placeHolder = matcher.group(1); 30 | if (placeHolder.indexOf('.') == -1) { 31 | throw new TemplateResolveException(); 32 | } 33 | ModelScope scope = ModelScope 34 | .valueOf( 35 | placeHolder.substring(0, placeHolder.indexOf('.')) 36 | .replace("Scope", "") 37 | .toUpperCase()); 38 | // key 格式为x.y.z 39 | String key = placeHolder.substring(placeHolder.indexOf('.') + 1); 40 | if (scope == null) { 41 | throw new TemplateResolveException(); 42 | } 43 | Object value = null; 44 | // 按照.分隔为数组,格式为[x,y,z] 45 | String[] segments = key.split("\\."); 46 | log.info("key: {} , segments:{}", key, Arrays.toString(segments)); 47 | switch (scope) { 48 | case REQUEST: 49 | value = request.getAttribute(segments[0]); 50 | break; 51 | case SESSION: 52 | value = request.getSession().getAttribute(segments[0]); 53 | break; 54 | case APPLICATION: 55 | value = request.getServletContext().getAttribute(segments[0]); 56 | break; 57 | default: 58 | break; 59 | } 60 | // 此时value为x,如果没有y、z,那么会直接返回;如果有,就会递归地进行属性读取(基于反射) 61 | if (segments.length > 1) { 62 | try { 63 | value = parse(value, segments, 1); 64 | } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { 65 | e.printStackTrace(); 66 | throw new TemplateResolveException(); 67 | } 68 | } 69 | log.info("value:{}", value); 70 | // 如果解析得到的值为null,则将占位符去掉;否则将占位符替换为值 71 | if (value == null) { 72 | matcher.appendReplacement(sb, ""); 73 | } else { 74 | //把group(1)得到的数据,替换为value 75 | matcher.appendReplacement(sb, value.toString()); 76 | } 77 | } 78 | // 将源文件后续部分添加至尾部 79 | matcher.appendTail(sb); 80 | String result = sb.toString(); 81 | return result.length() == 0 ? content : result; 82 | } 83 | 84 | /** 85 | * 基于反射实现多级查询,比如user.dept.name 86 | * 87 | * @param segments 88 | * @return 89 | */ 90 | private static Object parse(Object value, String[] segments, int index) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { 91 | if (index == segments.length) { 92 | return value; 93 | } 94 | Method method = value.getClass().getMethod("get" + StringUtils.capitalize(segments[index]), new Class[0]); 95 | return parse(method.invoke(value, new Object[0]), segments, index + 1); 96 | } 97 | } -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/util/IOUtil.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.util; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | import java.io.*; 6 | import java.net.URL; 7 | import java.nio.ByteBuffer; 8 | import java.nio.channels.FileChannel; 9 | 10 | /** 11 | * Created by zl on 2019/03/01. 12 | */ 13 | @Slf4j 14 | public class IOUtil { 15 | 16 | public static byte[] getBytesFromFile(String fileName) throws IOException { 17 | InputStream in = IOUtil.class.getResourceAsStream(fileName); 18 | if (in == null) { 19 | log.info("Not Found File:{}",fileName); 20 | throw new FileNotFoundException(); 21 | } 22 | log.info("正在读取文件:{}",fileName); 23 | return getBytesFromStream(in); 24 | } 25 | 26 | public static byte[] getBytesFromStream(InputStream in) throws IOException { 27 | ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 28 | byte[] buffer = new byte[1024]; 29 | int len = -1; 30 | while((len = in.read(buffer)) != -1){ 31 | outStream.write(buffer, 0, len); 32 | } 33 | outStream.close(); 34 | in.close(); 35 | return outStream.toByteArray(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/util/MimeTypeUtil.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.util; 2 | 3 | import eu.medsea.mimeutil.MimeUtil; 4 | import lombok.extern.slf4j.Slf4j; 5 | 6 | import java.util.Collection; 7 | 8 | import static com.lws.lwebserver.core.constant.Const.DEFAULT_CONTENT_TYPE; 9 | 10 | /** 11 | * Created by zl on 2019/03/01. 12 | */ 13 | @Slf4j 14 | public class MimeTypeUtil { 15 | static { 16 | MimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector"); 17 | } 18 | 19 | public static String getTypes(String fileName) { 20 | if(fileName.endsWith(".html")){ 21 | return DEFAULT_CONTENT_TYPE; 22 | } 23 | Collection mimeTypes = MimeUtil.getMimeTypes(MimeTypeUtil.class.getResource(fileName)); 24 | return mimeTypes.toArray()[0].toString(); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/util/PropertyUtil.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.util; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | import java.io.FileNotFoundException; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.util.Properties; 9 | 10 | /** 11 | * @author zl 12 | */ 13 | @Slf4j 14 | public class PropertyUtil { 15 | private static Properties props; 16 | 17 | static { 18 | loadProps(); 19 | } 20 | 21 | private synchronized static void loadProps() { 22 | log.info("开始加载properties文件内容......."); 23 | props = new Properties(); 24 | InputStream in = null; 25 | try { 26 | //加载配置 27 | in = PropertyUtil.class.getClassLoader().getResourceAsStream("server.properties"); 28 | props.load(in); 29 | } catch (FileNotFoundException e) { 30 | log.error(".properties文件未找到"); 31 | } catch (IOException e) { 32 | log.error("出现IOException"); 33 | } finally { 34 | try { 35 | if (null != in) { 36 | in.close(); 37 | } 38 | } catch (IOException e) { 39 | log.error(".properties文件流关闭出现异常"); 40 | } 41 | } 42 | log.info(".properties文件内容:" + props); 43 | } 44 | 45 | public static String getProperty(String key) { 46 | if (null == props) { 47 | loadProps(); 48 | } 49 | return props.getProperty(key); 50 | } 51 | 52 | public static String getProperty(String key, String defaultValue) { 53 | if (null == props) { 54 | loadProps(); 55 | } 56 | return props.getProperty(key, defaultValue); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/util/SynchronizedQueue.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.util; 2 | 3 | /** 4 | * @author zl 5 | * 对队列的封装 6 | */ 7 | public class SynchronizedQueue { 8 | public static final int DEFAULT_SIZE = 128; 9 | 10 | private Object[] queue; 11 | private int size; 12 | private int insert = 0; 13 | private int remove = 0; 14 | 15 | public SynchronizedQueue() { 16 | this(DEFAULT_SIZE); 17 | } 18 | 19 | public SynchronizedQueue(int initialSize) { 20 | queue = new Object[initialSize]; 21 | size = initialSize; 22 | } 23 | 24 | public synchronized boolean offer(T t) { 25 | queue[insert++] = t; 26 | 27 | // Wrap 28 | if (insert == size) { 29 | insert = 0; 30 | } 31 | 32 | if (insert == remove) { 33 | expand(); 34 | } 35 | return true; 36 | } 37 | 38 | public synchronized T poll() { 39 | if (insert == remove) { 40 | // empty 41 | return null; 42 | } 43 | 44 | @SuppressWarnings("unchecked") 45 | T result = (T) queue[remove]; 46 | queue[remove] = null; 47 | remove++; 48 | 49 | // Wrap 50 | if (remove == size) { 51 | remove = 0; 52 | } 53 | 54 | return result; 55 | } 56 | 57 | private void expand() { 58 | int newSize = size * 2; 59 | Object[] newQueue = new Object[newSize]; 60 | 61 | System.arraycopy(queue, insert, newQueue, 0, size - insert); 62 | System.arraycopy(queue, 0, newQueue, size - insert, insert); 63 | 64 | insert = size; 65 | remove = 0; 66 | queue = newQueue; 67 | size = newSize; 68 | } 69 | 70 | public synchronized int size() { 71 | int result = insert - remove; 72 | if (result < 0) { 73 | result += size; 74 | } 75 | return result; 76 | } 77 | 78 | public synchronized void clear() { 79 | queue = new Object[size]; 80 | insert = 0; 81 | remove = 0; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/util/UUIDUtil.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.util; 2 | 3 | import java.util.UUID; 4 | 5 | /** 6 | * Created by zl on 2019/03/01. 7 | */ 8 | public class UUIDUtil { 9 | public static String uuid(){ 10 | return UUID.randomUUID().toString().replace("-","").toUpperCase(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/java/com/lws/lwebserver/core/util/XMLUtil.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.core.util; 2 | 3 | import org.dom4j.Document; 4 | import org.dom4j.DocumentException; 5 | import org.dom4j.io.SAXReader; 6 | 7 | import java.io.InputStream; 8 | 9 | /** 10 | * Created by zl on 2019/03/01. 11 | */ 12 | public class XMLUtil { 13 | 14 | public static Document getDocument(InputStream in) { 15 | try { 16 | SAXReader reader = new SAXReader(); 17 | return reader.read(in); 18 | } catch (DocumentException e) { 19 | e.printStackTrace(); 20 | } 21 | return null; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defineYIDA/LWebServer/43df5270eb4126f646e355ada579339939d778c8/core/src/main/resources/log4j.properties -------------------------------------------------------------------------------- /core/src/main/webapp/errors/400.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 400 BAD_REQUEST 6 | 7 | 8 |

400 BAD_REQUEST

9 | 10 | -------------------------------------------------------------------------------- /core/src/main/webapp/errors/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 404 NOT_FOUND 6 | 7 | 8 |

404 NOT_FOUND

9 | 10 | -------------------------------------------------------------------------------- /core/src/main/webapp/errors/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 500 INTERNAL_SERVER_ERROR 6 | 7 | 8 |

500 INTERNAL_SERVER_ERROR

9 | 10 | -------------------------------------------------------------------------------- /example/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | LWebServer 7 | com.lws 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | example 13 | 14 | UTF-8 15 | 1.7.25 16 | 1.2.17 17 | 5.0.5.RELEASE 18 | 19 | 20 | 21 | 22 | com.lws 23 | core 24 | 1.0-SNAPSHOT 25 | 26 | 27 | junit 28 | junit 29 | 4.12 30 | test 31 | 32 | 33 | 34 | org.projectlombok 35 | lombok 36 | 1.16.18 37 | provided 38 | 39 | 40 | org.slf4j 41 | slf4j-log4j12 42 | ${slf4j.version} 43 | 44 | 45 | 46 | log4j 47 | log4j 48 | ${log4j.version} 49 | 50 | 51 | 52 | org.springframework 53 | spring-core 54 | [5.0.7,) 55 | 56 | 57 | 58 | 59 | LWebServer 60 | 61 | 62 | src/main/webapp 63 | 64 | WEB-INF/ 65 | 66 | false 67 | 68 | 69 | src/main/webapp/WEB-INF 70 | 71 | **/*.* 72 | 73 | false 74 | 75 | 76 | 77 | 78 | maven-assembly-plugin 79 | 80 | 81 | jar-with-dependencies 82 | 83 | 84 | 85 | com.lws.lwebserver.example.Main 86 | 87 | 88 | 89 | 90 | 91 | make-assembly 92 | package 93 | 94 | single 95 | 96 | 97 | 98 | 99 | 100 | org.apache.maven.plugins 101 | maven-compiler-plugin 102 | 103 | 1.8 104 | 1.8 105 | UTF-8 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /example/src/main/java/com/lws/lwebserver/example/Main.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.example; 2 | 3 | import com.lws.lwebserver.core.BootStrap; 4 | 5 | /** 6 | * @author zl 7 | */ 8 | public class Main { 9 | 10 | public static void main(String[] var){ 11 | BootStrap.run(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/src/main/java/com/lws/lwebserver/example/model/User.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.example.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @Author: zl 9 | * @Date: 2019/3/16 16:52 10 | */ 11 | @Data 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | public class User { 15 | private String username; 16 | private String password; 17 | private String realName; 18 | private Integer age; 19 | } 20 | -------------------------------------------------------------------------------- /example/src/main/java/com/lws/lwebserver/example/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.example.service; 2 | 3 | import com.lws.lwebserver.example.model.User; 4 | import lombok.extern.slf4j.Slf4j; 5 | 6 | import java.util.Map; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | 9 | /** 10 | * @Author: zl 11 | * @Date: 2019/3/16 16:53 12 | */ 13 | @Slf4j 14 | public class UserService { 15 | private static UserService instance = new UserService(); 16 | 17 | public static UserService getInstance() { 18 | return instance; 19 | } 20 | 21 | private Map users = new ConcurrentHashMap<>(); 22 | private Map online = new ConcurrentHashMap<>(); 23 | 24 | 25 | public UserService() { 26 | users.put("admin", new User("admin", "admin", "管理员", 20)); 27 | users.put("user", new User("user", "123456", "用户", 23)); 28 | } 29 | 30 | public boolean login(String username, String password) { 31 | User user = users.get(username); 32 | if (user == null) { 33 | return false; 34 | } 35 | if (password.equals(user.getPassword())) { 36 | online.put(username, ""); 37 | return true; 38 | } 39 | return false; 40 | } 41 | 42 | public User findByUsername(String username) { 43 | return users.get(username); 44 | } 45 | 46 | 47 | public void update(User user) { 48 | users.put(user.getUsername(),user); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /example/src/main/java/com/lws/lwebserver/example/web/filter/LogFilter.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.example.web.filter; 2 | 3 | 4 | import com.lws.lwebserver.core.fliter.Filter; 5 | import com.lws.lwebserver.core.fliter.FilterChain; 6 | import com.lws.lwebserver.core.request.Request; 7 | import com.lws.lwebserver.core.response.Response; 8 | import lombok.extern.slf4j.Slf4j; 9 | 10 | /** 11 | * @author zl 12 | */ 13 | @Slf4j 14 | public class LogFilter implements Filter { 15 | @Override 16 | public void init() { 17 | log.info("LogFilter init..."); 18 | } 19 | 20 | @Override 21 | public void doFilter(Request request, Response response, FilterChain filterChain) { 22 | log.info("{} before accessed, method is {}", request.getUrl(), request.getMethod()); 23 | filterChain.doFilter(request, response); 24 | log.info("{} after accessed, method is {}", request.getUrl(), request.getMethod()); 25 | } 26 | 27 | @Override 28 | public void destroy() { 29 | log.info("LogFilter destroy..."); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /example/src/main/java/com/lws/lwebserver/example/web/filter/LoginFilter.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.example.web.filter; 2 | 3 | 4 | 5 | import com.lws.lwebserver.core.fliter.Filter; 6 | import com.lws.lwebserver.core.fliter.FilterChain; 7 | import com.lws.lwebserver.core.request.Request; 8 | import com.lws.lwebserver.core.response.Response; 9 | import lombok.extern.slf4j.Slf4j; 10 | 11 | /** 12 | * @author zl 13 | */ 14 | @Slf4j 15 | public class LoginFilter implements Filter { 16 | 17 | @Override 18 | public void init() { 19 | log.info("LoginFilter init..."); 20 | } 21 | 22 | @Override 23 | public void doFilter(Request request, Response response, FilterChain filterChain) { 24 | log.info("当前访问的servletPath:{}", request.getServletPath()); 25 | // login直接放行,其他页面访问均需要登录 26 | if (request.getServletPath().equals("/login") || request.getServletPath().startsWith("/views/errors")) { 27 | log.info("直接放行"); 28 | filterChain.doFilter(request, response); 29 | } else { 30 | log.info("检查是否登录..."); 31 | if (request.getSession(false) != null && request.getSession().getAttribute("username") != null) { 32 | log.info("已登录,通过检查..."); 33 | filterChain.doFilter(request, response); 34 | } else { 35 | log.info("未登录,401"); 36 | // 未登录。重定向至登录页面 37 | response.sendRedirect("/login"); 38 | } 39 | } 40 | } 41 | 42 | @Override 43 | public void destroy() { 44 | log.info("LoginFilter destroy..."); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /example/src/main/java/com/lws/lwebserver/example/web/listener/MyServletRequestListener.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.example.web.listener; 2 | 3 | import com.lws.lwebserver.core.listener.ServletRequestListener; 4 | import com.lws.lwebserver.core.listener.event.ServletRequestEvent; 5 | import lombok.extern.slf4j.Slf4j; 6 | 7 | /** 8 | * @author zl 9 | */ 10 | @Slf4j 11 | public class MyServletRequestListener implements ServletRequestListener { 12 | 13 | @Override 14 | public void requestDestroyed(ServletRequestEvent sre) { 15 | log.info("request destroy..."); 16 | } 17 | 18 | @Override 19 | public void requestInitialized(ServletRequestEvent sre) { 20 | log.info("request init..."); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /example/src/main/java/com/lws/lwebserver/example/web/listener/ServletContextAndSessionListener.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.example.web.listener; 2 | 3 | import com.lws.lwebserver.core.listener.HttpSessionListener; 4 | import com.lws.lwebserver.core.listener.ServletContextListener; 5 | import com.lws.lwebserver.core.listener.event.HttpSessionEvent; 6 | import com.lws.lwebserver.core.listener.event.ServletContextEvent; 7 | import lombok.extern.slf4j.Slf4j; 8 | 9 | import java.util.concurrent.atomic.AtomicInteger; 10 | 11 | 12 | /** 13 | * @author zl 14 | */ 15 | @Slf4j 16 | public class ServletContextAndSessionListener implements ServletContextListener, HttpSessionListener { 17 | private AtomicInteger sessionCount = new AtomicInteger(); 18 | 19 | @Override 20 | public void contextInitialized(ServletContextEvent sce) { 21 | log.info("servlet context init..."); 22 | } 23 | 24 | @Override 25 | public void contextDestroyed(ServletContextEvent sce) { 26 | log.info("servlet context destroy..."); 27 | } 28 | 29 | @Override 30 | public void sessionCreated(HttpSessionEvent se) { 31 | log.info("session created, count = {}", this.sessionCount.incrementAndGet()); 32 | } 33 | 34 | @Override 35 | public void sessionDestroyed(HttpSessionEvent se) { 36 | log.info("session destroyed, count = {}", sessionCount.decrementAndGet()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /example/src/main/java/com/lws/lwebserver/example/web/servlet/LoginServlet.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.example.web.servlet; 2 | 3 | import com.lws.lwebserver.core.exception.base.ServletException; 4 | import com.lws.lwebserver.core.request.Request; 5 | import com.lws.lwebserver.core.response.Response; 6 | import com.lws.lwebserver.core.servlet.impl.HttpServlet; 7 | 8 | import com.lws.lwebserver.example.service.UserService; 9 | import lombok.extern.slf4j.Slf4j; 10 | 11 | import java.io.IOException; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | /** 16 | * Created by zl 17 | */ 18 | @Slf4j 19 | public class LoginServlet extends HttpServlet { 20 | 21 | private UserService userService; 22 | 23 | public LoginServlet() { 24 | userService = UserService.getInstance(); 25 | } 26 | 27 | @Override 28 | public void init() { 29 | log.info("LoginServlet init..."); 30 | } 31 | 32 | @Override 33 | public void destroy() { 34 | log.info("LoginServlet destroy..."); 35 | } 36 | 37 | @Override 38 | public void doGet(Request request, Response response) throws ServletException, IOException { 39 | String username = (String) request.getSession().getAttribute("username"); 40 | if (username != null) { 41 | log.info("已经登录,跳转至success页面"); 42 | response.sendRedirect("/views/success.html"); 43 | } else { 44 | request.getRequestDispatcher("/views/login.html").forward(request,response); 45 | } 46 | } 47 | 48 | @Override 49 | public void doPost(Request request, Response response) throws ServletException, IOException { 50 | Map> params = request.getParams(); 51 | String username = params.get("username").get(0); 52 | String password = params.get("password").get(0); 53 | if (userService.login(username, password)) { 54 | log.info("{} 登录成功", username); 55 | request.getSession().setAttribute("username", username); 56 | response.sendRedirect("/views/success.html"); 57 | } else { 58 | log.info("登录失败"); 59 | response.sendRedirect("/views/errors/400.html"); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /example/src/main/java/com/lws/lwebserver/example/web/servlet/LogoutServlet.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.example.web.servlet; 2 | 3 | import com.lws.lwebserver.core.exception.base.ServletException; 4 | import com.lws.lwebserver.core.request.Request; 5 | import com.lws.lwebserver.core.response.Response; 6 | import com.lws.lwebserver.core.servlet.impl.HttpServlet; 7 | import lombok.extern.slf4j.Slf4j; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * Created by zl 13 | */ 14 | @Slf4j 15 | public class LogoutServlet extends HttpServlet { 16 | 17 | @Override 18 | public void doGet(Request request, Response response) throws ServletException, IOException { 19 | request.getRequestDispatcher("/views/logout.html").forward(request,response); 20 | } 21 | 22 | @Override 23 | public void doPost(Request request, Response response) throws ServletException, IOException { 24 | request.getSession().removeAttribute("username"); 25 | request.getSession().invalidate(); 26 | response.sendRedirect("/login"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /example/src/main/java/com/lws/lwebserver/example/web/servlet/UserEditServlet.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.example.web.servlet; 2 | 3 | import com.lws.lwebserver.core.exception.base.ServletException; 4 | import com.lws.lwebserver.core.request.Request; 5 | import com.lws.lwebserver.core.response.Response; 6 | import com.lws.lwebserver.core.servlet.impl.HttpServlet; 7 | 8 | import com.lws.lwebserver.example.model.User; 9 | import com.lws.lwebserver.example.service.UserService; 10 | import lombok.extern.slf4j.Slf4j; 11 | 12 | import java.io.IOException; 13 | 14 | /** 15 | * @author zl 16 | */ 17 | @Slf4j 18 | public class UserEditServlet extends HttpServlet { 19 | private UserService userService; 20 | 21 | public UserEditServlet() { 22 | userService = UserService.getInstance(); 23 | } 24 | 25 | 26 | @Override 27 | public void doGet(Request request, Response response) throws ServletException, IOException { 28 | User user = userService.findByUsername((String) request.getSession().getAttribute("username")); 29 | request.setAttribute("user",user); 30 | request.getRequestDispatcher("/views/userEdit.html").forward(request,response); 31 | } 32 | 33 | @Override 34 | public void doPost(Request request, Response response) throws ServletException, IOException { 35 | log.info("{}",request.getParams()); 36 | User user = new User(); 37 | user.setUsername((String) request.getSession(false).getAttribute("username")); 38 | user.setRealName(request.getParameter("realName")); 39 | user.setAge(Integer.valueOf(request.getParameter("age"))); 40 | userService.update(user); 41 | 42 | request.setAttribute("user",user); 43 | request.getRequestDispatcher("/views/user.html").forward(request, response); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /example/src/main/java/com/lws/lwebserver/example/web/servlet/UserServlet.java: -------------------------------------------------------------------------------- 1 | package com.lws.lwebserver.example.web.servlet; 2 | 3 | import com.lws.lwebserver.core.exception.base.ServletException; 4 | import com.lws.lwebserver.core.request.Request; 5 | import com.lws.lwebserver.core.response.Response; 6 | import com.lws.lwebserver.core.servlet.impl.HttpServlet; 7 | 8 | import com.lws.lwebserver.example.model.User; 9 | import com.lws.lwebserver.example.service.UserService; 10 | import lombok.extern.slf4j.Slf4j; 11 | 12 | import java.io.IOException; 13 | 14 | /** 15 | * Created by zl 16 | */ 17 | @Slf4j 18 | public class UserServlet extends HttpServlet { 19 | private UserService userService; 20 | 21 | public UserServlet() { 22 | userService = UserService.getInstance(); 23 | } 24 | 25 | @Override 26 | public void doGet(Request request, Response response) throws ServletException, IOException { 27 | User user = userService.findByUsername((String) request.getSession().getAttribute("username")); 28 | request.setAttribute("user",user); 29 | request.getRequestDispatcher("/views/user.html").forward(request, response); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /example/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defineYIDA/LWebServer/43df5270eb4126f646e355ada579339939d778c8/example/src/main/resources/log4j.properties -------------------------------------------------------------------------------- /example/src/main/resources/server.properties: -------------------------------------------------------------------------------- 1 | server.port=8080 2 | server.connector=nio -------------------------------------------------------------------------------- /example/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | LoginServlet 6 | com.lws.lwebserver.example.web.servlet.LoginServlet 7 | 8 | 9 | LogoutServlet 10 | com.lws.lwebserver.example.web.servlet.LogoutServlet 11 | 12 | 13 | UserServlet 14 | com.lws.lwebserver.example.web.servlet.UserServlet 15 | 16 | 17 | UserEditServlet 18 | com.lws.lwebserver.example.web.servlet.UserEditServlet 19 | 20 | 21 | DefaultServlet 22 | com.lws.lwebserver.core.servlet.impl.DefaultServlet 23 | 24 | 25 | 26 | LoginServlet 27 | /login 28 | 29 | 30 | 31 | LogoutServlet 32 | /logout 33 | 34 | 35 | 36 | UserServlet 37 | /user 38 | 39 | 40 | 41 | UserEditServlet 42 | /user/edit 43 | 44 | 45 | 46 | DefaultServlet 47 | / 48 | 49 | 50 | 51 | LoginFilter 52 | com.lws.lwebserver.example.web.filter.LoginFilter 53 | 54 | 55 | 56 | LogFilter 57 | com.lws.lwebserver.example.web.filter.LogFilter 58 | 59 | 60 | 61 | LoginFilter 62 | /** 63 | 64 | 65 | 66 | LogFilter 67 | /** 68 | 69 | 70 | 71 | com.lws.lwebserver.example.web.listener.ServletContextAndSessionListener 72 | com.lws.lwebserver.example.web.listener.MyServletRequestListener 73 | 74 | 75 | -------------------------------------------------------------------------------- /example/src/main/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Index 6 | 7 | 8 | 查看用户详细信息
9 | Click me! 10 | 11 | -------------------------------------------------------------------------------- /example/src/main/webapp/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defineYIDA/LWebServer/43df5270eb4126f646e355ada579339939d778c8/example/src/main/webapp/static/favicon.ico -------------------------------------------------------------------------------- /example/src/main/webapp/views/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Login 6 | 7 | 8 |
9 | Username: 10 | Password: 11 | 12 |
13 | 14 | -------------------------------------------------------------------------------- /example/src/main/webapp/views/logout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Logout 6 | 7 | 8 | Logout 9 | 10 | -------------------------------------------------------------------------------- /example/src/main/webapp/views/success.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Success 6 | 7 | 8 | Welcome! ${sessionScope.username} 9 | 10 | -------------------------------------------------------------------------------- /example/src/main/webapp/views/user.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UserInfo 6 | 7 | 8 | 您的用户名是:${sessionScope.username} 9 | 10 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.lws 6 | LWebServer 7 | 1.0-SNAPSHOT 8 | 9 | example 10 | core 11 | 12 | pom 13 | 14 | LWebServer 15 | 16 | 17 | --------------------------------------------------------------------------------