├── .coveralls.yml ├── .gitignore ├── .travis.yml ├── CHANGE_LOG.md ├── LICENSE.txt ├── README.md ├── cgit.bat ├── pom.xml ├── release.bat ├── release.sh ├── release_rm.sh └── src ├── main ├── java │ └── com │ │ └── github │ │ └── houbb │ │ └── minicat │ │ ├── MiniCatBootstrapMain.java │ │ ├── bo │ │ └── RequestInfoBo.java │ │ ├── bs │ │ ├── MiniCatBootstrap.java │ │ ├── MiniCatBootstrapBio.java │ │ ├── MiniCatServerHandler.java │ │ └── servlet │ │ │ ├── MiniCatBootstrapBioSocket.java │ │ │ ├── MiniCatBootstrapBioThreadSocket.java │ │ │ ├── MiniCatBootstrapNetty.java │ │ │ ├── MiniCatBootstrapNioSocket.java │ │ │ ├── MiniCatBootstrapNioThreadSocket.java │ │ │ └── MiniCatNettyServerHandler.java │ │ ├── constant │ │ └── HttpMethodType.java │ │ ├── dto │ │ ├── IMiniCatRequest.java │ │ ├── IMiniCatResponse.java │ │ ├── MiniCatRequestAdaptor.java │ │ ├── MiniCatRequestBio.java │ │ ├── MiniCatRequestCommon.java │ │ ├── MiniCatResponseAdaptor.java │ │ ├── MiniCatResponseBio.java │ │ └── MiniCatResponseCommon.java │ │ ├── exception │ │ └── MiniCatException.java │ │ ├── package-info.java │ │ ├── support │ │ ├── attr │ │ │ ├── DefaultMiniCatAttrManager.java │ │ │ └── IMiniCatAttrManager.java │ │ ├── classloader │ │ │ └── WebAppClassLoader.java │ │ ├── context │ │ │ ├── DefaultMiniCatServletContext.java │ │ │ ├── IMiniCatContextInit.java │ │ │ ├── LocalMiniCatContextInit.java │ │ │ ├── MiniCatContextConfig.java │ │ │ └── WarsMiniCatContextInit.java │ │ ├── filter │ │ │ ├── MyMiniCatLoggingHttpFilter.java │ │ │ └── manager │ │ │ │ ├── DefaultFilterManager.java │ │ │ │ └── IFilterManager.java │ │ ├── listener │ │ │ ├── DefaultListenerManager.java │ │ │ ├── IListenerManager.java │ │ │ └── foo │ │ │ │ ├── MyServletContextAttrListener.java │ │ │ │ ├── MyServletContextListener.java │ │ │ │ ├── MyServletReadListener.java │ │ │ │ ├── MyServletRequestAttrListener.java │ │ │ │ ├── MyServletRequestListener.java │ │ │ │ └── MyServletWriteListener.java │ │ ├── package-info.java │ │ ├── request │ │ │ ├── EmptyRequestDispatcher.java │ │ │ ├── IRequestDispatcher.java │ │ │ ├── RequestDispatcherManager.java │ │ │ ├── ServletRequestDispatcher.java │ │ │ └── StaticHtmlRequestDispatcher.java │ │ ├── servlet │ │ │ ├── AbstractMiniCatHttpServlet.java │ │ │ ├── MyMiniCatHttpServlet.java │ │ │ └── manager │ │ │ │ ├── DefaultServletManager.java │ │ │ │ ├── IServletManager.java │ │ │ │ ├── LocalRootServletManager.java │ │ │ │ ├── LocalServletManager.java │ │ │ │ └── WarsServletManager.java │ │ ├── war │ │ │ ├── IWarExtractor.java │ │ │ └── WarExtractorDefault.java │ │ └── writer │ │ │ └── MyPrintWriter.java │ │ └── util │ │ ├── CostTimeUtil.java │ │ ├── InnerClassLoaderUtil.java │ │ ├── InnerHttpUtil.java │ │ ├── InnerRequestUtil.java │ │ ├── InnerResourceUtil.java │ │ └── InnerWarUtil.java └── resources │ ├── index.html │ └── web.xml └── test ├── java └── com │ └── github │ └── houbb │ └── minicat │ └── bs │ ├── MiniCatBootstrapMainTest.java │ ├── MiniCatBootstrapMainWithWarsTest.java │ ├── ResourceUtilTest.java │ └── socket │ ├── MiniCatBootstrapBioServletTest.java │ ├── MiniCatBootstrapBioThreadServletTest.java │ ├── MiniCatBootstrapNettyTest.java │ ├── MiniCatBootstrapNioSocketTest.java │ └── MiniCatBootstrapNioThreadSocketTest.java ├── resources └── reqest_demo.txt └── webapps ├── servlet.war └── servlet ├── META-INF ├── MANIFEST.MF └── maven │ └── com.github.houbb │ └── servlet-webxml │ ├── pom.properties │ └── pom.xml ├── WEB-INF ├── classes │ └── com │ │ └── github │ │ └── houbb │ │ └── servlet │ │ └── webxml │ │ └── IndexServlet.class └── web.xml └── index.html /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # maven ignore 2 | target/ 3 | #*.jar 4 | #*.war 5 | *.zip 6 | *.tar 7 | *.tar.gz 8 | 9 | # eclipse ignore 10 | .settings/ 11 | .project 12 | .classpath 13 | 14 | # idea ignore 15 | .idea/ 16 | *.ipr 17 | *.iml 18 | *.iws 19 | 20 | # temp ignore 21 | *.log 22 | *.cache 23 | *.diff 24 | *.patch 25 | *.tmp 26 | *.java~ 27 | *.properties~ 28 | *.xml~ 29 | 30 | # system ignore 31 | .DS_Store 32 | Thumbs.db 33 | 34 | # config 35 | jdbc_oracle.properties 36 | jdbc_mysql.properties 37 | jdbc.properties 38 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | install: mvn install -DskipTests=true -Dmaven.javadoc.skip=true 5 | script: mvn test 6 | after_success: 7 | - mvn clean cobertura:cobertura coveralls:report -------------------------------------------------------------------------------- /CHANGE_LOG.md: -------------------------------------------------------------------------------- 1 | # 变更日志 2 | 3 | | 类型 | 说明 | 4 | |:----|:----| 5 | | A | 新增 | 6 | | U | 更新 | 7 | | D | 删除 | 8 | | T | 测试 | 9 | | O | 优化 | 10 | | F | 修复BUG | 11 | 12 | # release_0.1.0 13 | 14 | | 序号 | 变更类型 | 说明 | 时间 | 备注 | 15 | |:---|:---|:-------|:------------------|:--| 16 | | 1 | A | 最简单的实现 | 2024-4-1 21:44:53 | | 17 | 18 | # release_0.2.0 19 | 20 | | 序号 | 变更类型 | 说明 | 时间 | 备注 | 21 | |:---|:---|:----------------------------|:------------------|:--| 22 | | 1 | A | request/response 的封装,静态文件访问 | 2024-4-2 21:44:53 | | 23 | 24 | # release_0.3.0 25 | 26 | | 序号 | 变更类型 | 说明 | 时间 | 备注 | 27 | |:---|:-----|:----------------------------|:------------------|:--| 28 | | 1 | A | 添加 servlet 的解析, web.xml 的解析 | 2024-4-3 21:44:53 | | 29 | | 2 | O | 处理代码优化 | 2024-4-3 21:44:53 | | 30 | | 3 | F | 修复请求有时解析失败问题 | 2024-4-3 21:44:53 | | 31 | 32 | # release_0.4.0 33 | 34 | | 序号 | 变更类型 | 说明 | 时间 | 备注 | 35 | |:---|:-----|:---------------------|:------------------|:--| 36 | | 1 | A | 启动改为异步+NIO+Netty 的方式 | 2024-4-5 17:47:03 | | 37 | 38 | # release_0.5.0 39 | 40 | | 序号 | 变更类型 | 说明 | 时间 | 备注 | 41 | |:---|:-----|:----------|:------------------|:--| 42 | | 1 | A | war 包解析处理 | 2024-4-6 10:45:50 | | 43 | 44 | 45 | # release_0.6.0 46 | 47 | | 序号 | 变更类型 | 说明 | 时间 | 备注 | 48 | |:---|:-----|:-------------------|:-------------------|:------| 49 | | 1 | A | 支持指定 web.xml 的位置策略 | 2024-4-11 21:38:54 | 统一抽象 | 50 | 51 | # release_0.7.0 52 | 53 | | 序号 | 变更类型 | 说明 | 时间 | 备注 | 54 | |:---|:-----|:---------------|:-------------------|:-----| 55 | | 1 | A | 添加 listener 策略 | 2024-4-12 22:55:27 | | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 项目简介 2 | 3 | ``` 4 | /\_/\ 5 | ( o.o ) 6 | > ^ < 7 | ``` 8 | 9 | mini-cat 是简易版本的 tomcat 实现。别称【嗅虎】(心有猛虎,轻嗅蔷薇。) 10 | 11 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.houbb/minicat/badge.svg)](http://mvnrepository.com/artifact/com.github.houbb/minicat) 12 | [![Build Status](https://www.travis-ci.org/houbb/minicat.svg?branch=master)](https://www.travis-ci.org/houbb/minicat?branch=master) 13 | [![Coverage Status](https://coveralls.io/repos/github/houbb/minicat/badge.svg?branch=master)](https://coveralls.io/github/houbb/minicat?branch=master) 14 | 15 | # 特性 16 | 17 | - 简单的启动实现/netty 支持 18 | 19 | - servlet 支持 20 | 21 | - 静态网页支持 22 | 23 | - filter/listener 支持 24 | 25 | - wars 支持 26 | 27 | # 变更日志 28 | 29 | > [变更日志](CHANGE_LOG.md) 30 | 31 | # 快速开始 32 | 33 | ## maven 依赖 34 | 35 | ```xml 36 | 37 | com.github.houbb 38 | minicat 39 | 0.7.0 40 | 41 | ``` 42 | 43 | ## 启动测试 44 | 45 | 运行测试类 `MiniCatBootstrapMain#main` 46 | 47 | ```java 48 | MiniCatBootstrap bootstrap = new MiniCatBootstrap(); 49 | bootstrap.start(); 50 | ``` 51 | 52 | 启动日志: 53 | 54 | ``` 55 | [INFO] [2024-04-03 11:09:15.178] [main] [c.g.h.m.s.s.WebXmlServletManager.register] - [MiniCat] register servlet, url=/my, servlet=com.github.houbb.minicat.support.servlet.MyMiniCatHttpServlet 56 | [INFO] [2024-04-03 11:09:15.180] [Thread-0] [c.g.h.m.b.MiniCatBootstrap.startSync] - [MiniCat] start listen on port 8080 57 | [INFO] [2024-04-03 11:09:15.180] [Thread-0] [c.g.h.m.b.MiniCatBootstrap.startSync] - [MiniCat] visit url http://127.0.0.1:8080 58 | ``` 59 | 60 | 页面访问:[http://127.0.0.1:8080](http://127.0.0.1:8080) 61 | 62 | 响应: 63 | 64 | ``` 65 | http://127.0.0.1:8080 66 | ``` 67 | 68 | ## 测试 69 | 70 | servlet: http://127.0.0.1:8080/my 71 | 72 | html: http://127.0.0.1:8080/index.html 73 | 74 | # 系列教程 75 | 76 | [从零手写实现 apache Tomcat-01-入门介绍](https://houbb.github.io/2016/11/07/web-server-tomcat-02-hand-write-overview) 77 | 78 | [从零手写实现 apache Tomcat-02-web.xml 入门详细介绍](https://houbb.github.io/2016/11/07/web-server-tomcat-02-hand-write-web-xml) 79 | 80 | [从零手写实现 tomcat-03-基本的 socket 实现](https://houbb.github.io/2016/11/07/web-server-tomcat-03-hand-write-simple-socket) 81 | 82 | [从零手写实现 tomcat-04-请求和响应的抽象](https://houbb.github.io/2016/11/07/web-server-tomcat-04-hand-write-request-and-resp) 83 | 84 | [从零手写实现 tomcat-05-servlet 处理支持](https://houbb.github.io/2016/11/07/web-server-tomcat-05-hand-write-servlet-web-xml) 85 | 86 | [从零手写实现 tomcat-06-servlet bio/thread/nio/netty 池化处理](https://houbb.github.io/2016/11/07/web-server-tomcat-06-hand-write-thread-pool) 87 | 88 | [从零手写实现 tomcat-07-war 如何解析处理三方的 war 包?](https://houbb.github.io/2016/11/07/web-server-tomcat-07-hand-write-war) 89 | 90 | [从零手写实现 tomcat-08-tomcat 如何与 springboot 集成?](https://houbb.github.io/2016/11/07/web-server-tomcat-08-hand-write-embed) 91 | 92 | [从零手写实现 tomcat-09-servlet 处理类](https://houbb.github.io/2016/11/07/web-server-tomcat-09-hand-write-servlet) 93 | 94 | [从零手写实现 tomcat-10-static resource 静态资源文件](https://houbb.github.io/2016/11/07/web-server-tomcat-10-hand-write-static-resource) 95 | 96 | [从零手写实现 tomcat-11-filter 过滤器](https://houbb.github.io/2016/11/07/web-server-tomcat-11-hand-write-filter) 97 | 98 | [从零手写实现 tomcat-12-listener 监听器](https://houbb.github.io/2016/11/07/web-server-tomcat-12-hand-write-listener) 99 | 100 | 101 | # ROAD-MAP 102 | 103 | - [x] servlet 标准支持 104 | - [x] 请求线程池支持 105 | - [x] NIO 实现 106 | - [x] netty 实现 107 | - [x] 加载 war 包 108 | - [x] listener 的实现 109 | - [] error/welcome 页面? 110 | - [] printWriter 等兼容不够优雅 111 | - [] 内嵌支持? 112 | - [] session 113 | - [] 注解的解析支持 114 | -------------------------------------------------------------------------------- /cgit.bat: -------------------------------------------------------------------------------- 1 | :: 用于提交当前变更(windows) 2 | :: author: houbb 3 | :: LastUpdateTime: 2018-11-22 09:08:52 4 | :: 用法:双击运行,或者当前路径 cmd 直接输入 .\cgit.bat 5 | 6 | git pull 7 | git add . 8 | git commit -m "[Feature] add for new" 9 | git push 10 | git status 11 | 12 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.github.houbb 8 | minicat 9 | 0.7.0 10 | 11 | 12 | 13 | 3.2 14 | 3.2 15 | 2.18.1 16 | false 17 | false 18 | 19 | 2.2.1 20 | 2.9.1 21 | 1.5 22 | 23 | 4.3.0 24 | 2.7 25 | 26 | 27 | UTF-8 28 | 1.8 29 | 30 | 31 | 1.1.8 32 | 0.9.0 33 | 34 | 35 | 4.13 36 | 4.0.1 37 | 1.6.1 38 | 1.1.6 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | com.github.houbb 50 | heaven 51 | ${heaven.version} 52 | 53 | 54 | com.github.houbb 55 | log-integration 56 | ${log-integration.version} 57 | 58 | 59 | 60 | 61 | junit 62 | junit 63 | ${junit.version} 64 | 65 | 66 | javax.servlet 67 | javax.servlet-api 68 | ${javax.servlet.version} 69 | 70 | 71 | 72 | dom4j 73 | dom4j 74 | ${dom4j.version} 75 | 76 | 77 | jaxen 78 | jaxen 79 | ${jaxen.version} 80 | 81 | 82 | 83 | io.netty 84 | netty-all 85 | 4.1.65.Final 86 | 87 | 88 | 89 | 90 | 91 | 92 | com.github.houbb 93 | heaven 94 | 95 | 96 | com.github.houbb 97 | log-integration 98 | 99 | 100 | 101 | javax.servlet 102 | javax.servlet-api 103 | 104 | 105 | 106 | dom4j 107 | dom4j 108 | 109 | 110 | jaxen 111 | jaxen 112 | 113 | 114 | 115 | io.netty 116 | netty-all 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | org.apache.maven.plugins 125 | maven-compiler-plugin 126 | ${plugin.compiler.version} 127 | 128 | ${project.compiler.level} 129 | ${project.compiler.level} 130 | ${project.build.sourceEncoding} 131 | -proc:none 132 | 133 | 134 | 135 | 136 | 137 | org.apache.maven.plugins 138 | maven-javadoc-plugin 139 | ${plugin.maven-javadoc-plugin.version} 140 | 141 | 142 | 143 | 144 | 145 | minicat 146 | The minicat tool for java 147 | 148 | 149 | org.sonatype.oss 150 | oss-parent 151 | 7 152 | 153 | 154 | 155 | The Apache Software License, Version 2.0 156 | http://www.apache.org/licenses/LICENSE-2.0.txt 157 | repo 158 | 159 | 160 | 161 | https://github.com/houbb/iexcel 162 | https://github.com/houbb/minicat.git 163 | https://houbb.github.io/ 164 | 165 | 166 | 167 | houbb 168 | houbinbin.echo@gmail.com 169 | https://houbb.github.io/ 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | release 179 | 180 | 181 | 182 | 183 | org.apache.maven.plugins 184 | maven-source-plugin 185 | ${plugin.maven-source-plugin.version} 186 | 187 | 188 | package 189 | 190 | jar-no-fork 191 | 192 | 193 | 194 | 195 | 196 | 197 | org.apache.maven.plugins 198 | maven-javadoc-plugin 199 | ${plugin.maven-javadoc-plugin.version} 200 | 201 | 202 | package 203 | 204 | jar 205 | 206 | 207 | 208 | 209 | 210 | 211 | org.apache.maven.plugins 212 | maven-gpg-plugin 213 | ${plugin.maven-gpg-plugin.version} 214 | 215 | 216 | verify 217 | 218 | sign 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | org.eluder.coveralls 228 | coveralls-maven-plugin 229 | ${plugin.coveralls.version} 230 | 231 | 232 | 233 | org.codehaus.mojo 234 | cobertura-maven-plugin 235 | ${plugin.cobertura.version} 236 | 237 | xml 238 | 256m 239 | 240 | true 241 | 242 | 243 | **/*Test.class 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | oss 255 | https://oss.sonatype.org/content/repositories/snapshots/ 256 | 257 | 258 | oss 259 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 260 | 261 | 262 | 263 | 264 | 265 | -------------------------------------------------------------------------------- /release.bat: -------------------------------------------------------------------------------- 1 | :: 用于 release 当前项目(windows) 2 | :: author: houbb 3 | :: LastUpdateTime: 2018-1-22 09:08:52 4 | :: 用法:双击运行,或者当前路径 cmd 直接输入 release.bat 5 | 6 | :: 关闭回显 7 | @echo OFF 8 | 9 | ECHO "============================= RELEASE START..." 10 | 11 | :: 版本号信息(需要手动指定) 12 | :::: 旧版本名称 13 | SET version=0.7.0 14 | :::: 新版本名称 15 | SET newVersion=0.8.0 16 | :::: 组织名称 17 | SET groupName=com.github.houbb 18 | :::: 项目名称 19 | SET projectName=minicat 20 | 21 | :: release 项目版本 22 | :::: snapshot 版本号 23 | SET snapshot_version=%version%"-SNAPSHOT" 24 | :::: 新的版本号 25 | SET release_version=%version% 26 | 27 | call mvn versions:set -DgroupId=%groupName% -DartifactId=%projectName% -DoldVersion=%snapshot_version% -DnewVersion=%release_version% 28 | call mvn -N versions:update-child-modules 29 | call mvn versions:commit 30 | call echo "1. RELEASE %snapshot_version% TO %release_version% DONE." 31 | 32 | 33 | :: 推送到 github 34 | git add . 35 | git commit -m "release branch %version%" 36 | git push 37 | git status 38 | 39 | ECHO "2. PUSH TO GITHUB DONE." 40 | 41 | :: 推送到 maven 中央仓库 42 | call mvn clean deploy -P release 43 | ECHO "3 PUSH TO MVN CENTER DONE." 44 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | echo "============================= RELEASE START..." 3 | 4 | ## 版本号信息(需要手动指定) 5 | oldVersion="${version}" 6 | newVersion="${version}" 7 | projectName="${artifactId}" 8 | 9 | # release 项目版本 10 | ## snapshot 版本号 11 | snapshot_version=${oldVersion}"-SNAPSHOT" 12 | ## 新的版本号 13 | release_version=${oldVersion} 14 | 15 | mvn versions:set -DgroupId=com.github.houbb -DartifactId=${projectName} -DoldVersion=${snapshot_version} -DnewVersion=${release_version} 16 | mvn -N versions:update-child-modules 17 | mvn versions:commit 18 | echo "1. RELEASE ${snapshot_version} TO ${release_version} DONE." 19 | 20 | 21 | # 推送到 github 22 | git add . 23 | git commit -m "release branch ${oldVersion}" 24 | git push 25 | git status 26 | 27 | echo "2. PUSH TO GITHUB DONE." 28 | 29 | 30 | # 推送到 maven 中央仓库 31 | mvn clean deploy -P release 32 | 33 | echo "3. PUSH TO MAVEN CENTER DONE." 34 | 35 | # 合并到 master 分支 36 | branchName="release_"${oldVersion} # 分支名称 37 | git checkout master 38 | git pull 39 | git checkout ${branchName} 40 | git rebase master 41 | git checkout master 42 | git merge ${branchName} 43 | git push 44 | 45 | echo "4. MERGE TO MASTER DONE." 46 | 47 | 48 | # 拉取新的分支 49 | newBranchName="release_"${newVersion} 50 | git branch ${newBranchName} 51 | git checkout ${newBranchName} 52 | git push --set-upstream origin ${newBranchName} 53 | 54 | echo "5. NEW BRANCH DONE." 55 | 56 | # 修改新分支的版本号 57 | ## snapshot 版本号 58 | snapshot_new_version=${newVersion}"-SNAPSHOT" 59 | mvn versions:set -DgroupId=com.github.houbb -DartifactId=${projectName} -DoldVersion=${release_version} -DnewVersion=${snapshot_new_version} 60 | mvn -N versions:update-child-modules 61 | mvn versions:commit 62 | 63 | git add . 64 | git commit -m "modify branch ${release_version} TO ${snapshot_new_version}" 65 | git push 66 | git status 67 | echo "6. MODIFY ${release_version} TO ${snapshot_new_version} DONE." 68 | 69 | echo "============================= RELEASE END..." 70 | 71 | 72 | # 使用方式: 73 | # 1. 赋值权限: chmod +x ./release.sh 74 | # 2. 执行: ./release.sh 75 | # Last Update Time: 2018-01-20 12:07:34 76 | # Author: houbb 77 | 78 | 79 | -------------------------------------------------------------------------------- /release_rm.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | echo "============================= RELEASE START..." 3 | 4 | ## 版本号信息(需要手动指定) 5 | oldVersion="1.0.0" 6 | newVersion="1.0.0" 7 | projectName="${artifactId}" 8 | 9 | # 删除分支 10 | oldBranchName="release_"${oldVersion} 11 | git branch -d ${oldBranchName} 12 | git push origin --delete ${oldBranchName} 13 | 14 | echo "1. Branch remove success..." 15 | 16 | # 拉取新的分支 17 | newBranchName="release_"${newVersion} 18 | git branch ${newBranchName} 19 | git checkout ${newBranchName} 20 | git push --set-upstream origin ${newBranchName} 21 | 22 | echo "2. NEW BRANCH DONE." 23 | 24 | # 修改新分支的版本号 25 | ## snapshot 版本号 26 | snapshot_new_version=${newVersion}"-SNAPSHOT" 27 | mvn versions:set -DgroupId=com.github.houbb -DartifactId=${projectName} -DoldVersion=${release_version} -DnewVersion=${snapshot_new_version} 28 | mvn -N versions:update-child-modules 29 | mvn versions:commit 30 | 31 | git add . 32 | git commit -m "modify branch ${release_version} TO ${snapshot_new_version}" 33 | git push 34 | git status 35 | echo "3. MODIFY ${release_version} TO ${snapshot_new_version} DONE." 36 | 37 | echo "============================= BRANCH RE-CREATE END..." 38 | 39 | echo "============================= BRANCH LIST =============================" 40 | git branch -a 41 | 42 | # 使用方式: 43 | # 注意:本脚本用于删除分支,谨慎使用! 44 | # 1. 赋值权限: chmod +x ./release_rm.sh 45 | # 2. 执行: ./release_rm.sh 46 | # Last Update Time: 2018-06-21 11:10:42 47 | # Author: houbb -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/MiniCatBootstrapMain.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat; 2 | 3 | import com.github.houbb.minicat.bs.MiniCatBootstrap; 4 | 5 | import java.util.concurrent.TimeUnit; 6 | 7 | /** 8 | * 这个文件的位置很重要。 9 | */ 10 | public class MiniCatBootstrapMain { 11 | 12 | public static void main(String[] args) throws InterruptedException { 13 | MiniCatBootstrap bootstrap = new MiniCatBootstrap(); 14 | bootstrap.start(); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/bo/RequestInfoBo.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.bo; 2 | 3 | public class RequestInfoBo { 4 | 5 | private String url; 6 | 7 | private String method; 8 | 9 | public RequestInfoBo(String url, String method) { 10 | this.url = url; 11 | this.method = method; 12 | } 13 | 14 | public String getUrl() { 15 | return url; 16 | } 17 | 18 | public void setUrl(String url) { 19 | this.url = url; 20 | } 21 | 22 | public String getMethod() { 23 | return method; 24 | } 25 | 26 | public void setMethod(String method) { 27 | this.method = method; 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return "RequestInfoBo{" + 33 | "url='" + url + '\'' + 34 | ", method='" + method + '\'' + 35 | '}'; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/bs/MiniCatBootstrap.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.bs; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | import com.github.houbb.minicat.bs.servlet.MiniCatBootstrapNetty; 6 | import com.github.houbb.minicat.exception.MiniCatException; 7 | import com.github.houbb.minicat.support.attr.DefaultMiniCatAttrManager; 8 | import com.github.houbb.minicat.support.attr.IMiniCatAttrManager; 9 | import com.github.houbb.minicat.support.context.IMiniCatContextInit; 10 | import com.github.houbb.minicat.support.context.LocalMiniCatContextInit; 11 | import com.github.houbb.minicat.support.context.MiniCatContextConfig; 12 | import com.github.houbb.minicat.support.filter.manager.DefaultFilterManager; 13 | import com.github.houbb.minicat.support.filter.manager.IFilterManager; 14 | import com.github.houbb.minicat.support.listener.DefaultListenerManager; 15 | import com.github.houbb.minicat.support.listener.IListenerManager; 16 | import com.github.houbb.minicat.support.request.IRequestDispatcher; 17 | import com.github.houbb.minicat.support.request.RequestDispatcherManager; 18 | import com.github.houbb.minicat.support.servlet.manager.DefaultServletManager; 19 | import com.github.houbb.minicat.support.servlet.manager.IServletManager; 20 | import com.github.houbb.minicat.support.war.IWarExtractor; 21 | import com.github.houbb.minicat.support.war.WarExtractorDefault; 22 | import com.github.houbb.minicat.util.InnerResourceUtil; 23 | import io.netty.bootstrap.ServerBootstrap; 24 | import io.netty.channel.ChannelFuture; 25 | import io.netty.channel.ChannelInitializer; 26 | import io.netty.channel.ChannelOption; 27 | import io.netty.channel.EventLoopGroup; 28 | import io.netty.channel.nio.NioEventLoopGroup; 29 | import io.netty.channel.socket.SocketChannel; 30 | import io.netty.channel.socket.nio.NioServerSocketChannel; 31 | 32 | import javax.servlet.ServletContextListener; 33 | import java.util.function.Consumer; 34 | 35 | /** 36 | * @since 0.1.0 37 | * @author 老马啸西风 38 | */ 39 | public class MiniCatBootstrap { 40 | 41 | private static final Log logger = LogFactory.getLog(MiniCatBootstrapNetty.class); 42 | 43 | /** 44 | * 启动端口号 45 | */ 46 | private final int port; 47 | 48 | /** 49 | * 默认文件夹 50 | * @since 0.5.0 51 | */ 52 | private String baseDir = InnerResourceUtil.getClassRootResource(MiniCatBootstrap.class); 53 | 54 | /** 55 | * war 解压管理 56 | * 57 | * @since 0.5.0 58 | */ 59 | private IWarExtractor warExtractor = new WarExtractorDefault(); 60 | 61 | /** 62 | * servlet 管理 63 | * 64 | * @since 0.5.0 65 | */ 66 | private IServletManager servletManager = new DefaultServletManager(); 67 | 68 | /** 69 | * 过滤管理器 70 | */ 71 | private IFilterManager filterManager = new DefaultFilterManager(); 72 | 73 | /** 74 | * 请求分发 75 | * @since 0.5.0 76 | */ 77 | private IRequestDispatcher requestDispatcher = new RequestDispatcherManager(); 78 | 79 | /** 80 | * 上下文初始化 81 | */ 82 | private IMiniCatContextInit miniCatContextInit = new LocalMiniCatContextInit(); 83 | 84 | /** 85 | * 监听器管理类 86 | * @since 0.7.0 87 | */ 88 | private IListenerManager listenerManager = new DefaultListenerManager(); 89 | 90 | /** 91 | * 属性管理 92 | */ 93 | private IMiniCatAttrManager miniCatAttrManager = new DefaultMiniCatAttrManager(); 94 | 95 | public MiniCatBootstrap(final int port, final String baseDir) { 96 | this.port = 8080; 97 | this.baseDir = baseDir; 98 | } 99 | 100 | public MiniCatBootstrap(final int port) { 101 | this.port = port; 102 | } 103 | 104 | public MiniCatBootstrap() { 105 | this(8080); 106 | } 107 | 108 | public void setFilterManager(IFilterManager filterManager) { 109 | this.filterManager = filterManager; 110 | } 111 | 112 | public void setMiniCatContextInit(IMiniCatContextInit miniCatContextInit) { 113 | this.miniCatContextInit = miniCatContextInit; 114 | } 115 | 116 | public void setBaseDir(String baseDir) { 117 | this.baseDir = baseDir; 118 | } 119 | 120 | public void setWarExtractor(IWarExtractor warExtractor) { 121 | this.warExtractor = warExtractor; 122 | } 123 | 124 | public void setServletManager(IServletManager servletManager) { 125 | this.servletManager = servletManager; 126 | } 127 | 128 | public void setRequestDispatcher(IRequestDispatcher requestDispatcher) { 129 | this.requestDispatcher = requestDispatcher; 130 | } 131 | 132 | public void setListenerManager(IListenerManager listenerManager) { 133 | this.listenerManager = listenerManager; 134 | } 135 | 136 | public void setMiniCatAttrManager(IMiniCatAttrManager miniCatAttrManager) { 137 | this.miniCatAttrManager = miniCatAttrManager; 138 | } 139 | 140 | protected MiniCatContextConfig buildMiniCatContextConfig() { 141 | MiniCatContextConfig config = new MiniCatContextConfig(); 142 | config.setPort(port); 143 | config.setServletManager(servletManager); 144 | config.setFilterManager(filterManager); 145 | config.setBaseDir(baseDir); 146 | config.setWarExtractor(warExtractor); 147 | config.setRequestDispatcher(requestDispatcher); 148 | config.setListenerManager(listenerManager); 149 | config.setMiniCatAttrManager(miniCatAttrManager); 150 | return config; 151 | } 152 | 153 | public void start() { 154 | logger.info("[MiniCat] start listen on port {}", port); 155 | logger.info("[MiniCat] visit url http://{}:{}", "127.0.0.1", port); 156 | 157 | EventLoopGroup bossGroup = new NioEventLoopGroup(); 158 | //worker 线程池的数量默认为 CPU 核心数的两倍 159 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 160 | final MiniCatContextConfig miniCatContextConfig = buildMiniCatContextConfig(); 161 | try { 162 | logger.info("[MiniCat] config={}", miniCatContextConfig); 163 | // 初始化 164 | this.miniCatContextInit.init(miniCatContextConfig); 165 | 166 | // init listener 监听器 167 | if(miniCatContextConfig != null) { 168 | listenerManager.getServletContextListeners() 169 | .forEach(servletContextListener -> servletContextListener.contextInitialized(miniCatContextConfig)); 170 | } 171 | 172 | ServerBootstrap serverBootstrap = new ServerBootstrap(); 173 | serverBootstrap.group(bossGroup, workerGroup) 174 | .channel(NioServerSocketChannel.class) 175 | .childHandler(new ChannelInitializer() { 176 | @Override 177 | protected void initChannel(SocketChannel ch) throws Exception { 178 | ch.pipeline().addLast(new MiniCatServerHandler(miniCatContextConfig)); 179 | } 180 | }) 181 | .option(ChannelOption.SO_BACKLOG, 128) 182 | .childOption(ChannelOption.SO_KEEPALIVE, true); 183 | 184 | // Bind and start to accept incoming connections. 185 | ChannelFuture future = serverBootstrap.bind(port).sync(); 186 | 187 | // Wait until the server socket is closed. 188 | future.channel().closeFuture().sync(); 189 | logger.info("DONE"); 190 | } catch (InterruptedException e) { 191 | logger.error("[MiniCat] start meet ex", e); 192 | throw new MiniCatException(e); 193 | } finally { 194 | workerGroup.shutdownGracefully(); 195 | bossGroup.shutdownGracefully(); 196 | 197 | // 关闭 198 | if(miniCatContextConfig != null) { 199 | listenerManager.getServletContextListeners() 200 | .forEach(servletContextListener -> servletContextListener.contextDestroyed(miniCatContextConfig)); 201 | } 202 | } 203 | } 204 | 205 | 206 | 207 | } 208 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/bs/MiniCatBootstrapBio.java: -------------------------------------------------------------------------------- 1 | //package com.github.houbb.minicat.bs; 2 | // 3 | //import com.github.houbb.log.integration.core.Log; 4 | //import com.github.houbb.log.integration.core.LogFactory; 5 | //import com.github.houbb.minicat.dto.MiniCatRequestBio; 6 | //import com.github.houbb.minicat.dto.MiniCatResponseBio; 7 | //import com.github.houbb.minicat.exception.MiniCatException; 8 | //import com.github.houbb.minicat.support.request.IRequestDispatcher; 9 | //import com.github.houbb.minicat.support.request.RequestDispatcherManager; 10 | //import com.github.houbb.minicat.support.servlet.manager.IServletManager; 11 | //import com.github.houbb.minicat.support.servlet.manager.LocalRootServletManager; 12 | // 13 | //import java.io.IOException; 14 | //import java.io.InputStream; 15 | //import java.net.ServerSocket; 16 | //import java.net.Socket; 17 | //import java.util.concurrent.ExecutorService; 18 | //import java.util.concurrent.Executors; 19 | // 20 | ///** 21 | // * @since 0.1.0 22 | // * @author 老马啸西风 23 | // */ 24 | //public class MiniCatBootstrapBio { 25 | // 26 | // private static final Log logger = LogFactory.getLog(MiniCatBootstrapBio.class); 27 | // 28 | // /** 29 | // * 启动端口号 30 | // */ 31 | // private final int port; 32 | // 33 | // /** 34 | // * 是否运行的标识 35 | // */ 36 | // private volatile boolean runningFlag = false; 37 | // 38 | // /** 39 | // * 服务端 socket 40 | // */ 41 | // private ServerSocket serverSocket; 42 | // 43 | // /** 44 | // * servlet 管理 45 | // * 46 | // * @since 0.3.0 47 | // */ 48 | // private IServletManager servletManager = new LocalRootServletManager(); 49 | // 50 | // /** 51 | // * 请求分发 52 | // * 53 | // * @since 0.3.0 54 | // */ 55 | // private IRequestDispatcher requestDispatcher = new RequestDispatcherManager(); 56 | // 57 | // public IRequestDispatcher getRequestDispatcher() { 58 | // return requestDispatcher; 59 | // } 60 | // 61 | // public void setRequestDispatcher(IRequestDispatcher requestDispatcher) { 62 | // this.requestDispatcher = requestDispatcher; 63 | // } 64 | // 65 | // public IServletManager getServletManager() { 66 | // return servletManager; 67 | // } 68 | // 69 | // public void setServletManager(IServletManager servletManager) { 70 | // this.servletManager = servletManager; 71 | // } 72 | // 73 | // private final ExecutorService threadPool; 74 | // 75 | // public MiniCatBootstrapBio(int port, int threadPoolSize) { 76 | // this.port = port; 77 | // this.threadPool = Executors.newFixedThreadPool(threadPoolSize); 78 | // } 79 | // 80 | // public MiniCatBootstrapBio(int port) { 81 | // this(port, 10); 82 | // } 83 | // 84 | // public MiniCatBootstrapBio() { 85 | // this(8080); 86 | // } 87 | // 88 | // public void start() { 89 | // // 引入线程池 90 | // Thread serverThread = new Thread(new Runnable() { 91 | // @Override 92 | // public void run() { 93 | // startSync(); 94 | // } 95 | // }); 96 | // 97 | // // 启动 98 | // serverThread.start(); 99 | // } 100 | // 101 | // /** 102 | // * 服务的启动 103 | // */ 104 | // public void startSync() { 105 | // if(runningFlag) { 106 | // logger.warn("[MiniCat] server is already start!"); 107 | // return; 108 | // } 109 | // 110 | // logger.info("[MiniCat] start listen on port {}", port); 111 | // logger.info("[MiniCat] visit url http://{}:{}", "127.0.0.1", port); 112 | // 113 | // try { 114 | // this.serverSocket = new ServerSocket(port); 115 | // runningFlag = true; 116 | // 117 | // while(runningFlag && !serverSocket.isClosed()){ 118 | // //TRW 119 | // Socket socket = serverSocket.accept(); 120 | // // 提交到线程池处理 121 | // threadPool.execute(new RequestHandler(socket)); 122 | // } 123 | // 124 | // logger.info("[MiniCat] end listen on port {}", port); 125 | // } catch (Exception e) { 126 | // logger.error("[MiniCat] start meet ex", e); 127 | // throw new MiniCatException(e); 128 | // } 129 | // } 130 | // 131 | // private class RequestHandler implements Runnable { 132 | // private final Socket socket; 133 | // 134 | // public RequestHandler(Socket socket) { 135 | // this.socket = socket; 136 | // } 137 | // 138 | // @Override 139 | // public void run() { 140 | // try { 141 | // InputStream inputStream = socket.getInputStream(); 142 | // MiniCatRequestBio request = new MiniCatRequestBio(inputStream); 143 | // MiniCatResponseBio response = new MiniCatResponseBio(socket.getOutputStream()); 144 | // 145 | // final RequestDispatcherContext dispatcherContext = new RequestDispatcherContext(); 146 | // dispatcherContext.setRequest(request); 147 | // dispatcherContext.setResponse(response); 148 | // dispatcherContext.setServletManager(servletManager); 149 | // requestDispatcher.dispatch(dispatcherContext); 150 | // 151 | // socket.close(); 152 | // } catch (IOException e) { 153 | // logger.error("[MiniCat] error handling request", e); 154 | // } 155 | // } 156 | // } 157 | // 158 | // /** 159 | // * 服务的暂停 160 | // */ 161 | // public void stop() { 162 | // logger.info("[MiniCat] stop called!"); 163 | // 164 | // if(!runningFlag) { 165 | // logger.warn("[MiniCat] server is not start!"); 166 | // return; 167 | // } 168 | // 169 | // try { 170 | // if(this.serverSocket != null) { 171 | // serverSocket.close(); 172 | // } 173 | // this.runningFlag = false; 174 | // 175 | // logger.info("[MiniCat] stop listen on port {}", port); 176 | // } catch (IOException e) { 177 | // logger.error("[MiniCat] stop meet ex", e); 178 | // throw new MiniCatException(e); 179 | // } 180 | // } 181 | // 182 | //} 183 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/bs/MiniCatServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.bs; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | import com.github.houbb.minicat.bo.RequestInfoBo; 6 | import com.github.houbb.minicat.dto.IMiniCatRequest; 7 | import com.github.houbb.minicat.dto.IMiniCatResponse; 8 | import com.github.houbb.minicat.dto.MiniCatRequestCommon; 9 | import com.github.houbb.minicat.dto.MiniCatResponseCommon; 10 | import com.github.houbb.minicat.support.context.MiniCatContextConfig; 11 | import com.github.houbb.minicat.support.listener.IListenerManager; 12 | import com.github.houbb.minicat.support.request.IRequestDispatcher; 13 | import com.github.houbb.minicat.support.writer.MyPrintWriter; 14 | import com.github.houbb.minicat.util.InnerRequestUtil; 15 | import io.netty.buffer.ByteBuf; 16 | import io.netty.buffer.Unpooled; 17 | import io.netty.channel.ChannelFutureListener; 18 | import io.netty.channel.ChannelHandlerContext; 19 | import io.netty.channel.ChannelInboundHandlerAdapter; 20 | 21 | import javax.servlet.ReadListener; 22 | import javax.servlet.ServletRequestEvent; 23 | import javax.servlet.ServletRequestListener; 24 | import javax.servlet.WriteListener; 25 | import java.io.File; 26 | import java.io.IOException; 27 | import java.io.PrintWriter; 28 | import java.nio.charset.Charset; 29 | import java.util.List; 30 | 31 | class MiniCatServerHandler extends ChannelInboundHandlerAdapter { 32 | 33 | private static final Log logger = LogFactory.getLog(MiniCatServerHandler.class); 34 | 35 | 36 | /** 37 | * 请求分发 38 | * 39 | * @since 0.3.0 40 | */ 41 | private final IRequestDispatcher requestDispatcher; 42 | 43 | /** 44 | * 配置信息 45 | * 46 | * @since 0.6.0 47 | */ 48 | private final MiniCatContextConfig miniCatContextConfig; 49 | 50 | /** 51 | * 监听器管理类 52 | * 53 | * @since 0.7.0 54 | */ 55 | private final IListenerManager listenerManager; 56 | 57 | MiniCatServerHandler(final MiniCatContextConfig miniCatContextConfig) { 58 | this.requestDispatcher = miniCatContextConfig.getRequestDispatcher(); 59 | this.miniCatContextConfig = miniCatContextConfig; 60 | this.listenerManager = miniCatContextConfig.getListenerManager(); 61 | } 62 | 63 | @Override 64 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 65 | final List readListeners = listenerManager.getReadListeners(); 66 | 67 | String requestString = null; 68 | try { 69 | ByteBuf buf = (ByteBuf) msg; 70 | byte[] bytes = new byte[buf.readableBytes()]; 71 | buf.readBytes(bytes); 72 | requestString = new String(bytes, Charset.defaultCharset()); 73 | logger.info("[MiniCat] channelRead requestString={}", requestString); 74 | 75 | // 读取 76 | readListeners.forEach(readListener -> { 77 | try { 78 | readListener.onDataAvailable(); 79 | readListener.onAllDataRead(); 80 | } catch (IOException e) { 81 | throw new RuntimeException(e); 82 | } 83 | }); 84 | } catch (Exception e) { 85 | // 读取 86 | readListeners.forEach(readListener -> { 87 | readListener.onError(e); 88 | }); 89 | logger.info("[MiniCat] channelRead meet ex", e); 90 | } 91 | 92 | 93 | // 获取请求信息 94 | RequestInfoBo requestInfoBo = InnerRequestUtil.buildRequestInfoBo(requestString); 95 | IMiniCatRequest request = new MiniCatRequestCommon(requestInfoBo.getMethod(), requestInfoBo.getUrl()); 96 | // 初始化 request 97 | final ServletRequestEvent servletRequestEvent = new ServletRequestEvent(miniCatContextConfig.getServletContext(), request); 98 | final List servletRequestListenerList = listenerManager.getServletRequestListeners(); 99 | servletRequestListenerList.forEach(servletRequestListener -> servletRequestListener.requestInitialized(servletRequestEvent)); 100 | 101 | IMiniCatResponse response = new MiniCatResponseCommon() { 102 | // 兼容一下 getPrint 的写法,其实实现的还是过于简陋了。 103 | @Override 104 | public PrintWriter getWriter() throws IOException { 105 | // 创建临时文件,文件名前缀为"temp",后缀为".txt",存放在默认临时目录中 106 | File tempFile = File.createTempFile("MiniCatTempFile", ".txt"); 107 | // 可选:设置临时文件在JVM退出时自动删除 108 | tempFile.deleteOnExit(); 109 | 110 | MyPrintWriter myPrintWriter = new MyPrintWriter(tempFile.getAbsoluteFile()); 111 | myPrintWriter.setCtx(ctx); 112 | 113 | return myPrintWriter; 114 | } 115 | 116 | @Override 117 | public void write(String text, String charsetStr) { 118 | final List writeListeners = listenerManager.getWriteListeners(); 119 | try { 120 | Charset charset = Charset.forName(charsetStr); 121 | ByteBuf responseBuf = Unpooled.copiedBuffer(text, charset); 122 | ctx.writeAndFlush(responseBuf) 123 | .addListener(ChannelFutureListener.CLOSE); // Close the channel after sending the response 124 | logger.info("[MiniCat] channelRead writeAndFlush DONE"); 125 | 126 | // 写入 127 | writeListeners.forEach(writeListener -> { 128 | try { 129 | writeListener.onWritePossible(); 130 | } catch (IOException e) { 131 | throw new RuntimeException(e); 132 | } 133 | }); 134 | } catch (Exception e) { 135 | logger.error("[MiniCat] channel write failed", e); 136 | // 写入 137 | writeListeners.forEach(writeListener -> { 138 | writeListener.onError(e); 139 | }); 140 | } 141 | } 142 | }; 143 | 144 | // 分发调用 145 | requestDispatcher.dispatch(request, response, miniCatContextConfig); 146 | 147 | // 结束 148 | servletRequestListenerList.forEach(servletRequestListener -> servletRequestListener.requestDestroyed(servletRequestEvent)); 149 | } 150 | 151 | @Override 152 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 153 | ctx.flush(); 154 | } 155 | 156 | @Override 157 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 158 | logger.error("exceptionCaught", cause); 159 | ctx.close(); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/bs/servlet/MiniCatBootstrapBioSocket.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.bs.servlet; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | import com.github.houbb.minicat.exception.MiniCatException; 6 | import com.github.houbb.minicat.util.InnerHttpUtil; 7 | 8 | import java.io.*; 9 | import java.net.ServerSocket; 10 | import java.net.Socket; 11 | import java.util.concurrent.ExecutorService; 12 | import java.util.concurrent.Executors; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | /** 16 | * @author 老马啸西风 17 | * @since 0.1.0 18 | */ 19 | public class MiniCatBootstrapBioSocket { 20 | 21 | private static final Log logger = LogFactory.getLog(MiniCatBootstrapBioSocket.class); 22 | 23 | /** 24 | * 启动端口号 25 | */ 26 | private final int port; 27 | 28 | /** 29 | * 服务端 socket 30 | */ 31 | private ServerSocket serverSocket; 32 | 33 | public MiniCatBootstrapBioSocket() { 34 | this.port = 8080; 35 | } 36 | 37 | public void start() { 38 | logger.info("[MiniCat] start listen on port {}", port); 39 | logger.info("[MiniCat] visit url http://{}:{}", "127.0.0.1", port); 40 | 41 | try { 42 | this.serverSocket = new ServerSocket(port); 43 | 44 | while (true) { 45 | Socket clientSocket = serverSocket.accept(); // 等待客户端连接 46 | 47 | // 从Socket获取输入流 48 | logger.info("readRequestString start"); 49 | String requestString = readRequestString(clientSocket); 50 | logger.info("readRequestString end"); 51 | 52 | // 这里模拟一下耗时呢 53 | TimeUnit.SECONDS.sleep(5); 54 | 55 | // 写回到客户端 56 | logger.info("writeToClient start"); 57 | writeToClient(clientSocket, requestString); 58 | logger.info("writeToClient end"); 59 | 60 | // 关闭连接 61 | clientSocket.close(); 62 | } 63 | 64 | 65 | } catch (Exception e) { 66 | logger.error("[MiniCat] start meet ex", e); 67 | throw new MiniCatException(e); 68 | } 69 | } 70 | 71 | private void writeToClient(Socket clientSocket, String requestString) throws IOException { 72 | OutputStream outputStream = clientSocket.getOutputStream(); 73 | String httpText = InnerHttpUtil.http200Resp("ECHO: \r\n" + requestString); 74 | outputStream.write(httpText.getBytes("UTF-8")); 75 | } 76 | 77 | private String readRequestString(Socket clientSocket) throws IOException { 78 | // 从Socket获取输入流 79 | BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 80 | StringBuilder requestBuilder = new StringBuilder(); 81 | String line; 82 | // 读取HTTP请求直到空行(表示HTTP请求结束) 83 | while ((line = reader.readLine()) != null && !line.isEmpty()) { 84 | requestBuilder.append(line).append("\n"); 85 | } 86 | return requestBuilder.toString(); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/bs/servlet/MiniCatBootstrapBioThreadSocket.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.bs.servlet; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | import com.github.houbb.minicat.exception.MiniCatException; 6 | import com.github.houbb.minicat.util.InnerHttpUtil; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.IOException; 10 | import java.io.InputStreamReader; 11 | import java.io.OutputStream; 12 | import java.net.ServerSocket; 13 | import java.net.Socket; 14 | import java.util.concurrent.ExecutorService; 15 | import java.util.concurrent.Executors; 16 | import java.util.concurrent.TimeUnit; 17 | 18 | /** 19 | * 实际测试还是会阻塞 20 | * 21 | * @author 老马啸西风 22 | * @since 0.1.0 23 | */ 24 | public class MiniCatBootstrapBioThreadSocket { 25 | 26 | private static final Log logger = LogFactory.getLog(MiniCatBootstrapBioThreadSocket.class); 27 | 28 | /** 29 | * 启动端口号 30 | */ 31 | private final int port; 32 | 33 | /** 34 | * 服务端 socket 35 | */ 36 | private ServerSocket serverSocket; 37 | 38 | private final ExecutorService threadPool; 39 | 40 | public MiniCatBootstrapBioThreadSocket() { 41 | this.port = 8080; 42 | 43 | threadPool = Executors.newFixedThreadPool(10); 44 | } 45 | 46 | public void start() { 47 | logger.info("[MiniCat] start listen on port {}", port); 48 | logger.info("[MiniCat] visit url http://{}:{}", "127.0.0.1", port); 49 | 50 | try { 51 | this.serverSocket = new ServerSocket(port); 52 | 53 | while (true) { 54 | Socket clientSocket = serverSocket.accept(); // 等待客户端连接 55 | 56 | // 从Socket获取输入流 57 | threadPool.submit(new Runnable() { 58 | @Override 59 | public void run() { 60 | handleSocket(clientSocket); 61 | } 62 | }); 63 | } 64 | 65 | 66 | } catch (Exception e) { 67 | logger.error("[MiniCat] start meet ex", e); 68 | throw new MiniCatException(e); 69 | } 70 | } 71 | 72 | private void handleSocket(Socket clientSocket) { 73 | try { 74 | logger.info("readRequestString start"); 75 | String requestString = readRequestString(clientSocket); 76 | logger.info("readRequestString end"); 77 | 78 | // 这里模拟一下耗时呢 79 | TimeUnit.SECONDS.sleep(5); 80 | 81 | // 写回到客户端 82 | logger.info("writeToClient start"); 83 | writeToClient(clientSocket, requestString); 84 | logger.info("writeToClient end"); 85 | 86 | // 关闭连接 87 | clientSocket.close(); 88 | } catch (IOException | InterruptedException e) { 89 | logger.error(""); 90 | } 91 | } 92 | 93 | private void writeToClient(Socket clientSocket, String requestString) throws IOException { 94 | OutputStream outputStream = clientSocket.getOutputStream(); 95 | String httpText = InnerHttpUtil.http200Resp("ECHO: \r\n" + requestString); 96 | outputStream.write(httpText.getBytes("UTF-8")); 97 | } 98 | 99 | private String readRequestString(Socket clientSocket) throws IOException { 100 | // 从Socket获取输入流 101 | BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 102 | StringBuilder requestBuilder = new StringBuilder(); 103 | String line; 104 | // 读取HTTP请求直到空行(表示HTTP请求结束) 105 | while ((line = reader.readLine()) != null && !line.isEmpty()) { 106 | requestBuilder.append(line).append("\n"); 107 | } 108 | return requestBuilder.toString(); 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/bs/servlet/MiniCatBootstrapNetty.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.bs.servlet; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | import com.github.houbb.minicat.exception.MiniCatException; 6 | import io.netty.bootstrap.ServerBootstrap; 7 | import io.netty.channel.ChannelFuture; 8 | import io.netty.channel.ChannelInitializer; 9 | import io.netty.channel.ChannelOption; 10 | import io.netty.channel.EventLoopGroup; 11 | import io.netty.channel.nio.NioEventLoopGroup; 12 | import io.netty.channel.socket.SocketChannel; 13 | import io.netty.channel.socket.nio.NioServerSocketChannel; 14 | 15 | 16 | public class MiniCatBootstrapNetty { 17 | 18 | private static final Log logger = LogFactory.getLog(MiniCatBootstrapNetty.class); 19 | 20 | /** 21 | * 启动端口号 22 | */ 23 | private final int port; 24 | 25 | public MiniCatBootstrapNetty() { 26 | this.port = 8080; 27 | } 28 | 29 | public void start() { 30 | logger.info("[MiniCat] start listen on port {}", port); 31 | logger.info("[MiniCat] visit url http://{}:{}", "127.0.0.1", port); 32 | 33 | EventLoopGroup bossGroup = new NioEventLoopGroup(); 34 | //worker 线程池的数量默认为 CPU 核心数的两倍 35 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 36 | 37 | try { 38 | ServerBootstrap serverBootstrap = new ServerBootstrap(); 39 | serverBootstrap.group(bossGroup, workerGroup) 40 | .channel(NioServerSocketChannel.class) 41 | .childHandler(new ChannelInitializer() { 42 | @Override 43 | protected void initChannel(SocketChannel ch) throws Exception { 44 | ch.pipeline().addLast(new MiniCatNettyServerHandler()); 45 | } 46 | }) 47 | .option(ChannelOption.SO_BACKLOG, 128) 48 | .childOption(ChannelOption.SO_KEEPALIVE, true); 49 | 50 | // Bind and start to accept incoming connections. 51 | ChannelFuture future = serverBootstrap.bind(port).sync(); 52 | 53 | // Wait until the server socket is closed. 54 | future.channel().closeFuture().sync(); 55 | } catch (InterruptedException e) { 56 | logger.error("[MiniCat] start meet ex", e); 57 | throw new MiniCatException(e); 58 | } finally { 59 | workerGroup.shutdownGracefully(); 60 | bossGroup.shutdownGracefully(); 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/bs/servlet/MiniCatBootstrapNioSocket.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.bs.servlet; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | import com.github.houbb.minicat.exception.MiniCatException; 6 | import com.github.houbb.minicat.util.InnerHttpUtil; 7 | 8 | import java.io.IOException; 9 | import java.net.InetSocketAddress; 10 | import java.nio.ByteBuffer; 11 | import java.nio.channels.SelectionKey; 12 | import java.nio.channels.Selector; 13 | import java.nio.channels.ServerSocketChannel; 14 | import java.nio.channels.SocketChannel; 15 | import java.util.Iterator; 16 | import java.util.Set; 17 | import java.util.concurrent.TimeUnit; 18 | 19 | public class MiniCatBootstrapNioSocket { 20 | 21 | private static final Log logger = LogFactory.getLog(MiniCatBootstrapNioSocket.class); 22 | 23 | private final int port; 24 | private ServerSocketChannel serverSocketChannel; 25 | private Selector selector; 26 | 27 | public MiniCatBootstrapNioSocket() { 28 | this.port = 8080; 29 | } 30 | 31 | public void start() { 32 | logger.info("[MiniCat] start listen on port {}", port); 33 | logger.info("[MiniCat] visit url http://{}:{}", "127.0.0.1", port); 34 | 35 | try { 36 | serverSocketChannel = ServerSocketChannel.open(); 37 | serverSocketChannel.bind(new InetSocketAddress(port)); 38 | serverSocketChannel.configureBlocking(false); 39 | 40 | selector = Selector.open(); 41 | serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); 42 | 43 | while (true) { 44 | int readyChannels = selector.select(); 45 | if (readyChannels == 0) { 46 | continue; 47 | } 48 | 49 | Set selectedKeys = selector.selectedKeys(); 50 | Iterator keyIterator = selectedKeys.iterator(); 51 | 52 | while (keyIterator.hasNext()) { 53 | SelectionKey key = keyIterator.next(); 54 | 55 | if (key.isAcceptable()) { 56 | handleAccept(key); 57 | } else if (key.isReadable()) { 58 | handleRead(key); 59 | } 60 | 61 | keyIterator.remove(); 62 | } 63 | } 64 | } catch (IOException | InterruptedException e) { 65 | logger.error("[MiniCat] start meet ex", e); 66 | throw new MiniCatException(e); 67 | } 68 | } 69 | 70 | private void handleAccept(SelectionKey key) throws IOException { 71 | ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel(); 72 | SocketChannel socketChannel = serverSocketChannel.accept(); 73 | socketChannel.configureBlocking(false); 74 | socketChannel.register(selector, SelectionKey.OP_READ); 75 | } 76 | 77 | private void handleRead(SelectionKey key) throws IOException, InterruptedException { 78 | logger.info("handle read start"); 79 | SocketChannel socketChannel = (SocketChannel) key.channel(); 80 | ByteBuffer buffer = ByteBuffer.allocate(1024); 81 | StringBuilder requestBuilder = new StringBuilder(); 82 | 83 | int bytesRead = socketChannel.read(buffer); 84 | while (bytesRead > 0) { 85 | buffer.flip(); 86 | while (buffer.hasRemaining()) { 87 | requestBuilder.append((char) buffer.get()); 88 | } 89 | buffer.clear(); 90 | bytesRead = socketChannel.read(buffer); 91 | } 92 | 93 | String requestString = requestBuilder.toString(); 94 | logger.info("handle read requestString={}", requestString); 95 | 96 | TimeUnit.SECONDS.sleep(5); // 模拟耗时操作 97 | 98 | logger.info("start write"); 99 | writeToClient(socketChannel, requestString); 100 | logger.info("end writeToClient"); 101 | 102 | socketChannel.close(); 103 | } 104 | 105 | private void writeToClient(SocketChannel socketChannel, String requestString) throws IOException { 106 | String httpText = InnerHttpUtil.http200Resp("ECHO: \r\n" + requestString); 107 | ByteBuffer buffer = ByteBuffer.wrap(httpText.getBytes("UTF-8")); 108 | socketChannel.write(buffer); 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/bs/servlet/MiniCatBootstrapNioThreadSocket.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.bs.servlet; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | import com.github.houbb.minicat.exception.MiniCatException; 6 | import com.github.houbb.minicat.util.InnerHttpUtil; 7 | 8 | import java.io.IOException; 9 | import java.net.InetSocketAddress; 10 | import java.nio.ByteBuffer; 11 | import java.nio.channels.SelectionKey; 12 | import java.nio.channels.Selector; 13 | import java.nio.channels.ServerSocketChannel; 14 | import java.nio.channels.SocketChannel; 15 | import java.util.Iterator; 16 | import java.util.Set; 17 | import java.util.concurrent.ExecutorService; 18 | import java.util.concurrent.Executors; 19 | import java.util.concurrent.TimeUnit; 20 | 21 | public class MiniCatBootstrapNioThreadSocket { 22 | 23 | private static final Log logger = LogFactory.getLog(MiniCatBootstrapNioThreadSocket.class); 24 | 25 | private final int port; 26 | private ServerSocketChannel serverSocketChannel; 27 | private Selector selector; 28 | private ExecutorService threadPool; 29 | 30 | public MiniCatBootstrapNioThreadSocket() { 31 | this.port = 8080; 32 | this.threadPool = Executors.newFixedThreadPool(10); // 10个线程的线程池 33 | } 34 | 35 | public void start() { 36 | logger.info("[MiniCat] start listen on port {}", port); 37 | logger.info("[MiniCat] visit url http://{}:{}", "127.0.0.1", port); 38 | 39 | try { 40 | serverSocketChannel = ServerSocketChannel.open(); 41 | serverSocketChannel.bind(new InetSocketAddress(port)); 42 | serverSocketChannel.configureBlocking(false); 43 | 44 | selector = Selector.open(); 45 | serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); 46 | 47 | while (true) { 48 | int readyChannels = selector.select(); 49 | if (readyChannels == 0) { 50 | continue; 51 | } 52 | 53 | Set selectedKeys = selector.selectedKeys(); 54 | Iterator keyIterator = selectedKeys.iterator(); 55 | 56 | while (keyIterator.hasNext()) { 57 | SelectionKey key = keyIterator.next(); 58 | 59 | if (key.isAcceptable()) { 60 | handleAccept(key); 61 | } else if (key.isReadable()) { 62 | handleRead(key); 63 | } 64 | 65 | keyIterator.remove(); 66 | } 67 | } 68 | } catch (IOException e) { 69 | logger.error("[MiniCat] start meet ex", e); 70 | throw new MiniCatException(e); 71 | } 72 | } 73 | 74 | private void handleAccept(SelectionKey key) throws IOException { 75 | ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel(); 76 | SocketChannel socketChannel = serverSocketChannel.accept(); 77 | socketChannel.configureBlocking(false); 78 | socketChannel.register(selector, SelectionKey.OP_READ); 79 | } 80 | 81 | private void handleRead(SelectionKey key) throws IOException { 82 | threadPool.execute(() -> { 83 | try { 84 | SocketChannel socketChannel = (SocketChannel) key.channel(); 85 | ByteBuffer buffer = ByteBuffer.allocate(1024); 86 | StringBuilder requestBuilder = new StringBuilder(); 87 | 88 | int bytesRead = socketChannel.read(buffer); 89 | while (bytesRead > 0) { 90 | buffer.flip(); 91 | while (buffer.hasRemaining()) { 92 | requestBuilder.append((char) buffer.get()); 93 | } 94 | buffer.clear(); 95 | bytesRead = socketChannel.read(buffer); 96 | } 97 | 98 | String requestString = requestBuilder.toString(); 99 | logger.info("read requestString={}", requestString); 100 | 101 | TimeUnit.SECONDS.sleep(5); // 模拟耗时操作 102 | writeToClient(socketChannel, requestString); 103 | logger.info("writeToClient done"); 104 | socketChannel.close(); 105 | } catch (InterruptedException | IOException e) { 106 | logger.error("[MiniCat] error processing request", e); 107 | } 108 | }); 109 | } 110 | 111 | private void writeToClient(SocketChannel socketChannel, String requestString) throws IOException { 112 | String httpText = InnerHttpUtil.http200Resp("ECHO: \r\n" + requestString); 113 | ByteBuffer buffer = ByteBuffer.wrap(httpText.getBytes("UTF-8")); 114 | socketChannel.write(buffer); 115 | } 116 | 117 | public void shutdown() { 118 | try { 119 | threadPool.shutdown(); 120 | threadPool.awaitTermination(5, TimeUnit.SECONDS); 121 | } catch (InterruptedException e) { 122 | logger.error("[MiniCat] error shutting down thread pool", e); 123 | Thread.currentThread().interrupt(); 124 | } finally { 125 | try { 126 | selector.close(); 127 | serverSocketChannel.close(); 128 | } catch (IOException e) { 129 | logger.error("[MiniCat] error closing server socket", e); 130 | } 131 | } 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/bs/servlet/MiniCatNettyServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.bs.servlet; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | import com.github.houbb.minicat.util.InnerHttpUtil; 6 | import io.netty.buffer.ByteBuf; 7 | import io.netty.buffer.Unpooled; 8 | import io.netty.channel.ChannelFutureListener; 9 | import io.netty.channel.ChannelHandlerContext; 10 | import io.netty.channel.ChannelInboundHandlerAdapter; 11 | 12 | import java.nio.charset.Charset; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | class MiniCatNettyServerHandler extends ChannelInboundHandlerAdapter { 16 | 17 | private static final Log logger = LogFactory.getLog(MiniCatNettyServerHandler.class); 18 | 19 | @Override 20 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 21 | ByteBuf buf = (ByteBuf) msg; 22 | byte[] bytes = new byte[buf.readableBytes()]; 23 | buf.readBytes(bytes); 24 | String requestString = new String(bytes, Charset.defaultCharset()); 25 | logger.info("channelRead requestString={}", requestString); 26 | 27 | // Simulating some processing time 28 | try { 29 | TimeUnit.SECONDS.sleep(5); 30 | } catch (InterruptedException e) { 31 | throw new RuntimeException(e); 32 | } 33 | 34 | String respText = InnerHttpUtil.http200Resp("ECHO: \r\n" + requestString);; 35 | ByteBuf responseBuf = Unpooled.copiedBuffer(respText.getBytes()); 36 | ctx.writeAndFlush(responseBuf) 37 | .addListener(ChannelFutureListener.CLOSE); // Close the channel after sending the response 38 | logger.info("channelRead writeAndFlush DONE"); 39 | } 40 | 41 | @Override 42 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 43 | ctx.flush(); 44 | } 45 | 46 | @Override 47 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 48 | logger.error("exceptionCaught", cause); 49 | ctx.close(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/constant/HttpMethodType.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.constant; 2 | 3 | /** 4 | * 方法类别 5 | * 6 | * @since 0.3.0 7 | */ 8 | public enum HttpMethodType { 9 | GET("GET", "GET 请求"), 10 | POST("POST", "POST 请求"), 11 | HEAD("HEAD", "HEAD 请求"), 12 | PUT("PUT", "PUT 请求"), 13 | DELETE("DELETE", "DELETE 请求"), 14 | OPTIONS("OPTIONS", "OPTIONS 请求"), 15 | TRACE("TRACE", "TRACE 请求"), 16 | CONNECT("CONNECT", "CONNECT 请求"), 17 | ; 18 | 19 | private final String code; 20 | private final String desc; 21 | 22 | HttpMethodType(String code, String desc) { 23 | this.code = code; 24 | this.desc = desc; 25 | } 26 | 27 | public String getCode() { 28 | return code; 29 | } 30 | 31 | public String getDesc() { 32 | return desc; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/dto/IMiniCatRequest.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.dto; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | 5 | /** 6 | * @since 0.4.0 7 | */ 8 | public interface IMiniCatRequest extends HttpServletRequest { 9 | 10 | /** 11 | * 获取请求地址 12 | * @return 请求地址 13 | */ 14 | String getUrl(); 15 | 16 | /** 17 | * 获取方法 18 | * @return 请求方法 19 | */ 20 | String getMethod(); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/dto/IMiniCatResponse.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.dto; 2 | 3 | import javax.servlet.http.HttpServletResponse; 4 | 5 | /** 6 | * @since 0.4.0 7 | */ 8 | public interface IMiniCatResponse extends HttpServletResponse { 9 | 10 | /** 11 | * 写入结果 12 | * @param text 文本 13 | * @param charset 编码 14 | */ 15 | void write(String text, String charset); 16 | 17 | 18 | /** 19 | * 写入结果 20 | * @param text 文本 21 | */ 22 | default void write(String text) { 23 | this.write(text, "UTF-8"); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/dto/MiniCatRequestAdaptor.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.dto; 2 | 3 | import com.github.houbb.minicat.support.attr.DefaultMiniCatAttrManager; 4 | import com.github.houbb.minicat.support.attr.IMiniCatAttrManager; 5 | 6 | import javax.servlet.*; 7 | import javax.servlet.http.*; 8 | import java.io.BufferedReader; 9 | import java.io.IOException; 10 | import java.io.UnsupportedEncodingException; 11 | import java.security.Principal; 12 | import java.util.*; 13 | 14 | /** 15 | * 请求适配器 16 | * @since 0.3.0 17 | */ 18 | public class MiniCatRequestAdaptor implements IMiniCatRequest { 19 | 20 | private IMiniCatAttrManager attrManager = new DefaultMiniCatAttrManager(); 21 | 22 | private List servletRequestAttributeListeners = new ArrayList<>(); 23 | 24 | public IMiniCatAttrManager getAttrManager() { 25 | return attrManager; 26 | } 27 | 28 | public void setAttrManager(IMiniCatAttrManager attrManager) { 29 | this.attrManager = attrManager; 30 | } 31 | 32 | public List getServletRequestAttributeListeners() { 33 | return servletRequestAttributeListeners; 34 | } 35 | 36 | public void setServletRequestAttributeListeners(List servletRequestAttributeListeners) { 37 | this.servletRequestAttributeListeners = servletRequestAttributeListeners; 38 | } 39 | 40 | @Override 41 | public String getAuthType() { 42 | return null; 43 | } 44 | 45 | @Override 46 | public Cookie[] getCookies() { 47 | return new Cookie[0]; 48 | } 49 | 50 | @Override 51 | public long getDateHeader(String name) { 52 | return 0; 53 | } 54 | 55 | @Override 56 | public String getHeader(String name) { 57 | return null; 58 | } 59 | 60 | @Override 61 | public Enumeration getHeaders(String name) { 62 | return null; 63 | } 64 | 65 | @Override 66 | public Enumeration getHeaderNames() { 67 | return null; 68 | } 69 | 70 | @Override 71 | public int getIntHeader(String name) { 72 | return 0; 73 | } 74 | 75 | @Override 76 | public String getUrl() { 77 | return null; 78 | } 79 | 80 | @Override 81 | public String getMethod() { 82 | return null; 83 | } 84 | 85 | @Override 86 | public String getPathInfo() { 87 | return null; 88 | } 89 | 90 | @Override 91 | public String getPathTranslated() { 92 | return null; 93 | } 94 | 95 | @Override 96 | public String getContextPath() { 97 | return null; 98 | } 99 | 100 | @Override 101 | public String getQueryString() { 102 | return null; 103 | } 104 | 105 | @Override 106 | public String getRemoteUser() { 107 | return null; 108 | } 109 | 110 | @Override 111 | public boolean isUserInRole(String role) { 112 | return false; 113 | } 114 | 115 | @Override 116 | public Principal getUserPrincipal() { 117 | return null; 118 | } 119 | 120 | @Override 121 | public String getRequestedSessionId() { 122 | return null; 123 | } 124 | 125 | @Override 126 | public String getRequestURI() { 127 | return null; 128 | } 129 | 130 | @Override 131 | public StringBuffer getRequestURL() { 132 | return null; 133 | } 134 | 135 | @Override 136 | public String getServletPath() { 137 | return null; 138 | } 139 | 140 | @Override 141 | public HttpSession getSession(boolean create) { 142 | return null; 143 | } 144 | 145 | @Override 146 | public HttpSession getSession() { 147 | return null; 148 | } 149 | 150 | @Override 151 | public String changeSessionId() { 152 | return null; 153 | } 154 | 155 | @Override 156 | public boolean isRequestedSessionIdValid() { 157 | return false; 158 | } 159 | 160 | @Override 161 | public boolean isRequestedSessionIdFromCookie() { 162 | return false; 163 | } 164 | 165 | @Override 166 | public boolean isRequestedSessionIdFromURL() { 167 | return false; 168 | } 169 | 170 | @Override 171 | public boolean isRequestedSessionIdFromUrl() { 172 | return false; 173 | } 174 | 175 | @Override 176 | public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { 177 | return false; 178 | } 179 | 180 | @Override 181 | public void login(String username, String password) throws ServletException { 182 | 183 | } 184 | 185 | @Override 186 | public void logout() throws ServletException { 187 | 188 | } 189 | 190 | @Override 191 | public Collection getParts() throws IOException, ServletException { 192 | return null; 193 | } 194 | 195 | @Override 196 | public Part getPart(String name) throws IOException, ServletException { 197 | return null; 198 | } 199 | 200 | @Override 201 | public T upgrade(Class handlerClass) throws IOException, ServletException { 202 | return null; 203 | } 204 | 205 | @Override 206 | public Object getAttribute(String name) { 207 | return attrManager.getAttribute(name); 208 | } 209 | 210 | @Override 211 | public Enumeration getAttributeNames() { 212 | return attrManager.getAttributeNames(); 213 | } 214 | 215 | @Override 216 | public String getCharacterEncoding() { 217 | return null; 218 | } 219 | 220 | @Override 221 | public void setCharacterEncoding(String env) throws UnsupportedEncodingException { 222 | 223 | } 224 | 225 | @Override 226 | public int getContentLength() { 227 | return 0; 228 | } 229 | 230 | @Override 231 | public long getContentLengthLong() { 232 | return 0; 233 | } 234 | 235 | @Override 236 | public String getContentType() { 237 | return null; 238 | } 239 | 240 | @Override 241 | public ServletInputStream getInputStream() throws IOException { 242 | return null; 243 | } 244 | 245 | @Override 246 | public String getParameter(String name) { 247 | return null; 248 | } 249 | 250 | @Override 251 | public Enumeration getParameterNames() { 252 | return null; 253 | } 254 | 255 | @Override 256 | public String[] getParameterValues(String name) { 257 | return new String[0]; 258 | } 259 | 260 | @Override 261 | public Map getParameterMap() { 262 | return null; 263 | } 264 | 265 | @Override 266 | public String getProtocol() { 267 | return null; 268 | } 269 | 270 | @Override 271 | public String getScheme() { 272 | return null; 273 | } 274 | 275 | @Override 276 | public String getServerName() { 277 | return null; 278 | } 279 | 280 | @Override 281 | public int getServerPort() { 282 | return 0; 283 | } 284 | 285 | @Override 286 | public BufferedReader getReader() throws IOException { 287 | return null; 288 | } 289 | 290 | @Override 291 | public String getRemoteAddr() { 292 | return null; 293 | } 294 | 295 | @Override 296 | public String getRemoteHost() { 297 | return null; 298 | } 299 | 300 | @Override 301 | public void setAttribute(String name, Object o) { 302 | attrManager.setAttribute(name, o); 303 | } 304 | 305 | @Override 306 | public void removeAttribute(String name) { 307 | attrManager.removeAttribute(name); 308 | } 309 | 310 | @Override 311 | public Locale getLocale() { 312 | return null; 313 | } 314 | 315 | @Override 316 | public Enumeration getLocales() { 317 | return null; 318 | } 319 | 320 | @Override 321 | public boolean isSecure() { 322 | return false; 323 | } 324 | 325 | @Override 326 | public RequestDispatcher getRequestDispatcher(String path) { 327 | return null; 328 | } 329 | 330 | @Override 331 | public String getRealPath(String path) { 332 | return null; 333 | } 334 | 335 | @Override 336 | public int getRemotePort() { 337 | return 0; 338 | } 339 | 340 | @Override 341 | public String getLocalName() { 342 | return null; 343 | } 344 | 345 | @Override 346 | public String getLocalAddr() { 347 | return null; 348 | } 349 | 350 | @Override 351 | public int getLocalPort() { 352 | return 0; 353 | } 354 | 355 | @Override 356 | public ServletContext getServletContext() { 357 | return null; 358 | } 359 | 360 | @Override 361 | public AsyncContext startAsync() throws IllegalStateException { 362 | return null; 363 | } 364 | 365 | @Override 366 | public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException { 367 | return null; 368 | } 369 | 370 | @Override 371 | public boolean isAsyncStarted() { 372 | return false; 373 | } 374 | 375 | @Override 376 | public boolean isAsyncSupported() { 377 | return false; 378 | } 379 | 380 | @Override 381 | public AsyncContext getAsyncContext() { 382 | return null; 383 | } 384 | 385 | @Override 386 | public DispatcherType getDispatcherType() { 387 | return null; 388 | } 389 | 390 | 391 | } 392 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/dto/MiniCatRequestBio.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.dto; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | 6 | import java.io.BufferedReader; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.InputStreamReader; 10 | 11 | /** 12 | * @since 0.2.0 13 | */ 14 | public class MiniCatRequestBio extends MiniCatRequestAdaptor { 15 | 16 | private static final Log logger = LogFactory.getLog(MiniCatRequestBio.class); 17 | 18 | /** 19 | * 请求方式 例如:GET/POST 20 | */ 21 | private String method; 22 | 23 | 24 | /** 25 | * / , /index.html 26 | */ 27 | 28 | private String url; 29 | 30 | 31 | /** 32 | * 其他的属性都是通过inputStream解析出来的。 33 | */ 34 | 35 | private final InputStream inputStream; 36 | 37 | public MiniCatRequestBio(InputStream inputStream) { 38 | this.inputStream = inputStream; 39 | 40 | this.readFromStream(); 41 | } 42 | 43 | private void readFromStream() { 44 | logger.info("[MiniCat] start readFrom stream id={}", inputStream); 45 | // 使用BufferedReader来读取输入流 46 | // 这个如果使用 TRW,会导致 socket 也被关闭。 47 | 48 | BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); 49 | try { 50 | String line; 51 | // 读取第一行,即HTTP请求行 52 | if ((line = reader.readLine()) != null && !line.isEmpty()) { 53 | // 获取第一行数据 54 | String[] strings = line.split(" "); 55 | this.method = strings[0]; 56 | this.url = strings[1]; 57 | } else { 58 | logger.info("[MiniCat] No HTTP request line found, ignoring."); 59 | } 60 | } catch (IOException e) { 61 | logger.error("[MiniCat] readFromStream meet ex", e); 62 | throw new RuntimeException(e); 63 | } finally { 64 | logger.info("[MiniCat] end readFrom stream"); 65 | } 66 | } 67 | 68 | /** 69 | * 直接根据 available 有时候读取不到数据 70 | * @since 0.3.0 71 | */ 72 | private void readFromStreamByBufferMock() { 73 | this.method = "get"; 74 | this.url = "/my"; 75 | } 76 | 77 | /** 78 | * 直接根据 available 有时候读取不到数据 79 | * 80 | * 后来测试发现这个会阻塞。 81 | * 82 | * @since 0.3.0 83 | */ 84 | @Deprecated 85 | private void readFromStreamByBufferBlocking() { 86 | byte[] buffer = new byte[1024]; // 使用固定大小的缓冲区 87 | int bytesRead = 0; 88 | 89 | try { 90 | while ((bytesRead = inputStream.read(buffer)) != -1) { // 循环读取数据直到EOF 91 | String inputStr = new String(buffer, 0, bytesRead); 92 | 93 | // 检查是否读取到完整的HTTP请求行 94 | if (inputStr.contains("\n")) { 95 | // 获取第一行数据 96 | String firstLineStr = inputStr.split("\\n")[0]; 97 | String[] strings = firstLineStr.split(" "); 98 | this.method = strings[0]; 99 | this.url = strings[1]; 100 | 101 | logger.info("[MiniCat] method={}, url={}", method, url); 102 | break; // 退出循环,因为我们已经读取到请求行 103 | } 104 | } 105 | 106 | if ("".equals(method)) { 107 | logger.info("[MiniCat] No HTTP request line found, ignoring."); 108 | // 可以选择抛出异常或者返回空请求对象 109 | } 110 | } catch (IOException e) { 111 | logger.error("[MiniCat] readFromStream meet ex", e); 112 | throw new RuntimeException(e); 113 | } 114 | } 115 | 116 | 117 | /** 118 | * 您遇到的问题是在某些情况下,`inputStream.available()` 返回的 `count` 为 0,导致后续的 `read` 方法调用无法读取任何数据。 119 | * 120 | * 这个问题可能与网络流的特性和 `available()` 方法的实现有关。 121 | * 122 | * 根据搜索结果【3】,网络流(如 Socket 流)与文件流不同,网络流的 `available()` 方法可能返回 0,即使实际上有数据可读。这是因为网络通讯是间断性的,数据可能分多个批次到达。 123 | * 124 | * 例如,对方发送的字节长度为100的数据,本地程序调用 `available()` 方法有时得到0,有时得到50,有时能得到100,大多数情况下是0。 125 | * 126 | * 这是因为网络传输层可能会将较长的数据包进行分割,而且数据的到达可能存在延迟。 127 | * 128 | * 为了解决这个问题,您可以采取以下措施: 129 | * 130 | * 1. **避免直接依赖 `available()` 方法**:由于 `available()` 方法在网络流中可能不准确,您可以尝试不使用此方法来预分配字节数组。相反,您可以使用一个固定大小的缓冲区,或者使用 `read()` 方法的循环来动态读取数据。 131 | * 132 | * 2. **使用循环读取数据**:您可以在一个循环中使用 `read()` 方法逐字节读取数据,直到读取到换行符(表示 HTTP 请求行的结束)或其他特定的分隔符。这种方法不依赖于 `available()` 方法,可以确保即使数据分批到达也能正确读取。 133 | * 134 | * 3. **处理粘包和拆包问题**:由于 TCP 流式传输的特性,您可能会遇到所谓的“粘包”和“拆包”问题。您可以通过在协议中定义明确的分隔符或使用特定的编码方式来处理这些问题。 135 | * 136 | * 4. **调整缓冲区大小**:您可以尝试调整读取数据时使用的缓冲区大小。较大的缓冲区可能有助于减少因数据分批到达导致的 `available()` 方法返回 0 的情况。 137 | * 138 | * 5. **错误处理和重试机制**:在读取数据时,如果遇到 `available()` 返回 0 或其他错误情况,您可以实现错误处理和重试机制。例如,您可以设置一个重试次数,如果读取失败,则在等待一段时间后重试。 139 | * 140 | * 6. **优化日志记录**:在您的代码中,如果 `readResult` 为 0,则记录了一条日志并返回。这可能不是最佳实践,因为它可能导致有用的请求被忽略。您可以记录更详细的错误信息,并考虑在这种情况下采取不同的行动,例如重试读取或发送一个错误响应给客户端。 141 | * 142 | * 通过上述措施,您可以提高您的服务器代码的健壮性,确保即使在网络条件不稳定的情况下也能正确处理 HTTP 请求。 143 | */ 144 | @Deprecated 145 | private void readFromStreamLossData() { 146 | try { 147 | //从输入流中获取请求信息 148 | int count = inputStream.available(); 149 | byte[] bytes = new byte[count]; 150 | int readResult = inputStream.read(bytes); 151 | String inputsStr = new String(bytes); 152 | logger.info("[MiniCat] readCount={}, input stream {}", readResult, inputsStr); 153 | if(readResult <= 0) { 154 | logger.info("[MiniCat] readCount is empty, ignore handle."); 155 | return; 156 | } 157 | 158 | //获取第一行数据 159 | String firstLineStr = inputsStr.split("\\n")[0]; //GET / HTTP/1.1 160 | String[] strings = firstLineStr.split(" "); 161 | this.method = strings[0]; 162 | this.url = strings[1]; 163 | 164 | logger.info("[MiniCat] method={}, url={}", method, url); 165 | } catch (IOException e) { 166 | logger.error("[MiniCat] readFromStream meet ex", e); 167 | throw new RuntimeException(e); 168 | } 169 | } 170 | 171 | public String getMethod() { 172 | return method; 173 | } 174 | 175 | public void setMethod(String method) { 176 | this.method = method; 177 | } 178 | 179 | public String getUrl() { 180 | return url; 181 | } 182 | 183 | public void setUrl(String url) { 184 | this.url = url; 185 | } 186 | 187 | @Override 188 | public String toString() { 189 | return "MiniCatRequest{" + 190 | "method='" + method + '\'' + 191 | ", url='" + url + '\'' + 192 | ", inputStream=" + inputStream + 193 | "} " + super.toString(); 194 | } 195 | 196 | } 197 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/dto/MiniCatRequestCommon.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.dto; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | 9 | /** 10 | * @since 0.2.0 11 | */ 12 | public class MiniCatRequestCommon extends MiniCatRequestAdaptor { 13 | 14 | private static final Log logger = LogFactory.getLog(MiniCatRequestCommon.class); 15 | 16 | /** 17 | * 请求方式 例如:GET/POST 18 | */ 19 | private String method; 20 | 21 | 22 | /** 23 | * / , /index.html 24 | */ 25 | 26 | private String url; 27 | 28 | public MiniCatRequestCommon(String method, String url) { 29 | this.method = method; 30 | this.url = url; 31 | } 32 | 33 | @Override 34 | public String getMethod() { 35 | return method; 36 | } 37 | 38 | public void setMethod(String method) { 39 | this.method = method; 40 | } 41 | 42 | @Override 43 | public String getUrl() { 44 | return url; 45 | } 46 | 47 | public void setUrl(String url) { 48 | this.url = url; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/dto/MiniCatResponseAdaptor.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.dto; 2 | 3 | import javax.servlet.ServletOutputStream; 4 | import javax.servlet.http.Cookie; 5 | import javax.servlet.http.HttpServletResponse; 6 | import java.io.IOException; 7 | import java.io.PrintWriter; 8 | import java.util.Collection; 9 | import java.util.Locale; 10 | 11 | /** 12 | * @since 0.3.0 13 | */ 14 | public class MiniCatResponseAdaptor implements IMiniCatResponse { 15 | 16 | @Override 17 | public void addCookie(Cookie cookie) { 18 | 19 | } 20 | 21 | @Override 22 | public boolean containsHeader(String name) { 23 | return false; 24 | } 25 | 26 | @Override 27 | public String encodeURL(String url) { 28 | return null; 29 | } 30 | 31 | @Override 32 | public String encodeRedirectURL(String url) { 33 | return null; 34 | } 35 | 36 | @Override 37 | public String encodeUrl(String url) { 38 | return null; 39 | } 40 | 41 | @Override 42 | public String encodeRedirectUrl(String url) { 43 | return null; 44 | } 45 | 46 | @Override 47 | public void sendError(int sc, String msg) throws IOException { 48 | 49 | } 50 | 51 | @Override 52 | public void sendError(int sc) throws IOException { 53 | 54 | } 55 | 56 | @Override 57 | public void sendRedirect(String location) throws IOException { 58 | 59 | } 60 | 61 | @Override 62 | public void setDateHeader(String name, long date) { 63 | 64 | } 65 | 66 | @Override 67 | public void addDateHeader(String name, long date) { 68 | 69 | } 70 | 71 | @Override 72 | public void setHeader(String name, String value) { 73 | 74 | } 75 | 76 | @Override 77 | public void addHeader(String name, String value) { 78 | 79 | } 80 | 81 | @Override 82 | public void setIntHeader(String name, int value) { 83 | 84 | } 85 | 86 | @Override 87 | public void addIntHeader(String name, int value) { 88 | 89 | } 90 | 91 | @Override 92 | public void setStatus(int sc) { 93 | 94 | } 95 | 96 | @Override 97 | public void setStatus(int sc, String sm) { 98 | 99 | } 100 | 101 | @Override 102 | public int getStatus() { 103 | return 0; 104 | } 105 | 106 | @Override 107 | public String getHeader(String name) { 108 | return null; 109 | } 110 | 111 | @Override 112 | public Collection getHeaders(String name) { 113 | return null; 114 | } 115 | 116 | @Override 117 | public Collection getHeaderNames() { 118 | return null; 119 | } 120 | 121 | @Override 122 | public String getCharacterEncoding() { 123 | return null; 124 | } 125 | 126 | @Override 127 | public String getContentType() { 128 | return null; 129 | } 130 | 131 | @Override 132 | public ServletOutputStream getOutputStream() throws IOException { 133 | return null; 134 | } 135 | 136 | @Override 137 | public PrintWriter getWriter() throws IOException { 138 | return null; 139 | } 140 | 141 | @Override 142 | public void setCharacterEncoding(String charset) { 143 | 144 | } 145 | 146 | @Override 147 | public void setContentLength(int len) { 148 | 149 | } 150 | 151 | @Override 152 | public void setContentLengthLong(long len) { 153 | 154 | } 155 | 156 | @Override 157 | public void setContentType(String type) { 158 | 159 | } 160 | 161 | @Override 162 | public void setBufferSize(int size) { 163 | 164 | } 165 | 166 | @Override 167 | public int getBufferSize() { 168 | return 0; 169 | } 170 | 171 | @Override 172 | public void flushBuffer() throws IOException { 173 | 174 | } 175 | 176 | @Override 177 | public void resetBuffer() { 178 | 179 | } 180 | 181 | @Override 182 | public boolean isCommitted() { 183 | return false; 184 | } 185 | 186 | @Override 187 | public void reset() { 188 | 189 | } 190 | 191 | @Override 192 | public void setLocale(Locale loc) { 193 | 194 | } 195 | 196 | @Override 197 | public Locale getLocale() { 198 | return null; 199 | } 200 | 201 | @Override 202 | public void write(String text, String charset) { 203 | 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/dto/MiniCatResponseBio.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.dto; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | import com.github.houbb.minicat.exception.MiniCatException; 6 | 7 | import java.io.IOException; 8 | import java.io.OutputStream; 9 | import java.io.PrintWriter; 10 | 11 | /** 12 | * @since 0.2.0 13 | */ 14 | public class MiniCatResponseBio extends MiniCatResponseAdaptor { 15 | 16 | private static final Log logger = LogFactory.getLog(MiniCatResponseBio.class); 17 | 18 | private final OutputStream outputStream; 19 | 20 | public MiniCatResponseBio(OutputStream outputStream) { 21 | this.outputStream = outputStream; 22 | } 23 | 24 | @Override 25 | public PrintWriter getWriter() throws IOException { 26 | // 重载输出实现 27 | return new PrintWriter(this.outputStream); 28 | } 29 | 30 | public void write(String text, String charset) { 31 | try { 32 | outputStream.write(text.getBytes(charset)); 33 | } catch (IOException e) { 34 | logger.error("[MiniCat] write failed text={}, charset={}", text, charset, e); 35 | throw new MiniCatException(e); 36 | } 37 | } 38 | 39 | public void write(String text) { 40 | write(text, "UTF-8"); 41 | } 42 | 43 | public void write(byte[] bytes) { 44 | try { 45 | outputStream.write(bytes); 46 | } catch (IOException e) { 47 | throw new MiniCatException(e); 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/dto/MiniCatResponseCommon.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.dto; 2 | 3 | /** 4 | * @since 0.4.0 5 | */ 6 | public abstract class MiniCatResponseCommon extends MiniCatResponseAdaptor { 7 | 8 | public abstract void write(String text, String charset); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/exception/MiniCatException.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.exception; 2 | 3 | public class MiniCatException extends RuntimeException { 4 | 5 | public MiniCatException() { 6 | } 7 | 8 | public MiniCatException(String message) { 9 | super(message); 10 | } 11 | 12 | public MiniCatException(String message, Throwable cause) { 13 | super(message, cause); 14 | } 15 | 16 | public MiniCatException(Throwable cause) { 17 | super(cause); 18 | } 19 | 20 | public MiniCatException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 21 | super(message, cause, enableSuppression, writableStackTrace); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/package-info.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat; -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/attr/DefaultMiniCatAttrManager.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.attr; 2 | 3 | import com.github.houbb.heaven.annotation.CommonEager; 4 | 5 | import java.util.*; 6 | 7 | /** 8 | * @since 0.7.0 9 | */ 10 | public class DefaultMiniCatAttrManager implements IMiniCatAttrManager { 11 | 12 | private final Map objectMap = new HashMap<>(); 13 | 14 | @Override 15 | public Object getAttribute(String key) { 16 | return objectMap.get(key); 17 | } 18 | 19 | @Override 20 | public Enumeration getAttributeNames() { 21 | return setToEnumeration(objectMap.keySet()); 22 | } 23 | 24 | @Override 25 | public void setAttribute(String key, Object object) { 26 | objectMap.put(key, object); 27 | } 28 | 29 | @Override 30 | public Object removeAttribute(String key) { 31 | return objectMap.remove(key); 32 | } 33 | 34 | @CommonEager 35 | public static Enumeration setToEnumeration(Set set) { 36 | final Iterator iterator = set.iterator(); 37 | return new Enumeration() { 38 | @Override 39 | public boolean hasMoreElements() { 40 | return iterator.hasNext(); 41 | } 42 | 43 | @Override 44 | public T nextElement() { 45 | return iterator.next(); 46 | } 47 | }; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/attr/IMiniCatAttrManager.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.attr; 2 | 3 | import java.util.Enumeration; 4 | 5 | /** 6 | * 属性管理类 7 | * 8 | * @since 0.7.0 9 | */ 10 | public interface IMiniCatAttrManager { 11 | 12 | 13 | Object getAttribute(String key); 14 | 15 | /** 16 | * Returns an Enumeration containing the 17 | * names of the attributes available to this request. 18 | * This method returns an empty Enumeration 19 | * if the request has no attributes available to it. 20 | * 21 | * @return an Enumeration of strings containing the names 22 | * of the request's attributes 23 | */ 24 | Enumeration getAttributeNames(); 25 | 26 | void setAttribute(String key, Object object); 27 | 28 | Object removeAttribute(String key); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/classloader/WebAppClassLoader.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.classloader; 2 | 3 | 4 | import java.io.IOException; 5 | import java.io.UncheckedIOException; 6 | import java.net.URI; 7 | import java.net.URL; 8 | import java.net.URLClassLoader; 9 | import java.nio.file.Files; 10 | import java.nio.file.Path; 11 | import java.nio.file.Paths; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | /** 16 | * https://www.liaoxuefeng.com/wiki/1545956031987744/1545956487069728 17 | * 18 | * 每一个 dir 的 classLoader 独立。 19 | */ 20 | public class WebAppClassLoader extends URLClassLoader { 21 | 22 | private Path classPath; 23 | private Path[] libJars; 24 | 25 | public WebAppClassLoader(Path classPath, Path libPath) throws IOException { 26 | super(createUrls(classPath, libPath), ClassLoader.getSystemClassLoader()); 27 | // super("WebAppClassLoader", createUrls(classPath, libPath), ClassLoader.getSystemClassLoader()); 28 | // 29 | this.classPath = classPath.toAbsolutePath().normalize(); 30 | if(libPath.toFile().exists()) { 31 | this.libJars = Files.list(libPath).filter(p -> p.toString().endsWith(".jar")).map(p -> p.toAbsolutePath().normalize()).sorted().toArray(Path[]::new); 32 | } 33 | } 34 | 35 | static URL[] createUrls(Path classPath, Path libPath) throws IOException { 36 | List urls = new ArrayList<>(); 37 | urls.add(toDirURL(classPath)); 38 | 39 | //lib 可能不存在 40 | if(libPath.toFile().exists()) { 41 | Files.list(libPath).filter(p -> p.toString().endsWith(".jar")).sorted().forEach(p -> { 42 | urls.add(toJarURL(p)); 43 | }); 44 | } 45 | 46 | return urls.toArray(new URL[0]); 47 | } 48 | 49 | static URL toDirURL(Path p) { 50 | try { 51 | if (Files.isDirectory(p)) { 52 | String abs = toAbsPath(p); 53 | if (!abs.endsWith("/")) { 54 | abs = abs + "/"; 55 | } 56 | return URI.create("file://" + abs).toURL(); 57 | } 58 | throw new IOException("Path is not a directory: " + p); 59 | } catch (IOException e) { 60 | throw new UncheckedIOException(e); 61 | } 62 | } 63 | 64 | //D:\github\minicat\src\test\webapps\servlet\WEB-INF\classes 65 | //D:\github\minicat\src\test\webapps\WEB-INF\classes 66 | 67 | static URL toJarURL(Path p) { 68 | try { 69 | if (Files.isRegularFile(p)) { 70 | String abs = toAbsPath(p); 71 | return URI.create("file://" + abs).toURL(); 72 | } 73 | throw new IOException("Path is not a jar file: " + p); 74 | } catch (IOException e) { 75 | throw new UncheckedIOException(e); 76 | } 77 | } 78 | 79 | static String toAbsPath(Path p) throws IOException { 80 | return p.toAbsolutePath().normalize().toString().replace('\\', '/'); 81 | } 82 | 83 | public static void main(String[] args) throws Exception { 84 | String path ="D:\\github\\minicat\\src\\test\\webapps\\servlet\\WEB-INF\\classes"; 85 | String path2 ="D:\\github\\minicat\\src\\test\\webapps\\servlet\\WEB-INF\\lib"; 86 | WebAppClassLoader webAppClassLoader = new WebAppClassLoader(Paths.get(path), Paths.get(path2)); 87 | 88 | Class clazz = webAppClassLoader.loadClass("com.github.houbb.servlet.webxml.IndexServlet"); 89 | System.out.println(clazz.getMethods().length); 90 | // HttpServlet servlet = (HttpServlet) webAppClassLoader.createInstance("com.github.houbb.servlet.webxml.IndexServlet"); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/context/DefaultMiniCatServletContext.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.context; 2 | 3 | import javax.servlet.*; 4 | import javax.servlet.descriptor.JspConfigDescriptor; 5 | import java.io.InputStream; 6 | import java.net.MalformedURLException; 7 | import java.net.URL; 8 | import java.util.Enumeration; 9 | import java.util.EventListener; 10 | import java.util.Map; 11 | import java.util.Set; 12 | 13 | /** 14 | * TODO: 这个类的信息完善 15 | * 16 | */ 17 | public class DefaultMiniCatServletContext implements ServletContext { 18 | 19 | @Override 20 | public String getContextPath() { 21 | return null; 22 | } 23 | 24 | @Override 25 | public ServletContext getContext(String uripath) { 26 | return null; 27 | } 28 | 29 | @Override 30 | public int getMajorVersion() { 31 | return 0; 32 | } 33 | 34 | @Override 35 | public int getMinorVersion() { 36 | return 0; 37 | } 38 | 39 | @Override 40 | public int getEffectiveMajorVersion() { 41 | return 0; 42 | } 43 | 44 | @Override 45 | public int getEffectiveMinorVersion() { 46 | return 0; 47 | } 48 | 49 | @Override 50 | public String getMimeType(String file) { 51 | return null; 52 | } 53 | 54 | @Override 55 | public Set getResourcePaths(String path) { 56 | return null; 57 | } 58 | 59 | @Override 60 | public URL getResource(String path) throws MalformedURLException { 61 | return null; 62 | } 63 | 64 | @Override 65 | public InputStream getResourceAsStream(String path) { 66 | return null; 67 | } 68 | 69 | @Override 70 | public RequestDispatcher getRequestDispatcher(String path) { 71 | return null; 72 | } 73 | 74 | @Override 75 | public RequestDispatcher getNamedDispatcher(String name) { 76 | return null; 77 | } 78 | 79 | @Override 80 | public Servlet getServlet(String name) throws ServletException { 81 | return null; 82 | } 83 | 84 | @Override 85 | public Enumeration getServlets() { 86 | return null; 87 | } 88 | 89 | @Override 90 | public Enumeration getServletNames() { 91 | return null; 92 | } 93 | 94 | @Override 95 | public void log(String msg) { 96 | 97 | } 98 | 99 | @Override 100 | public void log(Exception exception, String msg) { 101 | 102 | } 103 | 104 | @Override 105 | public void log(String message, Throwable throwable) { 106 | 107 | } 108 | 109 | @Override 110 | public String getRealPath(String path) { 111 | return null; 112 | } 113 | 114 | @Override 115 | public String getServerInfo() { 116 | return null; 117 | } 118 | 119 | @Override 120 | public String getInitParameter(String name) { 121 | return null; 122 | } 123 | 124 | @Override 125 | public Enumeration getInitParameterNames() { 126 | return null; 127 | } 128 | 129 | @Override 130 | public boolean setInitParameter(String name, String value) { 131 | return false; 132 | } 133 | 134 | @Override 135 | public Object getAttribute(String name) { 136 | return null; 137 | } 138 | 139 | @Override 140 | public Enumeration getAttributeNames() { 141 | return null; 142 | } 143 | 144 | @Override 145 | public void setAttribute(String name, Object object) { 146 | } 147 | 148 | @Override 149 | public void removeAttribute(String name) { 150 | 151 | } 152 | 153 | @Override 154 | public String getServletContextName() { 155 | return null; 156 | } 157 | 158 | @Override 159 | public ServletRegistration.Dynamic addServlet(String servletName, String className) { 160 | return null; 161 | } 162 | 163 | @Override 164 | public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) { 165 | return null; 166 | } 167 | 168 | @Override 169 | public ServletRegistration.Dynamic addServlet(String servletName, Class servletClass) { 170 | return null; 171 | } 172 | 173 | @Override 174 | public ServletRegistration.Dynamic addJspFile(String servletName, String jspFile) { 175 | return null; 176 | } 177 | 178 | @Override 179 | public T createServlet(Class clazz) throws ServletException { 180 | return null; 181 | } 182 | 183 | @Override 184 | public ServletRegistration getServletRegistration(String servletName) { 185 | return null; 186 | } 187 | 188 | @Override 189 | public Map getServletRegistrations() { 190 | return null; 191 | } 192 | 193 | @Override 194 | public FilterRegistration.Dynamic addFilter(String filterName, String className) { 195 | return null; 196 | } 197 | 198 | @Override 199 | public FilterRegistration.Dynamic addFilter(String filterName, Filter filter) { 200 | return null; 201 | } 202 | 203 | @Override 204 | public FilterRegistration.Dynamic addFilter(String filterName, Class filterClass) { 205 | return null; 206 | } 207 | 208 | @Override 209 | public T createFilter(Class clazz) throws ServletException { 210 | return null; 211 | } 212 | 213 | @Override 214 | public FilterRegistration getFilterRegistration(String filterName) { 215 | return null; 216 | } 217 | 218 | @Override 219 | public Map getFilterRegistrations() { 220 | return null; 221 | } 222 | 223 | @Override 224 | public SessionCookieConfig getSessionCookieConfig() { 225 | return null; 226 | } 227 | 228 | @Override 229 | public void setSessionTrackingModes(Set sessionTrackingModes) { 230 | 231 | } 232 | 233 | @Override 234 | public Set getDefaultSessionTrackingModes() { 235 | return null; 236 | } 237 | 238 | @Override 239 | public Set getEffectiveSessionTrackingModes() { 240 | return null; 241 | } 242 | 243 | @Override 244 | public void addListener(String className) { 245 | 246 | } 247 | 248 | @Override 249 | public void addListener(T t) { 250 | 251 | } 252 | 253 | @Override 254 | public void addListener(Class listenerClass) { 255 | 256 | } 257 | 258 | @Override 259 | public T createListener(Class clazz) throws ServletException { 260 | return null; 261 | } 262 | 263 | @Override 264 | public JspConfigDescriptor getJspConfigDescriptor() { 265 | return null; 266 | } 267 | 268 | @Override 269 | public ClassLoader getClassLoader() { 270 | return null; 271 | } 272 | 273 | @Override 274 | public void declareRoles(String... roleNames) { 275 | 276 | } 277 | 278 | @Override 279 | public String getVirtualServerName() { 280 | return null; 281 | } 282 | 283 | @Override 284 | public int getSessionTimeout() { 285 | return 0; 286 | } 287 | 288 | @Override 289 | public void setSessionTimeout(int sessionTimeout) { 290 | 291 | } 292 | 293 | @Override 294 | public String getRequestCharacterEncoding() { 295 | return null; 296 | } 297 | 298 | @Override 299 | public void setRequestCharacterEncoding(String encoding) { 300 | 301 | } 302 | 303 | @Override 304 | public String getResponseCharacterEncoding() { 305 | return null; 306 | } 307 | 308 | @Override 309 | public void setResponseCharacterEncoding(String encoding) { 310 | 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/context/IMiniCatContextInit.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.context; 2 | 3 | public interface IMiniCatContextInit { 4 | 5 | /** 6 | * 初始化 7 | * @param config 配置 8 | */ 9 | void init(final MiniCatContextConfig config); 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/context/LocalMiniCatContextInit.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.context; 2 | 3 | import com.github.houbb.heaven.util.lang.StringUtil; 4 | import com.github.houbb.log.integration.core.Log; 5 | import com.github.houbb.log.integration.core.LogFactory; 6 | import com.github.houbb.minicat.bs.servlet.MiniCatBootstrapNetty; 7 | import com.github.houbb.minicat.exception.MiniCatException; 8 | import com.github.houbb.minicat.support.classloader.WebAppClassLoader; 9 | import com.github.houbb.minicat.support.filter.manager.IFilterManager; 10 | import com.github.houbb.minicat.support.servlet.manager.IServletManager; 11 | import com.github.houbb.minicat.support.war.IWarExtractor; 12 | import com.github.houbb.minicat.util.InnerResourceUtil; 13 | import org.dom4j.Document; 14 | import org.dom4j.Element; 15 | import org.dom4j.io.SAXReader; 16 | 17 | import javax.servlet.Filter; 18 | import javax.servlet.http.HttpServlet; 19 | import java.io.File; 20 | import java.nio.file.Path; 21 | import java.util.*; 22 | 23 | public class LocalMiniCatContextInit implements IMiniCatContextInit { 24 | 25 | private static final Log logger = LogFactory.getLog(LocalMiniCatContextInit.class); 26 | 27 | private final File webXmlFile; 28 | 29 | private final String urlPrefix; 30 | 31 | private MiniCatContextConfig miniCatContextConfig; 32 | 33 | public LocalMiniCatContextInit(File webXmlFile, String urlPrefix) { 34 | this.webXmlFile = webXmlFile; 35 | this.urlPrefix = urlPrefix; 36 | } 37 | 38 | public LocalMiniCatContextInit(File webXmlFile) { 39 | this(webXmlFile, ""); 40 | } 41 | 42 | public LocalMiniCatContextInit() { 43 | this(new File(InnerResourceUtil.buildFullPath(InnerResourceUtil.getClassLoaderResource(LocalMiniCatContextInit.class), 44 | "web.xml"))); 45 | } 46 | 47 | protected void beforeProcessWebXml() { 48 | final String baseDir = miniCatContextConfig.getBaseDir(); 49 | final IWarExtractor warExtractor = miniCatContextConfig.getWarExtractor(); 50 | 51 | logger.info("[MiniCat] beforeProcessWebXml start baseDir={}", miniCatContextConfig.getBaseDir()); 52 | 53 | //1. 加载解析所有的 war 包 54 | //2. 解压 war 包 55 | //3. 解析对应的 servlet 映射关系 56 | warExtractor.extract(baseDir); 57 | 58 | logger.info("[MiniCat] beforeProcessWebXml end"); 59 | } 60 | 61 | /** 62 | * 初始化 63 | * 64 | * @param config 配置 65 | */ 66 | public void init(final MiniCatContextConfig config) { 67 | this.miniCatContextConfig = config; 68 | 69 | beforeProcessWebXml(); 70 | 71 | // 解析 war 处理 72 | processWebXml(); 73 | } 74 | 75 | /** 76 | * 处理 web 文件 77 | */ 78 | protected void processWebXml() { 79 | try { 80 | SAXReader reader = new SAXReader(); 81 | Document document = reader.read(webXmlFile); 82 | 83 | Element root = document.getRootElement(); 84 | 85 | // 1. 处理 servlet 86 | final IServletManager servletManager = this.miniCatContextConfig.getServletManager(); 87 | processWebServlet(root, servletManager); 88 | 89 | //2. 处理 filter 90 | final IFilterManager filterManager = this.miniCatContextConfig.getFilterManager(); 91 | processWebFilter(root, filterManager); 92 | 93 | //3. 处理 listener 94 | initListener(root); 95 | } catch (Exception e) { 96 | throw new MiniCatException(e); 97 | } 98 | } 99 | 100 | /** 101 | * 初始化监听器 102 | * @since 0.7.0 103 | */ 104 | protected void initListener(Element root) { 105 | try { 106 | List filterElements = root.elements("listener"); 107 | List servletClassList = new ArrayList<>(); 108 | for (Element servletElement : filterElements) { 109 | String servletClass = servletElement.elementText("listener-class"); 110 | servletClassList.add(servletClass); 111 | } 112 | 113 | // 这个是以 web.xml 为主题。 114 | servletClassList.forEach(className -> { 115 | try { 116 | Class servletClazz = Class.forName(className); 117 | EventListener eventListener = (EventListener) servletClazz.newInstance(); 118 | miniCatContextConfig.getListenerManager().addEventListener(eventListener); 119 | } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { 120 | throw new MiniCatException(e); 121 | } 122 | }); 123 | } catch (Exception e) { 124 | throw new MiniCatException(e); 125 | } 126 | } 127 | 128 | protected void processWebFilter(Element root, final IFilterManager filterManager) { 129 | Map servletClassNameMap = new HashMap<>(); 130 | Map urlPatternMap = new HashMap<>(); 131 | List servletElements = root.elements("filter"); 132 | for (Element servletElement : servletElements) { 133 | String servletName = servletElement.elementText("filter-name"); 134 | String servletClass = servletElement.elementText("filter-class"); 135 | servletClassNameMap.put(servletName, servletClass); 136 | } 137 | List urlMappingElements = root.elements("filter-mapping"); 138 | for (Element urlElem : urlMappingElements) { 139 | String servletName = urlElem.elementText("filter-name"); 140 | String urlPattern = urlElem.elementText("url-pattern"); 141 | urlPatternMap.put(servletName, urlPattern); 142 | } 143 | handleFilterConfigMap(servletClassNameMap, urlPatternMap, filterManager); 144 | } 145 | 146 | protected void handleFilterConfigMap(Map filterClassNameMap, Map urlPatternMap, final IFilterManager filterManager) { 147 | try { 148 | for (Map.Entry urlPatternEntry : urlPatternMap.entrySet()) { 149 | String filterName = urlPatternEntry.getKey(); 150 | String urlPattern = urlPatternEntry.getValue(); 151 | 152 | String className = filterClassNameMap.get(filterName); 153 | if (StringUtil.isEmpty(className)) { 154 | throw new MiniCatException("className not found for filterName: " + filterName); 155 | } 156 | 157 | Class servletClazz = Class.forName(className); 158 | Filter httpServlet = (Filter) servletClazz.newInstance(); 159 | 160 | // 构建 161 | String fullUrlPattern = buildFullUrlPattern(urlPattern); 162 | filterManager.register(fullUrlPattern, httpServlet); 163 | } 164 | } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { 165 | throw new MiniCatException(e); 166 | } 167 | } 168 | 169 | 170 | protected String buildFullUrlPattern(String urlPattern) { 171 | return urlPrefix + urlPattern; 172 | } 173 | 174 | protected void processWebServlet(Element root, final IServletManager servletManager) { 175 | Map servletClassNameMap = new HashMap<>(); 176 | Map urlPatternMap = new HashMap<>(); 177 | List servletElements = root.elements("servlet"); 178 | for (Element servletElement : servletElements) { 179 | String servletName = servletElement.elementText("servlet-name"); 180 | String servletClass = servletElement.elementText("servlet-class"); 181 | servletClassNameMap.put(servletName, servletClass); 182 | } 183 | List urlMappingElements = root.elements("servlet-mapping"); 184 | for (Element urlElem : urlMappingElements) { 185 | String servletName = urlElem.elementText("servlet-name"); 186 | String urlPattern = urlElem.elementText("url-pattern"); 187 | urlPatternMap.put(servletName, urlPattern); 188 | } 189 | handleServletConfigMap(servletClassNameMap, urlPatternMap, servletManager); 190 | } 191 | 192 | protected void handleServletConfigMap(Map servletClassNameMap, Map urlPatternMap, final IServletManager servletManager) { 193 | try { 194 | for (Map.Entry urlPatternEntry : urlPatternMap.entrySet()) { 195 | String servletName = urlPatternEntry.getKey(); 196 | String urlPattern = urlPatternEntry.getValue(); 197 | 198 | String className = servletClassNameMap.get(servletName); 199 | if (StringUtil.isEmpty(className)) { 200 | throw new MiniCatException("className not found for servletName: " + servletName); 201 | } 202 | 203 | Class servletClazz = Class.forName(className); 204 | HttpServlet httpServlet = (HttpServlet) servletClazz.newInstance(); 205 | 206 | // 构建 207 | String fullUrlPattern = buildFullUrlPattern(urlPattern); 208 | servletManager.register(fullUrlPattern, httpServlet); 209 | } 210 | } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { 211 | throw new MiniCatException(e); 212 | } 213 | } 214 | 215 | 216 | } 217 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/context/MiniCatContextConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.context; 2 | 3 | import com.github.houbb.minicat.support.attr.IMiniCatAttrManager; 4 | import com.github.houbb.minicat.support.filter.manager.IFilterManager; 5 | import com.github.houbb.minicat.support.listener.IListenerManager; 6 | import com.github.houbb.minicat.support.request.IRequestDispatcher; 7 | import com.github.houbb.minicat.support.servlet.manager.IServletManager; 8 | import com.github.houbb.minicat.support.war.IWarExtractor; 9 | 10 | import javax.servlet.ServletContext; 11 | import javax.servlet.ServletContextAttributeEvent; 12 | import javax.servlet.ServletContextAttributeListener; 13 | import javax.servlet.ServletContextEvent; 14 | import java.util.Enumeration; 15 | import java.util.function.Consumer; 16 | 17 | /** 18 | * @since 0.6.0 19 | */ 20 | public class MiniCatContextConfig extends ServletContextEvent implements IMiniCatAttrManager { 21 | 22 | /** 23 | * 启动端口号 24 | */ 25 | private int port; 26 | 27 | /** 28 | * 默认文件夹 29 | * @since 0.5.0 30 | */ 31 | private String baseDir; 32 | 33 | /** 34 | * war 解压管理 35 | * 36 | * @since 0.5.0 37 | */ 38 | private IWarExtractor warExtractor; 39 | 40 | /** 41 | * servlet 管理 42 | * 43 | * @since 0.5.0 44 | */ 45 | private IServletManager servletManager; 46 | 47 | /** 48 | * 过滤管理器 49 | */ 50 | private IFilterManager filterManager; 51 | 52 | /** 53 | * 请求分发 54 | * @since 0.5.0 55 | */ 56 | private IRequestDispatcher requestDispatcher; 57 | 58 | /** 59 | * 监听器管理类 60 | * 61 | * @since 0.7.0 62 | */ 63 | private IListenerManager listenerManager; 64 | 65 | /** 66 | * 属性管理 67 | * @since 0.7.0 68 | */ 69 | private IMiniCatAttrManager miniCatAttrManager; 70 | 71 | private ServletContext servletContext; 72 | 73 | public IMiniCatAttrManager getMiniCatAttrManager() { 74 | return miniCatAttrManager; 75 | } 76 | 77 | public void setMiniCatAttrManager(IMiniCatAttrManager miniCatAttrManager) { 78 | this.miniCatAttrManager = miniCatAttrManager; 79 | } 80 | 81 | public IListenerManager getListenerManager() { 82 | return listenerManager; 83 | } 84 | 85 | public void setListenerManager(IListenerManager listenerManager) { 86 | this.listenerManager = listenerManager; 87 | } 88 | 89 | /** 90 | * Construct a ServletContextEvent from the given context. 91 | * 92 | * @param source - the ServletContext that is sending the event. 93 | */ 94 | public MiniCatContextConfig(ServletContext source) { 95 | super(source); 96 | this.servletContext = source; 97 | } 98 | 99 | /** 100 | * Construct a ServletContextEvent from the given context. 101 | * 102 | */ 103 | public MiniCatContextConfig() { 104 | this(new DefaultMiniCatServletContext()); 105 | } 106 | 107 | public IWarExtractor getWarExtractor() { 108 | return warExtractor; 109 | } 110 | 111 | public void setWarExtractor(IWarExtractor warExtractor) { 112 | this.warExtractor = warExtractor; 113 | } 114 | 115 | public int getPort() { 116 | return port; 117 | } 118 | 119 | public void setPort(int port) { 120 | this.port = port; 121 | } 122 | 123 | public String getBaseDir() { 124 | return baseDir; 125 | } 126 | 127 | public void setBaseDir(String baseDir) { 128 | this.baseDir = baseDir; 129 | } 130 | 131 | public IServletManager getServletManager() { 132 | return servletManager; 133 | } 134 | 135 | public void setServletManager(IServletManager servletManager) { 136 | this.servletManager = servletManager; 137 | } 138 | 139 | public IFilterManager getFilterManager() { 140 | return filterManager; 141 | } 142 | 143 | public void setFilterManager(IFilterManager filterManager) { 144 | this.filterManager = filterManager; 145 | } 146 | 147 | public IRequestDispatcher getRequestDispatcher() { 148 | return requestDispatcher; 149 | } 150 | 151 | public void setRequestDispatcher(IRequestDispatcher requestDispatcher) { 152 | this.requestDispatcher = requestDispatcher; 153 | } 154 | 155 | 156 | @Override 157 | public Object getAttribute(String key) { 158 | return miniCatAttrManager.getAttribute(key); 159 | } 160 | 161 | @Override 162 | public Enumeration getAttributeNames() { 163 | return miniCatAttrManager.getAttributeNames(); 164 | } 165 | 166 | @Override 167 | public void setAttribute(String key, Object object) { 168 | this.miniCatAttrManager.setAttribute(key, object); 169 | 170 | final ServletContextAttributeEvent servletContextAttributeEvent = new ServletContextAttributeEvent(servletContext, key, object); 171 | // 监听 172 | this.listenerManager.getServletContextAttributeListeners().forEach(new Consumer() { 173 | @Override 174 | public void accept(ServletContextAttributeListener servletContextAttributeListener) { 175 | servletContextAttributeListener.attributeAdded(servletContextAttributeEvent); 176 | } 177 | }); 178 | } 179 | 180 | @Override 181 | public Object removeAttribute(String key) { 182 | Object result = this.miniCatAttrManager.removeAttribute(key); 183 | 184 | final ServletContextAttributeEvent servletContextAttributeEvent = new ServletContextAttributeEvent(servletContext, key, result); 185 | // 监听 186 | this.listenerManager.getServletContextAttributeListeners().forEach(new Consumer() { 187 | @Override 188 | public void accept(ServletContextAttributeListener servletContextAttributeListener) { 189 | servletContextAttributeListener.attributeRemoved(servletContextAttributeEvent); 190 | } 191 | }); 192 | 193 | return result; 194 | } 195 | 196 | @Override 197 | public String toString() { 198 | return "MiniCatContextConfig{" + 199 | "port=" + port + 200 | ", baseDir='" + baseDir + '\'' + 201 | ", warExtractor=" + warExtractor + 202 | ", servletManager=" + servletManager + 203 | ", filterManager=" + filterManager + 204 | ", requestDispatcher=" + requestDispatcher + 205 | ", listenerManager=" + listenerManager + 206 | ", miniCatAttrManager=" + miniCatAttrManager + 207 | "} " + super.toString(); 208 | } 209 | 210 | } 211 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/context/WarsMiniCatContextInit.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.context; 2 | 3 | import com.github.houbb.heaven.util.lang.StringUtil; 4 | import com.github.houbb.log.integration.core.Log; 5 | import com.github.houbb.log.integration.core.LogFactory; 6 | import com.github.houbb.minicat.exception.MiniCatException; 7 | import com.github.houbb.minicat.support.classloader.WebAppClassLoader; 8 | import com.github.houbb.minicat.util.InnerResourceUtil; 9 | import org.dom4j.Document; 10 | import org.dom4j.DocumentException; 11 | import org.dom4j.Element; 12 | import org.dom4j.io.SAXReader; 13 | 14 | import javax.servlet.Filter; 15 | import javax.servlet.http.HttpServlet; 16 | import java.io.File; 17 | import java.nio.file.Path; 18 | import java.nio.file.Paths; 19 | import java.util.*; 20 | import java.util.function.Consumer; 21 | 22 | /** 23 | * 24 | * @since 0.6.0 25 | */ 26 | public class WarsMiniCatContextInit implements IMiniCatContextInit { 27 | 28 | private static final Log logger = LogFactory.getLog(WarsMiniCatContextInit.class); 29 | 30 | private final String baseWarDirStr; 31 | 32 | private MiniCatContextConfig miniCatContextConfig; 33 | 34 | public WarsMiniCatContextInit(String baseDirStr) { 35 | this.baseWarDirStr = baseDirStr; 36 | } 37 | 38 | /** 39 | * 初始化 40 | * 41 | * @param config 配置 42 | */ 43 | public void init(final MiniCatContextConfig config) { 44 | this.miniCatContextConfig = config; 45 | logger.info("[MiniCat] servlet init with baseWarDirStr={}, config={}", baseWarDirStr, config); 46 | 47 | // 分别解析 war 包 48 | File baseDir = new File(baseWarDirStr); 49 | 50 | File[] files = baseDir.listFiles(); 51 | for (File file : files) { 52 | if (file.isDirectory()) { 53 | handleWarPackage(file); 54 | } 55 | } 56 | } 57 | 58 | /** 59 | * 处理单个 war 包 60 | * 61 | * @param warDir 文件夹 62 | */ 63 | protected void handleWarPackage(File warDir) { 64 | logger.info("[MiniCat] handleWarPackage baseDirStr={}, file={}", baseWarDirStr, warDir); 65 | String prefix = "/" + warDir.getName(); 66 | // 模拟 tomcat,如果在根目录下,war 的名字为 root。则认为是根路径项目。 67 | if ("ROOT".equalsIgnoreCase(prefix)) { 68 | // 这里需要 / 吗? 69 | prefix = ""; 70 | } 71 | 72 | String webXmlPath = getWebXmlPath(warDir); 73 | File webXmlFile = new File(webXmlPath); 74 | if (!webXmlFile.exists()) { 75 | logger.warn("[MiniCat] webXmlPath={} not found", webXmlPath); 76 | return; 77 | } 78 | 79 | try { 80 | SAXReader reader = new SAXReader(); 81 | Document document = reader.read(webXmlFile); 82 | Element root = document.getRootElement(); 83 | 84 | //1. servlet 85 | initServletMapping(prefix, root, warDir); 86 | 87 | //2. filter 88 | initFilterMapping(prefix, root, warDir); 89 | 90 | //3. listener 91 | initListener(root, warDir); 92 | } catch (DocumentException e) { 93 | throw new RuntimeException(e); 94 | } 95 | } 96 | 97 | /** 98 | * 初始化监听器 99 | * @since 0.7.0 100 | */ 101 | protected void initListener(Element root, 102 | File warDir) { 103 | try { 104 | List filterElements = root.elements("listener"); 105 | List servletClassList = new ArrayList<>(); 106 | for (Element servletElement : filterElements) { 107 | String servletClass = servletElement.elementText("listener-class"); 108 | servletClassList.add(servletClass); 109 | } 110 | 111 | // 自定义 class loader 112 | Path classesPath = buildClassesPath(baseWarDirStr, warDir); 113 | Path libPath = buildLibPath(baseWarDirStr, warDir); 114 | 115 | // 这个是以 web.xml 为主题。 116 | try(WebAppClassLoader webAppClassLoader = new WebAppClassLoader(classesPath, libPath)) { 117 | servletClassList.forEach(className -> { 118 | try { 119 | Class servletClazz = webAppClassLoader.loadClass(className); 120 | EventListener eventListener = (EventListener) servletClazz.newInstance(); 121 | miniCatContextConfig.getListenerManager().addEventListener(eventListener); 122 | } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { 123 | throw new MiniCatException(e); 124 | } 125 | }); 126 | }; 127 | } catch (Exception e) { 128 | throw new MiniCatException(e); 129 | } 130 | } 131 | 132 | protected void initFilterMapping(String urlPrefix, 133 | Element root, 134 | File warDir) { 135 | try { 136 | Map filterClassNameMap = new HashMap<>(); 137 | Map urlPatternMap = new HashMap<>(); 138 | List filterElements = root.elements("filter"); 139 | for (Element servletElement : filterElements) { 140 | String servletName = servletElement.elementText("filter-name"); 141 | String servletClass = servletElement.elementText("filter-class"); 142 | filterClassNameMap.put(servletName, servletClass); 143 | } 144 | List urlMappingElements = root.elements("filter-mapping"); 145 | for (Element urlElem : urlMappingElements) { 146 | String servletName = urlElem.elementText("filter-name"); 147 | String urlPattern = urlElem.elementText("url-pattern"); 148 | urlPatternMap.put(servletName, urlPattern); 149 | } 150 | 151 | // 自定义 class loader 152 | Path classesPath = buildClassesPath(baseWarDirStr, warDir); 153 | Path libPath = buildLibPath(baseWarDirStr, warDir); 154 | 155 | // 这个是以 web.xml 为主题。 156 | try(WebAppClassLoader webAppClassLoader = new WebAppClassLoader(classesPath, libPath)) { 157 | // 循环处理 158 | for(Map.Entry urlPatternEntry : urlPatternMap.entrySet()) { 159 | String filterName = urlPatternEntry.getKey(); 160 | String urlPattern = urlPatternEntry.getValue(); 161 | 162 | String className = filterClassNameMap.get(filterName); 163 | if(StringUtil.isEmpty(className)) { 164 | throw new MiniCatException("className not found for filterName: " + filterName); 165 | } 166 | 167 | // 构建 168 | handleUrlPatternAndFilter(urlPrefix, urlPattern, className, webAppClassLoader); 169 | } 170 | }; 171 | } catch (Exception e) { 172 | throw new MiniCatException(e); 173 | } 174 | } 175 | 176 | protected void handleUrlPatternAndFilter(String urlPrefix, 177 | String urlPattern, 178 | String className, 179 | WebAppClassLoader webAppClassLoader) throws InstantiationException, IllegalAccessException, ClassNotFoundException { 180 | Class servletClazz = webAppClassLoader.loadClass(className); 181 | Filter filter = (Filter) servletClazz.newInstance(); 182 | miniCatContextConfig.getFilterManager().register(urlPrefix + urlPattern, filter); 183 | } 184 | 185 | 186 | //1. 解析 web.xml 187 | //2. 读取对应的 servlet mapping 188 | //3. 保存对应的 url + servlet 示例到 servletMap 189 | protected void initServletMapping(String urlPrefix, 190 | Element root, 191 | File warDir) { 192 | try { 193 | Map servletClassNameMap = new HashMap<>(); 194 | Map urlPatternMap = new HashMap<>(); 195 | List servletElements = root.elements("servlet"); 196 | for (Element servletElement : servletElements) { 197 | String servletName = servletElement.elementText("servlet-name"); 198 | String servletClass = servletElement.elementText("servlet-class"); 199 | servletClassNameMap.put(servletName, servletClass); 200 | } 201 | List urlMappingElements = root.elements("servlet-mapping"); 202 | for (Element urlElem : urlMappingElements) { 203 | String servletName = urlElem.elementText("servlet-name"); 204 | String urlPattern = urlElem.elementText("url-pattern"); 205 | urlPatternMap.put(servletName, urlPattern); 206 | } 207 | 208 | // 自定义 class loader 209 | Path classesPath = buildClassesPath(baseWarDirStr, warDir); 210 | Path libPath = buildLibPath(baseWarDirStr, warDir); 211 | 212 | // 这个是以 web.xml 为主题。 213 | try(WebAppClassLoader webAppClassLoader = new WebAppClassLoader(classesPath, libPath)) { 214 | // 循环处理 215 | for(Map.Entry urlPatternEntry : urlPatternMap.entrySet()) { 216 | String servletName = urlPatternEntry.getKey(); 217 | String urlPattern = urlPatternEntry.getValue(); 218 | 219 | String className = servletClassNameMap.get(servletName); 220 | if(StringUtil.isEmpty(className)) { 221 | throw new MiniCatException("className not found for servletName: " + servletName); 222 | } 223 | 224 | // 构建 225 | handleUrlPatternAndServlet(urlPrefix, urlPattern, className, webAppClassLoader); 226 | } 227 | }; 228 | } catch (Exception e) { 229 | throw new MiniCatException(e); 230 | } 231 | } 232 | 233 | protected void handleUrlPatternAndServlet(String urlPrefix, 234 | String urlPattern, 235 | String className, 236 | WebAppClassLoader webAppClassLoader) throws InstantiationException, IllegalAccessException, ClassNotFoundException { 237 | Class servletClazz = webAppClassLoader.loadClass(className); 238 | HttpServlet httpServlet = (HttpServlet) servletClazz.newInstance(); 239 | 240 | miniCatContextConfig.getServletManager().register(urlPrefix + urlPattern, httpServlet); 241 | } 242 | 243 | 244 | protected Path buildClassesPath(String baseDirStr, File warDir) { 245 | String path = InnerResourceUtil.buildFullPath(baseDirStr, warDir.getName() + "/WEB-INF/classes/"); 246 | return Paths.get(path); 247 | } 248 | 249 | protected Path buildLibPath(String baseDirStr, File warDir) { 250 | String path = InnerResourceUtil.buildFullPath(baseDirStr, warDir.getName() + "WEB-INF/lib/"); 251 | return Paths.get(path); 252 | } 253 | 254 | protected String buildClassFullPath(String baseDirStr, 255 | String className) { 256 | String prefix = InnerResourceUtil.buildFullPath(baseDirStr, "WEB-INF/classes/"); 257 | 258 | String classNamePath = StringUtil.packageToPath(className) + ".class"; 259 | 260 | return prefix + classNamePath; 261 | } 262 | 263 | protected String getWebXmlPath(File file) { 264 | return file.getAbsolutePath() + "/WEB-INF/web.xml"; 265 | } 266 | 267 | 268 | 269 | 270 | } 271 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/filter/MyMiniCatLoggingHttpFilter.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.filter; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | 6 | import javax.servlet.FilterChain; 7 | import javax.servlet.ServletException; 8 | import javax.servlet.ServletRequest; 9 | import javax.servlet.ServletResponse; 10 | import javax.servlet.http.HttpFilter; 11 | import java.io.IOException; 12 | 13 | /** 14 | * 15 | * @since 0.6.0 16 | */ 17 | public class MyMiniCatLoggingHttpFilter extends HttpFilter { 18 | 19 | private static final Log logger = LogFactory.getLog(MyMiniCatLoggingHttpFilter.class); 20 | 21 | @Override 22 | public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { 23 | logger.info("[MiniCat] MyMiniCatLoggingHttpFilter#doFilter req={}, resp={}", req, res); 24 | 25 | super.doFilter(req, res, chain); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/filter/manager/DefaultFilterManager.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.filter.manager; 2 | 3 | import com.github.houbb.heaven.util.lang.StringUtil; 4 | import com.github.houbb.log.integration.core.Log; 5 | import com.github.houbb.log.integration.core.LogFactory; 6 | import com.github.houbb.minicat.exception.MiniCatException; 7 | import com.github.houbb.minicat.support.servlet.manager.DefaultServletManager; 8 | 9 | import javax.servlet.Filter; 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | /** 16 | * filter 管理 17 | * 18 | * @since 0.6.0 19 | */ 20 | public class DefaultFilterManager implements IFilterManager { 21 | 22 | private static final Log logger = LogFactory.getLog(DefaultServletManager.class); 23 | 24 | protected final Map filterMap = new HashMap<>(); 25 | 26 | protected String baseDirStr; 27 | 28 | protected void doInit(String baseDirStr) { 29 | this.baseDirStr = baseDirStr; 30 | } 31 | 32 | @Override 33 | public void init(String baseDir) { 34 | if(StringUtil.isEmpty(baseDir)) { 35 | throw new MiniCatException("baseDir is empty!"); 36 | } 37 | 38 | doInit(baseDir); 39 | } 40 | 41 | @Override 42 | public void register(String url, Filter filter) { 43 | logger.info("[MiniCat] register Filter, url={}, Filter={}", url, filter.getClass().getName()); 44 | 45 | filterMap.put(url, filter); 46 | } 47 | 48 | @Override 49 | public Filter getFilter(String url) { 50 | return filterMap.get(url); 51 | } 52 | 53 | @Override 54 | public List getMatchFilters(String url) { 55 | List resultList = new ArrayList<>(); 56 | 57 | for(Map.Entry entry : filterMap.entrySet()) { 58 | String urlPattern = entry.getKey(); 59 | if(url.matches(urlPattern)) { 60 | resultList.add(entry.getValue()); 61 | } 62 | } 63 | 64 | return resultList; 65 | } 66 | 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/filter/manager/IFilterManager.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.filter.manager; 2 | 3 | import javax.servlet.Filter; 4 | import java.util.List; 5 | 6 | /** 7 | * filter 管理 8 | * 9 | * @since 0.6.0 10 | */ 11 | public interface IFilterManager { 12 | 13 | /** 14 | * 初始化 15 | * @param baseDir 基础文件夹 16 | * @since 0.5.0 17 | */ 18 | void init(String baseDir); 19 | 20 | /** 21 | * 注册 servlet 22 | * 23 | * @param url url 24 | * @param filter servlet 25 | */ 26 | void register(String url, Filter filter); 27 | 28 | /** 29 | * 获取 servlet 30 | * 31 | * @param url url 32 | * @return servlet 33 | */ 34 | Filter getFilter(String url); 35 | 36 | /** 37 | * 获取匹配的 38 | * @param url 正则 39 | * @return 结果 40 | */ 41 | List getMatchFilters(String url); 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/listener/DefaultListenerManager.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.listener; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | import com.github.houbb.minicat.exception.MiniCatException; 6 | import com.github.houbb.minicat.support.listener.foo.MyServletContextAttrListener; 7 | 8 | import javax.servlet.*; 9 | import java.util.ArrayList; 10 | import java.util.EventListener; 11 | import java.util.List; 12 | 13 | public class DefaultListenerManager implements IListenerManager { 14 | 15 | private static final Log logger = LogFactory.getLog(DefaultListenerManager.class); 16 | 17 | private final List servletContextListeners = new ArrayList<>(); 18 | private final List servletContextAttributeListeners = new ArrayList<>(); 19 | private final List servletRequestListeners = new ArrayList<>(); 20 | private final List servletRequestAttributeListeners = new ArrayList<>(); 21 | private final List readListeners = new ArrayList<>(); 22 | private final List writeListeners = new ArrayList<>(); 23 | 24 | @Override 25 | public void addEventListener(EventListener eventListener) { 26 | logger.info("Start add event ={}", eventListener.getClass().getName()); 27 | 28 | if(eventListener instanceof ServletContextListener) { 29 | servletContextListeners.add((ServletContextListener) eventListener); 30 | } else if(eventListener instanceof ServletContextAttributeListener) { 31 | servletContextAttributeListeners.add((ServletContextAttributeListener) eventListener); 32 | } else if(eventListener instanceof ServletRequestListener) { 33 | servletRequestListeners.add((ServletRequestListener) eventListener); 34 | } else if(eventListener instanceof ServletRequestAttributeListener) { 35 | servletRequestAttributeListeners.add((ServletRequestAttributeListener) eventListener); 36 | } else if(eventListener instanceof ReadListener) { 37 | readListeners.add((ReadListener) eventListener); 38 | } else if(eventListener instanceof WriteListener) { 39 | writeListeners.add((WriteListener) eventListener); 40 | } else { 41 | throw new MiniCatException("Not support type of eventListener=" + eventListener.getClass().getName()); 42 | } 43 | } 44 | 45 | @Override 46 | public List getServletContextListeners() { 47 | return servletContextListeners; 48 | } 49 | 50 | @Override 51 | public List getServletContextAttributeListeners() { 52 | return servletContextAttributeListeners; 53 | } 54 | 55 | @Override 56 | public List getServletRequestListeners() { 57 | return servletRequestListeners; 58 | } 59 | 60 | @Override 61 | public List getServletRequestAttributeListeners() { 62 | return servletRequestAttributeListeners; 63 | } 64 | 65 | @Override 66 | public List getReadListeners() { 67 | return readListeners; 68 | } 69 | 70 | @Override 71 | public List getWriteListeners() { 72 | return writeListeners; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/listener/IListenerManager.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.listener; 2 | 3 | import javax.servlet.*; 4 | import java.util.EventListener; 5 | import java.util.List; 6 | 7 | public interface IListenerManager { 8 | 9 | void addEventListener(final EventListener eventListener); 10 | 11 | List getServletContextListeners(); 12 | 13 | List getServletContextAttributeListeners(); 14 | 15 | List getServletRequestListeners(); 16 | 17 | List getServletRequestAttributeListeners(); 18 | 19 | List getReadListeners(); 20 | 21 | List getWriteListeners(); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/listener/foo/MyServletContextAttrListener.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.listener.foo; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | 6 | import javax.servlet.ServletContextAttributeEvent; 7 | import javax.servlet.ServletContextAttributeListener; 8 | import javax.servlet.ServletContextEvent; 9 | import javax.servlet.ServletContextListener; 10 | 11 | public class MyServletContextAttrListener implements ServletContextAttributeListener { 12 | 13 | private static final Log logger = LogFactory.getLog(MyServletContextAttrListener.class); 14 | 15 | 16 | @Override 17 | public void attributeAdded(ServletContextAttributeEvent event) { 18 | logger.info("MyServletContextAttrListener attributeAdded, event={}", event); 19 | } 20 | 21 | @Override 22 | public void attributeRemoved(ServletContextAttributeEvent event) { 23 | logger.info("MyServletContextAttrListener attributeAdded, event={}", event); 24 | } 25 | 26 | @Override 27 | public void attributeReplaced(ServletContextAttributeEvent event) { 28 | logger.info("MyServletContextAttrListener attributeAdded, event={}", event); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/listener/foo/MyServletContextListener.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.listener.foo; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | 6 | import javax.servlet.ServletContextEvent; 7 | import javax.servlet.ServletContextListener; 8 | 9 | public class MyServletContextListener implements ServletContextListener { 10 | 11 | private static final Log logger = LogFactory.getLog(MyServletContextListener.class); 12 | 13 | @Override 14 | public void contextInitialized(ServletContextEvent sce) { 15 | logger.info("MyServletContextListener contextInitialized"); 16 | } 17 | 18 | @Override 19 | public void contextDestroyed(ServletContextEvent sce) { 20 | logger.info("MyServletContextListener contextDestroyed"); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/listener/foo/MyServletReadListener.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.listener.foo; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | 6 | import javax.servlet.ReadListener; 7 | import javax.servlet.ServletContextEvent; 8 | import javax.servlet.ServletContextListener; 9 | import java.io.IOException; 10 | 11 | public class MyServletReadListener implements ReadListener { 12 | 13 | private static final Log logger = LogFactory.getLog(MyServletReadListener.class); 14 | 15 | @Override 16 | public void onDataAvailable() throws IOException { 17 | logger.info("MyServletReadListener onDataAvailable"); 18 | } 19 | 20 | @Override 21 | public void onAllDataRead() throws IOException { 22 | logger.info("MyServletReadListener onAllDataRead"); 23 | } 24 | 25 | @Override 26 | public void onError(Throwable t) { 27 | logger.error("MyServletReadListener onError", t); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/listener/foo/MyServletRequestAttrListener.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.listener.foo; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | 6 | import javax.servlet.ServletRequestAttributeEvent; 7 | import javax.servlet.ServletRequestAttributeListener; 8 | import javax.servlet.ServletRequestEvent; 9 | import javax.servlet.ServletRequestListener; 10 | 11 | public class MyServletRequestAttrListener implements ServletRequestAttributeListener { 12 | 13 | private static final Log logger = LogFactory.getLog(MyServletRequestAttrListener.class); 14 | 15 | @Override 16 | public void attributeAdded(ServletRequestAttributeEvent srae) { 17 | logger.info("MyServletRequestAttrListener attributeAdded event={}", srae); 18 | } 19 | 20 | @Override 21 | public void attributeRemoved(ServletRequestAttributeEvent srae) { 22 | logger.info("MyServletRequestAttrListener attributeRemoved event={}", srae); 23 | } 24 | 25 | @Override 26 | public void attributeReplaced(ServletRequestAttributeEvent srae) { 27 | logger.info("MyServletRequestAttrListener attributeReplaced event={}", srae); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/listener/foo/MyServletRequestListener.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.listener.foo; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | 6 | import javax.servlet.ServletContextEvent; 7 | import javax.servlet.ServletContextListener; 8 | import javax.servlet.ServletRequestEvent; 9 | import javax.servlet.ServletRequestListener; 10 | 11 | public class MyServletRequestListener implements ServletRequestListener { 12 | 13 | private static final Log logger = LogFactory.getLog(MyServletRequestListener.class); 14 | 15 | @Override 16 | public void requestDestroyed(ServletRequestEvent sre) { 17 | logger.info("MyServletRequestListener requestDestroyed"); 18 | } 19 | 20 | @Override 21 | public void requestInitialized(ServletRequestEvent sre) { 22 | logger.info("MyServletRequestListener requestInitialized"); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/listener/foo/MyServletWriteListener.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.listener.foo; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | 6 | import javax.servlet.ServletContextEvent; 7 | import javax.servlet.ServletContextListener; 8 | import javax.servlet.WriteListener; 9 | import java.io.IOException; 10 | 11 | public class MyServletWriteListener implements WriteListener { 12 | 13 | private static final Log logger = LogFactory.getLog(MyServletWriteListener.class); 14 | 15 | @Override 16 | public void onWritePossible() throws IOException { 17 | logger.info("MyServletWriteListener onWritePossible"); 18 | } 19 | 20 | @Override 21 | public void onError(Throwable t) { 22 | logger.error("MyServletWriteListener onError", t); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/package-info.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support; -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/request/EmptyRequestDispatcher.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.request; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | import com.github.houbb.minicat.bs.MiniCatBootstrap; 6 | import com.github.houbb.minicat.dto.IMiniCatRequest; 7 | import com.github.houbb.minicat.dto.IMiniCatResponse; 8 | import com.github.houbb.minicat.support.context.MiniCatContextConfig; 9 | import com.github.houbb.minicat.util.InnerHttpUtil; 10 | 11 | /** 12 | * 空请求 13 | */ 14 | public class EmptyRequestDispatcher implements IRequestDispatcher{ 15 | 16 | private static final Log logger = LogFactory.getLog(MiniCatBootstrap.class); 17 | 18 | /** 19 | * 请求分发 20 | */ 21 | @Override 22 | public void dispatch(final IMiniCatRequest request, 23 | final IMiniCatResponse response, 24 | final MiniCatContextConfig config) { 25 | logger.warn("[MiniCat] empty request url"); 26 | 27 | //也可以返回默认页面之类的 28 | response.write(InnerHttpUtil.http400Resp()); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/request/IRequestDispatcher.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.request; 2 | 3 | import com.github.houbb.minicat.dto.IMiniCatRequest; 4 | import com.github.houbb.minicat.dto.IMiniCatResponse; 5 | import com.github.houbb.minicat.support.context.MiniCatContextConfig; 6 | 7 | public interface IRequestDispatcher { 8 | 9 | /** 10 | * 请求分发 11 | * @param config 配置 12 | * @param request 请求 13 | * @param response 响应 14 | */ 15 | void dispatch(final IMiniCatRequest request, 16 | final IMiniCatResponse response, 17 | final MiniCatContextConfig config); 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/request/RequestDispatcherManager.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.request; 2 | 3 | import com.github.houbb.heaven.util.lang.StringUtil; 4 | import com.github.houbb.log.integration.core.Log; 5 | import com.github.houbb.log.integration.core.LogFactory; 6 | import com.github.houbb.minicat.dto.IMiniCatRequest; 7 | import com.github.houbb.minicat.dto.IMiniCatResponse; 8 | import com.github.houbb.minicat.support.context.MiniCatContextConfig; 9 | import com.github.houbb.minicat.support.servlet.manager.IServletManager; 10 | 11 | import javax.servlet.Filter; 12 | import javax.servlet.ServletException; 13 | import java.io.IOException; 14 | import java.util.List; 15 | import java.util.function.Consumer; 16 | 17 | /** 18 | * 请求分发管理器 19 | * 20 | * @since 0.3.0 21 | */ 22 | public class RequestDispatcherManager implements IRequestDispatcher { 23 | 24 | private static final Log logger = LogFactory.getLog(RequestDispatcherManager.class); 25 | 26 | private final IRequestDispatcher emptyRequestDispatcher = new EmptyRequestDispatcher(); 27 | private final IRequestDispatcher servletRequestDispatcher = new ServletRequestDispatcher(); 28 | private final IRequestDispatcher staticHtmlRequestDispatcher = new StaticHtmlRequestDispatcher(); 29 | 30 | @Override 31 | public void dispatch(final IMiniCatRequest request, 32 | final IMiniCatResponse response, 33 | final MiniCatContextConfig config) { 34 | final IServletManager servletManager = config.getServletManager(); 35 | 36 | 37 | // 判断文件是否存在 38 | String requestUrl = request.getUrl(); 39 | //before 40 | List filterList = config.getFilterManager().getMatchFilters(requestUrl); 41 | 42 | // 获取请求分发 43 | final IRequestDispatcher requestDispatcher = getRequestDispatcher(requestUrl); 44 | 45 | // 请求前 46 | filterList.forEach(filter -> { 47 | try { 48 | filter.doFilter(request, response, null); 49 | } catch (IOException | ServletException e) { 50 | throw new RuntimeException(e); 51 | } 52 | }); 53 | 54 | // 正式分发 55 | requestDispatcher.dispatch(request, response, config); 56 | } 57 | 58 | private IRequestDispatcher getRequestDispatcher(String requestUrl) { 59 | if (StringUtil.isEmpty(requestUrl)) { 60 | return emptyRequestDispatcher; 61 | } else { 62 | if (requestUrl.endsWith(".html")) { 63 | return staticHtmlRequestDispatcher; 64 | } else { 65 | return servletRequestDispatcher; 66 | } 67 | } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/request/ServletRequestDispatcher.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.request; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | import com.github.houbb.minicat.bs.MiniCatBootstrap; 6 | import com.github.houbb.minicat.dto.IMiniCatRequest; 7 | import com.github.houbb.minicat.dto.IMiniCatResponse; 8 | import com.github.houbb.minicat.exception.MiniCatException; 9 | import com.github.houbb.minicat.support.context.MiniCatContextConfig; 10 | import com.github.houbb.minicat.support.servlet.manager.IServletManager; 11 | import com.github.houbb.minicat.util.InnerHttpUtil; 12 | 13 | import javax.servlet.http.HttpServlet; 14 | 15 | /** 16 | * 静态页面 17 | */ 18 | public class ServletRequestDispatcher implements IRequestDispatcher { 19 | 20 | private static final Log logger = LogFactory.getLog(ServletRequestDispatcher.class); 21 | 22 | public void dispatch(final IMiniCatRequest request, 23 | final IMiniCatResponse response, 24 | final MiniCatContextConfig config) { 25 | final IServletManager servletManager = config.getServletManager(); 26 | 27 | // 直接和 servlet 映射 28 | final String requestUrl = request.getUrl(); 29 | HttpServlet httpServlet = servletManager.getServlet(requestUrl); 30 | if(httpServlet == null) { 31 | logger.warn("[MiniCat] requestUrl={} mapping not found", requestUrl); 32 | response.write(InnerHttpUtil.http404Resp()); 33 | } else { 34 | // 正常的逻辑处理 35 | try { 36 | httpServlet.service(request, response); 37 | } catch (Exception e) { 38 | logger.error("[MiniCat] http servlet handle meet ex", e); 39 | 40 | throw new MiniCatException(e); 41 | } 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/request/StaticHtmlRequestDispatcher.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.request; 2 | 3 | import com.github.houbb.heaven.util.io.FileUtil; 4 | import com.github.houbb.log.integration.core.Log; 5 | import com.github.houbb.log.integration.core.LogFactory; 6 | import com.github.houbb.minicat.bs.MiniCatBootstrap; 7 | import com.github.houbb.minicat.dto.IMiniCatRequest; 8 | import com.github.houbb.minicat.dto.IMiniCatResponse; 9 | import com.github.houbb.minicat.support.context.MiniCatContextConfig; 10 | import com.github.houbb.minicat.util.InnerHttpUtil; 11 | import com.github.houbb.minicat.util.InnerResourceUtil; 12 | 13 | /** 14 | * 静态页面 15 | * @author 老马啸西风 16 | */ 17 | public class StaticHtmlRequestDispatcher implements IRequestDispatcher { 18 | 19 | private static final Log logger = LogFactory.getLog(StaticHtmlRequestDispatcher.class); 20 | 21 | public void dispatch(final IMiniCatRequest request, 22 | final IMiniCatResponse response, 23 | final MiniCatContextConfig config) { 24 | 25 | String absolutePath = InnerResourceUtil.buildFullPath(config.getBaseDir(), request.getUrl()); 26 | String content = FileUtil.getFileContent(absolutePath); 27 | logger.info("[MiniCat] static html path: {}, content={}", absolutePath, content); 28 | String html = InnerHttpUtil.http200Resp(content); 29 | response.write(html); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/servlet/AbstractMiniCatHttpServlet.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.servlet; 2 | 3 | import com.github.houbb.minicat.constant.HttpMethodType; 4 | 5 | import javax.servlet.ServletException; 6 | import javax.servlet.ServletRequest; 7 | import javax.servlet.ServletResponse; 8 | import javax.servlet.http.HttpServlet; 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | import java.io.IOException; 12 | 13 | public abstract class AbstractMiniCatHttpServlet extends HttpServlet { 14 | 15 | public abstract void doGet(HttpServletRequest request, HttpServletResponse response); 16 | 17 | public abstract void doPost(HttpServletRequest request, HttpServletResponse response); 18 | 19 | @Override 20 | public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { 21 | HttpServletRequest httpServletRequest = (HttpServletRequest) req; 22 | HttpServletResponse httpServletResponse = (HttpServletResponse) res; 23 | if(HttpMethodType.GET.getCode().equalsIgnoreCase(httpServletRequest.getMethod())) { 24 | this.doGet(httpServletRequest, httpServletResponse); 25 | return; 26 | } 27 | 28 | this.doPost(httpServletRequest, httpServletResponse); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/servlet/MyMiniCatHttpServlet.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.servlet; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | import com.github.houbb.minicat.dto.IMiniCatResponse; 6 | import com.github.houbb.minicat.dto.MiniCatResponseBio; 7 | import com.github.houbb.minicat.util.InnerHttpUtil; 8 | 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | 12 | /** 13 | * 仅用于测试 14 | * 15 | * @since 0.3.0 16 | * @author 老马啸西风 17 | */ 18 | public class MyMiniCatHttpServlet extends AbstractMiniCatHttpServlet { 19 | 20 | private static final Log logger = LogFactory.getLog(MyMiniCatHttpServlet.class); 21 | 22 | 23 | // 方法实现 24 | @Override 25 | public void doGet(HttpServletRequest request, HttpServletResponse response) { 26 | logger.info("MyMiniCatServlet-get"); 27 | // 模拟耗时 28 | try { 29 | Thread.sleep(5000); 30 | } catch (InterruptedException e) { 31 | e.printStackTrace(); 32 | } 33 | String content = "MyMiniCatServlet-get"; 34 | IMiniCatResponse miniCatResponse = (IMiniCatResponse) response; 35 | miniCatResponse.write(InnerHttpUtil.http200Resp(content)); 36 | logger.info("MyMiniCatServlet-get-end"); 37 | } 38 | 39 | @Override 40 | public void doPost(HttpServletRequest request, HttpServletResponse response) { 41 | String content = "MyMiniCatServlet-post"; 42 | 43 | IMiniCatResponse miniCatResponse = (IMiniCatResponse) response; 44 | miniCatResponse.write(InnerHttpUtil.http200Resp(content)); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/servlet/manager/DefaultServletManager.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.servlet.manager; 2 | 3 | import com.github.houbb.heaven.util.lang.StringUtil; 4 | import com.github.houbb.log.integration.core.Log; 5 | import com.github.houbb.log.integration.core.LogFactory; 6 | import com.github.houbb.minicat.exception.MiniCatException; 7 | 8 | import javax.servlet.http.HttpServlet; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | /** 13 | * servlet 管理 14 | * 15 | * 基于 web.xml 的读取解析 16 | * @since 0.5.0 17 | * @author 老马啸西风 18 | */ 19 | public class DefaultServletManager implements IServletManager { 20 | 21 | private static final Log logger = LogFactory.getLog(DefaultServletManager.class); 22 | 23 | protected final Map servletMap = new HashMap<>(); 24 | 25 | protected String baseDir; 26 | 27 | protected void doInit(String baseDirStr) { 28 | this.baseDir = baseDirStr; 29 | } 30 | 31 | @Override 32 | public void init(String baseDir) { 33 | if(StringUtil.isEmpty(baseDir)) { 34 | throw new MiniCatException("baseDir is empty!"); 35 | } 36 | 37 | doInit(baseDir); 38 | } 39 | 40 | @Override 41 | public void register(String url, HttpServlet servlet) { 42 | logger.info("[MiniCat] register servlet, url={}, servlet={}", url, servlet.getClass().getName()); 43 | 44 | servletMap.put(url, servlet); 45 | } 46 | 47 | @Override 48 | public HttpServlet getServlet(String url) { 49 | return servletMap.get(url); 50 | } 51 | 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/servlet/manager/IServletManager.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.servlet.manager; 2 | 3 | import javax.servlet.http.HttpServlet; 4 | 5 | /** 6 | * servlet 管理 7 | * 8 | * @since 0.3.0 9 | */ 10 | public interface IServletManager { 11 | 12 | /** 13 | * 初始化 14 | * @param baseDir 基础文件夹 15 | * @since 0.5.0 16 | */ 17 | void init(String baseDir); 18 | 19 | /** 20 | * 注册 servlet 21 | * 22 | * @param url url 23 | * @param servlet servlet 24 | */ 25 | void register(String url, HttpServlet servlet); 26 | 27 | /** 28 | * 获取 servlet 29 | * 30 | * @param url url 31 | * @return servlet 32 | */ 33 | HttpServlet getServlet(String url); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/servlet/manager/LocalRootServletManager.java: -------------------------------------------------------------------------------- 1 | //package com.github.houbb.minicat.support.servlet.manager; 2 | // 3 | //import com.github.houbb.heaven.support.handler.IHandler; 4 | //import com.github.houbb.log.integration.core.Log; 5 | //import com.github.houbb.log.integration.core.LogFactory; 6 | //import com.github.houbb.minicat.exception.MiniCatException; 7 | //import org.dom4j.Document; 8 | //import org.dom4j.Element; 9 | //import org.dom4j.io.SAXReader; 10 | // 11 | //import javax.servlet.http.HttpServlet; 12 | //import java.io.InputStream; 13 | //import java.util.List; 14 | // 15 | ///** 16 | // * servlet 管理 17 | // * 18 | // * 基于 web.xml 的读取解析 19 | // * 20 | // * 默认根路径 21 | // * 22 | // * @since 0.3.0 23 | // */ 24 | //public class LocalRootServletManager extends DefaultServletManager { 25 | // 26 | // private static final Log logger = LogFactory.getLog(LocalRootServletManager.class); 27 | // 28 | // @Override 29 | // protected void doInit(String baseDir) { 30 | // InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml"); 31 | // 32 | // this.loadFromWebXml(resourceAsStream); 33 | // } 34 | // 35 | // protected void loadFromWebXml(InputStream resourceAsStream) { 36 | // this.loadFromWebXml("", resourceAsStream); 37 | // } 38 | // 39 | // //1. 解析 web.xml 40 | // //2. 读取对应的 servlet mapping 41 | // //3. 保存对应的 url + servlet 示例到 servletMap 42 | // protected void loadFromWebXml(String urlPrefix, InputStream resourceAsStream) { 43 | // this.loadFromWebXml(urlPrefix, resourceAsStream, new IHandler() { 44 | // @Override 45 | // public Class handle(String s) { 46 | // try { 47 | // // 默认直接加载本地的 class 48 | // return Class.forName(s); 49 | // } catch (ClassNotFoundException e) { 50 | // throw new RuntimeException(e); 51 | // } 52 | // } 53 | // }); 54 | // } 55 | // 56 | // //1. 解析 web.xml 57 | // //2. 读取对应的 servlet mapping 58 | // //3. 保存对应的 url + servlet 示例到 servletMap 59 | // protected void loadFromWebXml(String urlPrefix, InputStream resourceAsStream, IHandler servletClassHandler) { 60 | //// InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml"); 61 | // SAXReader saxReader = new SAXReader(); 62 | // try { 63 | // Document document = saxReader.read(resourceAsStream); 64 | // Element rootElement = document.getRootElement(); 65 | // 66 | // List selectNodes = rootElement.selectNodes("//servlet"); 67 | // //1, 找到所有的servlet标签,找到servlet-name和servlet-class 68 | // //2, 根据servlet-name找到中与其匹配的 69 | // for (Element element : selectNodes) { 70 | // /** 71 | // * 1, 找到所有的servlet标签,找到servlet-name和servlet-class 72 | // */ 73 | // Element servletNameElement = (Element) element.selectSingleNode("servlet-name"); 74 | // String servletName = servletNameElement.getStringValue(); 75 | // Element servletClassElement = (Element) element.selectSingleNode("servlet-class"); 76 | // String servletClass = servletClassElement.getStringValue(); 77 | // 78 | // /** 79 | // * 2, 根据servlet-name找到中与其匹配的 80 | // */ 81 | // //Xpath表达式:从/web-app/servlet-mapping下查询,查询出servlet-name=servletName的元素 82 | // Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']'"); 83 | // 84 | // String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue(); 85 | // Class servletClazz = servletClassHandler.handle(servletClass); 86 | // HttpServlet httpServlet = (HttpServlet) servletClazz.newInstance(); 87 | // 88 | // this.register(urlPrefix + urlPattern, httpServlet); 89 | // } 90 | // 91 | // } catch (Exception e) { 92 | // logger.error("[MiniCat] read web.xml failed", e); 93 | // 94 | // throw new MiniCatException(e); 95 | // } 96 | // } 97 | //} 98 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/servlet/manager/LocalServletManager.java: -------------------------------------------------------------------------------- 1 | //package com.github.houbb.minicat.support.servlet.manager; 2 | // 3 | //import com.github.houbb.heaven.util.lang.StringUtil; 4 | //import com.github.houbb.log.integration.core.Log; 5 | //import com.github.houbb.log.integration.core.LogFactory; 6 | //import com.github.houbb.minicat.exception.MiniCatException; 7 | //import org.dom4j.Document; 8 | //import org.dom4j.Element; 9 | //import org.dom4j.io.SAXReader; 10 | // 11 | //import javax.servlet.Filter; 12 | //import javax.servlet.http.HttpServlet; 13 | //import java.io.File; 14 | //import java.io.InputStream; 15 | //import java.util.HashMap; 16 | //import java.util.List; 17 | //import java.util.Map; 18 | // 19 | ///** 20 | // * servlet 管理 21 | // * 22 | // * 基于 web.xml 的读取解析 23 | // * 24 | // * 默认根路径 25 | // * 26 | // * @since 0.6.0 27 | // */ 28 | //public class LocalServletManager extends DefaultServletManager { 29 | // 30 | // private static final Log logger = LogFactory.getLog(LocalServletManager.class); 31 | // 32 | // /** 33 | // * web 文件地址 34 | // */ 35 | // protected final File webXmlFile; 36 | // 37 | // /** 38 | // * 请求地址前缀 39 | // */ 40 | // private final String urlPrefix; 41 | // 42 | // public LocalServletManager(String webXmlPath, String urlPrefix) { 43 | // this.webXmlFile = new File(webXmlPath); 44 | // this.urlPrefix = urlPrefix; 45 | // } 46 | // 47 | // public LocalServletManager(String webXmlPath) { 48 | // this(webXmlPath, ""); 49 | // } 50 | // 51 | // @Override 52 | // protected void doInit(String baseDir) { 53 | // InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml"); 54 | // 55 | // this.processWebXml(); 56 | // } 57 | // 58 | // 59 | // 60 | //} 61 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/servlet/manager/WarsServletManager.java: -------------------------------------------------------------------------------- 1 | //package com.github.houbb.minicat.support.servlet.manager; 2 | // 3 | //import com.github.houbb.heaven.util.lang.StringUtil; 4 | //import com.github.houbb.log.integration.core.Log; 5 | //import com.github.houbb.log.integration.core.LogFactory; 6 | //import com.github.houbb.minicat.exception.MiniCatException; 7 | //import com.github.houbb.minicat.support.classloader.WebAppClassLoader; 8 | //import com.github.houbb.minicat.support.war.WarExtractorDefault; 9 | //import com.github.houbb.minicat.util.InnerResourceUtil; 10 | //import org.dom4j.Document; 11 | //import org.dom4j.Element; 12 | //import org.dom4j.io.SAXReader; 13 | // 14 | //import javax.servlet.http.HttpServlet; 15 | //import java.io.File; 16 | //import java.nio.file.Path; 17 | //import java.nio.file.Paths; 18 | //import java.util.HashMap; 19 | //import java.util.List; 20 | //import java.util.Map; 21 | // 22 | ///** 23 | // * servlet 管理 24 | // *

25 | // * 基于 web.xml 的读取解析 26 | // * 27 | // * @since 0.5.0 28 | // */ 29 | //public class WarsServletManager extends DefaultServletManager { 30 | // 31 | // private static final Log logger = LogFactory.getLog(WarExtractorDefault.class); 32 | // 33 | // private String baseDirStr; 34 | // 35 | // @Override 36 | // protected void doInit(String baseDirStr) { 37 | // logger.info("[MiniCat] servlet init with baseDir={}", baseDirStr); 38 | // this.baseDirStr = baseDirStr; 39 | // 40 | // // 分别解析 war 包 41 | // File baseDir = new File(baseDirStr); 42 | // 43 | // File[] files = baseDir.listFiles(); 44 | // for (File file : files) { 45 | // if (file.isDirectory()) { 46 | // handleWarPackage(file); 47 | // } 48 | // } 49 | // } 50 | // 51 | // /** 52 | // * 处理单个 war 包 53 | // * 54 | // * @param warDir 文件夹 55 | // */ 56 | // protected void handleWarPackage(File warDir) { 57 | // logger.info("[MiniCat] handleWarPackage baseDirStr={}, file={}", baseDirStr, warDir); 58 | // String prefix = "/" + warDir.getName(); 59 | // // 模拟 tomcat,如果在根目录下,war 的名字为 root。则认为是根路径项目。 60 | // if ("ROOT".equalsIgnoreCase(prefix)) { 61 | // // 这里需要 / 吗? 62 | // prefix = ""; 63 | // } 64 | // 65 | // String webXmlPath = getWebXmlPath(warDir); 66 | // File webXmlFile = new File(webXmlPath); 67 | // if (!webXmlFile.exists()) { 68 | // logger.warn("[MiniCat] webXmlPath={} not found", webXmlPath); 69 | // return; 70 | // } 71 | // 72 | // loadUrlAndServletClass(prefix, webXmlFile, warDir); 73 | // } 74 | // 75 | // //1. 解析 web.xml 76 | // //2. 读取对应的 servlet mapping 77 | // //3. 保存对应的 url + servlet 示例到 servletMap 78 | // protected void loadUrlAndServletClass(String urlPrefix, 79 | // File webXmlFile, 80 | // File warDir) { 81 | // try { 82 | // SAXReader reader = new SAXReader(); 83 | // Document document = reader.read(webXmlFile); 84 | // 85 | // Element root = document.getRootElement(); 86 | // 87 | // Map servletClassNameMap = new HashMap<>(); 88 | // Map urlPatternMap = new HashMap<>(); 89 | // List servletElements = root.elements("servlet"); 90 | // for (Element servletElement : servletElements) { 91 | // String servletName = servletElement.elementText("servlet-name"); 92 | // String servletClass = servletElement.elementText("servlet-class"); 93 | // servletClassNameMap.put(servletName, servletClass); 94 | // } 95 | // 96 | // List urlMappingElements = root.elements("servlet-mapping"); 97 | // for (Element urlElem : urlMappingElements) { 98 | // String servletName = urlElem.elementText("servlet-name"); 99 | // String urlPattern = urlElem.elementText("url-pattern"); 100 | // urlPatternMap.put(servletName, urlPattern); 101 | // } 102 | // 103 | // // 自定义 class loader 104 | // Path classesPath = buildClassesPath(baseDirStr, warDir); 105 | // Path libPath = buildLibPath(baseDirStr, warDir); 106 | // 107 | // // 这个是以 web.xml 为主题。 108 | // try(WebAppClassLoader webAppClassLoader = new WebAppClassLoader(classesPath, libPath)) { 109 | // // 循环处理 110 | // for(Map.Entry urlPatternEntry : urlPatternMap.entrySet()) { 111 | // String servletName = urlPatternEntry.getKey(); 112 | // String urlPattern = urlPatternEntry.getValue(); 113 | // 114 | // String className = servletClassNameMap.get(servletName); 115 | // if(StringUtil.isEmpty(className)) { 116 | // throw new MiniCatException("className not found for servletName: " + servletName); 117 | // } 118 | // 119 | // // 构建 120 | // handleUrlAndClass(urlPrefix, urlPattern, className, webAppClassLoader); 121 | // } 122 | // }; 123 | // } catch (Exception e) { 124 | // throw new MiniCatException(e); 125 | // } 126 | // } 127 | // 128 | // protected void handleUrlAndClass(String urlPrefix, 129 | // String urlPattern, 130 | // String className, 131 | // WebAppClassLoader webAppClassLoader) throws InstantiationException, IllegalAccessException, ClassNotFoundException { 132 | // Class servletClazz = webAppClassLoader.loadClass(className); 133 | // HttpServlet httpServlet = (HttpServlet) servletClazz.newInstance(); 134 | // 135 | // super.register(urlPrefix + urlPattern, httpServlet); 136 | // } 137 | // 138 | // 139 | // protected Path buildClassesPath(String baseDirStr, File warDir) { 140 | // String path = InnerResourceUtil.buildFullPath(baseDirStr, warDir.getName() + "/WEB-INF/classes/"); 141 | // return Paths.get(path); 142 | // } 143 | // 144 | // protected Path buildLibPath(String baseDirStr, File warDir) { 145 | // String path = InnerResourceUtil.buildFullPath(baseDirStr, warDir.getName() + "WEB-INF/lib/"); 146 | // return Paths.get(path); 147 | // } 148 | // 149 | // protected String buildClassFullPath(String baseDirStr, 150 | // String className) { 151 | // String prefix = InnerResourceUtil.buildFullPath(baseDirStr, "WEB-INF/classes/"); 152 | // 153 | // String classNamePath = StringUtil.packageToPath(className) + ".class"; 154 | // 155 | // return prefix + classNamePath; 156 | // } 157 | // 158 | // protected String getWebXmlPath(File file) { 159 | // return file.getAbsolutePath() + "/WEB-INF/web.xml"; 160 | // } 161 | // 162 | //} 163 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/war/IWarExtractor.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.war; 2 | 3 | public interface IWarExtractor { 4 | 5 | /** 6 | * 解压处理 7 | * @param baseDir 基本文件夹 8 | */ 9 | void extract(String baseDir); 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/war/WarExtractorDefault.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.war; 2 | 3 | import com.github.houbb.heaven.util.io.FileUtil; 4 | import com.github.houbb.heaven.util.util.ArrayUtil; 5 | import com.github.houbb.log.integration.core.Log; 6 | import com.github.houbb.log.integration.core.LogFactory; 7 | import com.github.houbb.minicat.exception.MiniCatException; 8 | import com.github.houbb.minicat.util.InnerResourceUtil; 9 | import com.github.houbb.minicat.util.InnerWarUtil; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | 14 | /** 15 | * 默认的 war 处理 16 | * 17 | * @since 0.5.0 18 | */ 19 | public class WarExtractorDefault implements IWarExtractor { 20 | 21 | private static final Log logger = LogFactory.getLog(WarExtractorDefault.class); 22 | 23 | @Override 24 | public void extract(String baseDirStr) { 25 | logger.info("[MiniCat] start extract baseDirStr={}", baseDirStr); 26 | 27 | //1. check 28 | // Path baseDir = Paths.get(baseDirStr); 29 | File baseDirFile = new File(baseDirStr); 30 | if (!baseDirFile.exists()) { 31 | logger.error("[MiniCat] base dir not found!"); 32 | throw new MiniCatException("baseDir not found!"); 33 | } 34 | 35 | //2. list war 36 | File[] files = baseDirFile.listFiles(); 37 | if (ArrayUtil.isNotEmpty(files)) { 38 | for (File file : files) { 39 | String fileName = file.getName(); 40 | if (fileName.endsWith(".war")) { 41 | logger.error("[MiniCat] start extract war={}", fileName); 42 | handleWarFile(baseDirStr, file); 43 | } 44 | } 45 | } 46 | 47 | // handle war package 48 | logger.info("[MiniCat] end extract baseDir={}", baseDirStr); 49 | } 50 | 51 | /** 52 | * 处理 war 文件 53 | * 54 | * @param baseDirStr 基础 55 | * @param warFile war 文件 56 | */ 57 | private void handleWarFile(String baseDirStr, 58 | File warFile) { 59 | //1. 删除历史的 war 解压包 60 | String warPackageName = FileUtil.getFileName(warFile.getName()); 61 | String fullWarPackagePath = InnerResourceUtil.buildFullPath(baseDirStr, warPackageName); 62 | FileUtil.deleteFileRecursive(fullWarPackagePath); 63 | 64 | //2. 解压文件 65 | try { 66 | InnerWarUtil.extractWar(warFile.getAbsolutePath(), fullWarPackagePath); 67 | } catch (IOException e) { 68 | logger.error("[MiniCat] handleWarFile failed, warPackageName={}", warFile.getAbsoluteFile(), e); 69 | throw new MiniCatException(e); 70 | } 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/support/writer/MyPrintWriter.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.support.writer; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | import com.github.houbb.minicat.util.InnerHttpUtil; 6 | import io.netty.buffer.ByteBuf; 7 | import io.netty.buffer.Unpooled; 8 | import io.netty.channel.ChannelFutureListener; 9 | import io.netty.channel.ChannelHandlerContext; 10 | 11 | import java.io.*; 12 | import java.nio.charset.Charset; 13 | 14 | public class MyPrintWriter extends PrintWriter { 15 | 16 | private static final Log logger = LogFactory.getLog(MyPrintWriter.class); 17 | 18 | public MyPrintWriter(Writer out) { 19 | super(out); 20 | } 21 | 22 | public MyPrintWriter(Writer out, boolean autoFlush) { 23 | super(out, autoFlush); 24 | } 25 | 26 | public MyPrintWriter(OutputStream out) { 27 | super(out); 28 | } 29 | 30 | public MyPrintWriter(OutputStream out, boolean autoFlush) { 31 | super(out, autoFlush); 32 | } 33 | 34 | public MyPrintWriter(String fileName) throws FileNotFoundException { 35 | super(fileName); 36 | } 37 | 38 | public MyPrintWriter(String fileName, String csn) throws FileNotFoundException, UnsupportedEncodingException { 39 | super(fileName, csn); 40 | } 41 | 42 | public MyPrintWriter(File file) throws FileNotFoundException { 43 | super(file); 44 | } 45 | 46 | public MyPrintWriter(File file, String csn) throws FileNotFoundException, UnsupportedEncodingException { 47 | super(file, csn); 48 | } 49 | 50 | private ChannelHandlerContext ctx; 51 | 52 | public ChannelHandlerContext getCtx() { 53 | return ctx; 54 | } 55 | 56 | public void setCtx(ChannelHandlerContext ctx) { 57 | this.ctx = ctx; 58 | } 59 | 60 | public void print(String text) { 61 | text = InnerHttpUtil.http200Resp(text); 62 | Charset charset = Charset.forName("UTF-8"); 63 | ByteBuf responseBuf = Unpooled.copiedBuffer(text, charset); 64 | ctx.writeAndFlush(responseBuf) 65 | .addListener(ChannelFutureListener.CLOSE); // Close the channel after sending the response 66 | logger.info("[MiniCat] channelRead writeAndFlush DONE"); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/util/CostTimeUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.util; 2 | 3 | @Deprecated 4 | public class CostTimeUtil { 5 | 6 | public static long costTimeMock() { 7 | long start = System.currentTimeMillis(); 8 | // 避免是 sleep 影响判断 9 | String text = "111111111111111111111111111111111111111"; 10 | 11 | for(int i = 0; i < 10000000; i++) { 12 | String text1 = text+i; 13 | text1.matches("123[0-9]"); 14 | } 15 | 16 | return System.currentTimeMillis() - start; 17 | } 18 | 19 | public static void main(String[] args) { 20 | System.out.println(costTimeMock());; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/util/InnerClassLoaderUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.util; 2 | 3 | import com.github.houbb.minicat.exception.MiniCatException; 4 | 5 | import java.io.File; 6 | import java.net.MalformedURLException; 7 | import java.net.URL; 8 | import java.net.URLClassLoader; 9 | import java.util.LinkedHashSet; 10 | import java.util.Set; 11 | 12 | /** 13 | * 类加载 14 | * 15 | * @since 0.5.0 16 | */ 17 | public class InnerClassLoaderUtil { 18 | 19 | public static Class loadClass(String classFilePath, String classFullName) { 20 | // 确保类名与文件路径匹配 21 | // 如果类名是com.example.YourClass,则.class文件应该位于com/example/目录下 22 | 23 | try { 24 | // 创建一个URL对象,表示该class文件的路径 25 | File file = new File(classFilePath); 26 | URL url = file.toURI().toURL(); 27 | 28 | // 创建一个URLClassLoader对象,加载指定的URL 29 | // 使用当前线程的上下文类加载器作为父类加载器 30 | URLClassLoader classLoader = new URLClassLoader(new URL[]{url}, Thread.currentThread().getContextClassLoader()); 31 | 32 | // 使用加载的类 33 | return classLoader.loadClass(classFullName); 34 | } catch (MalformedURLException e) { 35 | // 更好的异常处理,记录异常信息 36 | e.printStackTrace(); 37 | throw new RuntimeException("Failed to convert file to URL", e); 38 | } catch (ClassNotFoundException e) { 39 | e.printStackTrace(); 40 | throw new RuntimeException("Class not found: " + classFullName, e); 41 | } 42 | } 43 | 44 | /** 45 | * 加载指定路径的.class文件对应的Class对象 46 | * 47 | * @param classFilePath 绝对路径的.class文件路径 48 | * @return Class对象 49 | * @throws MalformedURLException 异常 50 | */ 51 | public static Class loadClassFromFile(String classFilePath) throws MalformedURLException { 52 | if (classFilePath == null || classFilePath.isEmpty()) { 53 | throw new IllegalArgumentException("Class file path must not be null or empty."); 54 | } 55 | 56 | // 将文件路径转换为URL 57 | File file = new File(classFilePath); 58 | URL url = file.toURI().toURL(); 59 | 60 | // 创建URLClassLoader实例 61 | URLClassLoader classLoader = new URLClassLoader(new URL[]{url}); 62 | 63 | try { 64 | // 获取文件名,去除.class扩展名,得到类名 65 | String className = file.getName().substring(0, file.getName().length() - 6); 66 | // 加载Class对象 67 | return classLoader.loadClass(className); 68 | } catch (ClassNotFoundException e) { 69 | throw new RuntimeException("Class not found: " + classFilePath, e); 70 | } 71 | } 72 | 73 | public static void main(String[] args) throws MalformedURLException, ClassNotFoundException { 74 | String classFilePath = "D:\\github\\minicat\\src\\test\\webapps\\servlet\\WEB-INF\\classes\\com\\github\\houbb\\servlet\\webxml\\IndexServlet.class"; 75 | 76 | // 创建一个URL对象,表示该class文件的路径 77 | File file = new File(classFilePath); 78 | URL url = file.toURI().toURL(); 79 | 80 | URLClassLoader urlClassLoader = URLClassLoader.newInstance(new URL[] {url}); 81 | 82 | Class clazz = urlClassLoader.loadClass("com.github.houbb.servlet.webxml.IndexServlet"); 83 | System.out.println(clazz.getName()); 84 | } 85 | 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/util/InnerHttpUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.util; 2 | 3 | public class InnerHttpUtil { 4 | 5 | /** 6 | * 符合 http 标准的字符串 7 | * @param rawText 原始文本 8 | * @return 结果 9 | */ 10 | public static String http200Resp(String rawText) { 11 | String format = "HTTP/1.1 200 OK\r\n" + 12 | "Content-Type: text/plain\r\n" + 13 | "\r\n" + 14 | "%s"; 15 | 16 | return String.format(format, rawText); 17 | } 18 | 19 | /** 20 | * 400 响应 21 | * @return 结果 22 | * @since 0.2.0 23 | */ 24 | public static String http400Resp() { 25 | return "HTTP/1.1 400 Bad Request\r\n" + 26 | "Content-Type: text/plain\r\n" + 27 | "\r\n" + 28 | "400 Bad Request: The request could not be understood by the server due to malformed syntax."; 29 | } 30 | 31 | /** 32 | * 404 响应 33 | * @return 结果 34 | * @since 0.2.0 35 | */ 36 | public static String http404Resp() { 37 | String response = "HTTP/1.1 404 Not Found\r\n" + 38 | "Content-Type: text/plain\r\n" + 39 | "\r\n" + 40 | "404 Not Found: The requested resource was not found on this server."; 41 | 42 | return response; 43 | } 44 | 45 | /** 46 | * 500 响应 47 | * @return 结果 48 | * @since 0.2.0 49 | */ 50 | public static String http500Resp() { 51 | return "HTTP/1.1 500 Internal Server Error\r\n" + 52 | "Content-Type: text/plain\r\n" + 53 | "\r\n" + 54 | "500 Internal Server Error: The server encountered an unexpected condition that prevented it from fulfilling the request."; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/util/InnerRequestUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.util; 2 | 3 | import com.github.houbb.minicat.bo.RequestInfoBo; 4 | 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | 8 | public class InnerRequestUtil { 9 | 10 | /** 11 | * 12 | * Server read: GET /my HTTP/1.1 13 | * Host: localhost:8080 14 | * Connection: keep-alive 15 | * Cache-Control: max-age=0 16 | * sec-ch-ua: "Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123" 17 | * sec-ch-ua-mobile: ?0 18 | * sec-ch-ua-platform: "Windows" 19 | * Upgrade-Insecure-Requests: 1 20 | * User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 21 | * 22 | * @param text 文本 23 | * @return 结果 24 | * @since 0.4.0 25 | */ 26 | public static RequestInfoBo buildRequestInfoBo(String text) { 27 | // 使用正则表达式按行分割请求字符串 28 | String[] requestLines = text.split("\r\n"); 29 | 30 | // 获取第一行请求行 31 | String firstLine = requestLines[0]; 32 | 33 | String[] strings = firstLine.split(" "); 34 | String method = strings[0]; 35 | String url = strings[1]; 36 | 37 | return new RequestInfoBo(url, method); 38 | } 39 | 40 | public static RequestInfoBo buildRequestInfoBo(final InputStream inputStream) { 41 | byte[] buffer = new byte[1024]; // 使用固定大小的缓冲区 42 | int bytesRead = 0; 43 | 44 | try { 45 | while ((bytesRead = inputStream.read(buffer)) != -1) { // 循环读取数据直到EOF 46 | String inputStr = new String(buffer, 0, bytesRead); 47 | 48 | // 检查是否读取到完整的HTTP请求行 49 | if (inputStr.contains("\n")) { 50 | // 获取第一行数据 51 | String firstLineStr = inputStr.split("\\n")[0]; 52 | 53 | return buildRequestInfoBo(firstLineStr); 54 | } 55 | } 56 | 57 | return null; 58 | } catch (IOException e) { 59 | throw new RuntimeException(e); 60 | } 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/util/InnerResourceUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.util; 2 | 3 | public class InnerResourceUtil { 4 | 5 | // 获取当前线程的上下文类加载器的资源路径 6 | public static String getCurrentThreadContextClassLoaderResource() { 7 | return Thread.currentThread().getContextClassLoader().getResource("").getPath(); 8 | } 9 | 10 | // 获取系统类加载器的资源路径 11 | public static String getSystemClassLoaderResource() { 12 | return ClassLoader.getSystemResource("").getPath(); 13 | } 14 | 15 | // 获取指定类加载器的资源路径 16 | public static String getClassLoaderResource(Class clazz) { 17 | return clazz.getClassLoader().getResource("").getPath(); 18 | } 19 | 20 | // 获取指定类的根路径 21 | public static String getClassRootResource(Class clazz) { 22 | return clazz.getResource("/").getPath(); 23 | } 24 | 25 | // 获取指定类的路径 26 | public static String getClassResource(Class clazz) { 27 | return clazz.getResource("").getPath(); 28 | } 29 | 30 | // 获取当前工作目录的路径 31 | public static String getCurrentWorkingDirectory() { 32 | return System.getProperty("user.dir"); 33 | } 34 | 35 | // 获取类路径的路径 36 | public static String getClassPath() { 37 | return System.getProperty("java.class.path"); 38 | } 39 | 40 | 41 | /** 42 | * 构建完整的路径。 43 | * 给定一个前缀和路径,此方法将两者结合形成一个完整的URL或文件路径。 44 | * 如果前缀末尾没有斜杠,则会在两者之间添加一个斜杠,确保路径的正确拼接。 45 | * 46 | * @param prefix 路径前缀。例如,http://example.com或C:\Users。 47 | * @param path 相对路径。例如,path/to/resource或test.txt。 48 | * @return 拼接后的完整路径。确保路径正确连接,不会因缺少斜杠而导致路径错误。 49 | */ 50 | public static String buildFullPath(String prefix, String path) { 51 | // 检查前缀是否已以斜杠结尾,如果是,则直接拼接路径;如果不是,则在前缀后添加斜杠再拼接路径 52 | if(path.startsWith("/")) { 53 | path = path.substring(1); 54 | } 55 | 56 | if(prefix.endsWith("/")) { 57 | return prefix + path; 58 | } else { 59 | return prefix + "/" + path; 60 | } 61 | } 62 | 63 | public static void main(String[] args) { 64 | // 示例用法 65 | System.out.println("Current Thread Context ClassLoader Resource: " + getCurrentThreadContextClassLoaderResource()); 66 | System.out.println("System ClassLoader Resource: " + getSystemClassLoaderResource()); 67 | System.out.println("Class Loader Resource: " + getClassLoaderResource(InnerResourceUtil.class)); 68 | System.out.println("Class Root Resource: " + getClassRootResource(InnerResourceUtil.class)); 69 | System.out.println("Class Resource: " + getClassResource(InnerResourceUtil.class)); 70 | System.out.println("Current Working Directory: " + getCurrentWorkingDirectory()); 71 | System.out.println("Class Path: " + getClassPath()); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/github/houbb/minicat/util/InnerWarUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.util; 2 | 3 | import com.github.houbb.heaven.util.io.FileUtil; 4 | 5 | import java.io.File; 6 | import java.io.FileOutputStream; 7 | import java.io.IOException; 8 | import java.nio.file.Files; 9 | import java.nio.file.Paths; 10 | import java.util.zip.ZipEntry; 11 | import java.util.zip.ZipInputStream; 12 | 13 | /** 14 | * 15 | * @since 0.5.0 16 | */ 17 | public class InnerWarUtil { 18 | 19 | public static void main(String[] args) { 20 | String warFilePath = "C:\\Users\\Administrator\\Downloads\\simple-servlet-master\\simple-servlet-master\\target\\servlet.war"; // 要解压的 WAR 文件路径 21 | String destinationDirectory = "C:\\Users\\Administrator\\Downloads\\simple-servlet-master\\simple-servlet-master\\target\\servlet"; // 解压后的目标目录 22 | 23 | try { 24 | extractWar(warFilePath, destinationDirectory); 25 | System.out.println("WAR 文件解压成功!"); 26 | } catch (IOException e) { 27 | System.err.println("解压 WAR 文件时出错:" + e.getMessage()); 28 | } 29 | } 30 | 31 | public static void extractWar(String warFilePath, String destinationDirectory) throws IOException { 32 | File destinationDir = new File(destinationDirectory); 33 | if (!destinationDir.exists()) { 34 | destinationDir.mkdirs(); 35 | } 36 | 37 | try (ZipInputStream zipInputStream = new ZipInputStream(Files.newInputStream(Paths.get(warFilePath)))) { 38 | ZipEntry entry = zipInputStream.getNextEntry(); 39 | while (entry != null) { 40 | String filePath = destinationDirectory + File.separator + entry.getName(); 41 | if (!entry.isDirectory()) { 42 | extractFile(zipInputStream, filePath); 43 | } else { 44 | File dir = new File(filePath); 45 | dir.mkdir(); 46 | } 47 | zipInputStream.closeEntry(); 48 | entry = zipInputStream.getNextEntry(); 49 | } 50 | } 51 | } 52 | 53 | private static void extractFile(ZipInputStream zipInputStream, String filePath) throws IOException { 54 | FileUtil.createFile(filePath); 55 | 56 | try (FileOutputStream fos = new FileOutputStream(filePath)) { 57 | byte[] buffer = new byte[1024]; 58 | int length; 59 | while ((length = zipInputStream.read(buffer)) > 0) { 60 | fos.write(buffer, 0, length); 61 | } 62 | } 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /src/main/resources/index.html: -------------------------------------------------------------------------------- 1 | mini cat index html! -------------------------------------------------------------------------------- /src/main/resources/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | my 7 | com.github.houbb.minicat.support.servlet.MyMiniCatHttpServlet 8 | 9 | 10 | my 11 | /my 12 | 13 | 14 | 15 | 16 | LoggingFilter 17 | com.github.houbb.minicat.support.filter.MyMiniCatLoggingHttpFilter 18 | 19 | 20 | LoggingFilter 21 | /* 22 | 23 | 24 | 25 | 26 | com.github.houbb.minicat.support.listener.foo.MyServletContextAttrListener 27 | 28 | 29 | com.github.houbb.minicat.support.listener.foo.MyServletContextListener 30 | 31 | 32 | com.github.houbb.minicat.support.listener.foo.MyServletReadListener 33 | 34 | 35 | com.github.houbb.minicat.support.listener.foo.MyServletWriteListener 36 | 37 | 38 | com.github.houbb.minicat.support.listener.foo.MyServletRequestListener 39 | 40 | 41 | com.github.houbb.minicat.support.listener.foo.MyServletRequestAttrListener 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/test/java/com/github/houbb/minicat/bs/MiniCatBootstrapMainTest.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.bs; 2 | 3 | public class MiniCatBootstrapMainTest { 4 | 5 | public static void main(String[] args) throws InterruptedException { 6 | MiniCatBootstrap bootstrap = new MiniCatBootstrap(); 7 | bootstrap.start(); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/com/github/houbb/minicat/bs/MiniCatBootstrapMainWithWarsTest.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.bs; 2 | 3 | public class MiniCatBootstrapMainWithWarsTest { 4 | 5 | public static void main(String[] args) throws InterruptedException { 6 | String baseWarDir = "D:\\github\\minicat\\src\\test\\webapps"; 7 | MiniCatBootstrap bootstrap = new MiniCatBootstrap(8080, baseWarDir); 8 | bootstrap.start(); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/com/github/houbb/minicat/bs/ResourceUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.bs; 2 | 3 | public class ResourceUtilTest { 4 | 5 | // 获取当前线程的上下文类加载器的资源路径 6 | public static String getCurrentThreadContextClassLoaderResource() { 7 | return Thread.currentThread().getContextClassLoader().getResource("").getPath(); 8 | } 9 | 10 | // 获取系统类加载器的资源路径 11 | public static String getSystemClassLoaderResource() { 12 | return ClassLoader.getSystemResource("").getPath(); 13 | } 14 | 15 | // 获取指定类加载器的资源路径 16 | public static String getClassLoaderResource(Class clazz) { 17 | return clazz.getClassLoader().getResource("").getPath(); 18 | } 19 | 20 | // 获取指定类的根路径 21 | public static String getClassRootResource(Class clazz) { 22 | return clazz.getResource("/").getPath(); 23 | } 24 | 25 | // 获取指定类的路径 26 | public static String getClassResource(Class clazz) { 27 | return clazz.getResource("").getPath(); 28 | } 29 | 30 | // 获取当前工作目录的路径 31 | public static String getCurrentWorkingDirectory() { 32 | return System.getProperty("user.dir"); 33 | } 34 | 35 | // 获取类路径的路径 36 | public static String getClassPath() { 37 | return System.getProperty("java.class.path"); 38 | } 39 | 40 | 41 | /** 42 | * 构建完整的路径。 43 | * 给定一个前缀和路径,此方法将两者结合形成一个完整的URL或文件路径。 44 | * 如果前缀末尾没有斜杠,则会在两者之间添加一个斜杠,确保路径的正确拼接。 45 | * 46 | * @param prefix 路径前缀。例如,http://example.com或C:\Users。 47 | * @param path 相对路径。例如,path/to/resource或test.txt。 48 | * @return 拼接后的完整路径。确保路径正确连接,不会因缺少斜杠而导致路径错误。 49 | */ 50 | public static String buildFullPath(String prefix, String path) { 51 | // 检查前缀是否已以斜杠结尾,如果是,则直接拼接路径;如果不是,则在前缀后添加斜杠再拼接路径 52 | if(path.startsWith("/")) { 53 | path = path.substring(1); 54 | } 55 | 56 | if(prefix.endsWith("/")) { 57 | return prefix + path; 58 | } else { 59 | return prefix + "/" + path; 60 | } 61 | } 62 | 63 | public static void main(String[] args) { 64 | // 示例用法 65 | System.out.println("Current Thread Context ClassLoader Resource: " + getCurrentThreadContextClassLoaderResource()); 66 | System.out.println("System ClassLoader Resource: " + getSystemClassLoaderResource()); 67 | System.out.println("Class Loader Resource: " + getClassLoaderResource(ResourceUtilTest.class)); 68 | System.out.println("Class Root Resource: " + getClassRootResource(ResourceUtilTest.class)); 69 | System.out.println("Class Resource: " + getClassResource(ResourceUtilTest.class)); 70 | System.out.println("Current Working Directory: " + getCurrentWorkingDirectory()); 71 | System.out.println("Class Path: " + getClassPath()); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/test/java/com/github/houbb/minicat/bs/socket/MiniCatBootstrapBioServletTest.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.bs.socket; 2 | 3 | import com.github.houbb.minicat.bs.servlet.MiniCatBootstrapBioSocket; 4 | 5 | public class MiniCatBootstrapBioServletTest { 6 | 7 | public static void main(String[] args) throws InterruptedException { 8 | MiniCatBootstrapBioSocket bootstrap = new MiniCatBootstrapBioSocket(); 9 | bootstrap.start(); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/com/github/houbb/minicat/bs/socket/MiniCatBootstrapBioThreadServletTest.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.bs.socket; 2 | 3 | import com.github.houbb.minicat.bs.servlet.MiniCatBootstrapBioThreadSocket; 4 | 5 | public class MiniCatBootstrapBioThreadServletTest { 6 | 7 | public static void main(String[] args) throws InterruptedException { 8 | MiniCatBootstrapBioThreadSocket bootstrap = new MiniCatBootstrapBioThreadSocket(); 9 | bootstrap.start(); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/com/github/houbb/minicat/bs/socket/MiniCatBootstrapNettyTest.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.bs.socket; 2 | 3 | import com.github.houbb.minicat.bs.servlet.MiniCatBootstrapNetty; 4 | 5 | public class MiniCatBootstrapNettyTest { 6 | 7 | public static void main(String[] args) throws InterruptedException { 8 | MiniCatBootstrapNetty bootstrap = new MiniCatBootstrapNetty(); 9 | bootstrap.start(); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/com/github/houbb/minicat/bs/socket/MiniCatBootstrapNioSocketTest.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.bs.socket; 2 | 3 | import com.github.houbb.minicat.bs.servlet.MiniCatBootstrapNioSocket; 4 | 5 | public class MiniCatBootstrapNioSocketTest { 6 | 7 | public static void main(String[] args) { 8 | MiniCatBootstrapNioSocket nioSocket = new MiniCatBootstrapNioSocket(); 9 | nioSocket.start(); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/com/github/houbb/minicat/bs/socket/MiniCatBootstrapNioThreadSocketTest.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.minicat.bs.socket; 2 | 3 | import com.github.houbb.minicat.bs.servlet.MiniCatBootstrapNioThreadSocket; 4 | 5 | public class MiniCatBootstrapNioThreadSocketTest { 6 | 7 | public static void main(String[] args) { 8 | MiniCatBootstrapNioThreadSocket nioSocket = new MiniCatBootstrapNioThreadSocket(); 9 | nioSocket.start(); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/test/resources/reqest_demo.txt: -------------------------------------------------------------------------------- 1 | Server read: GET /my HTTP/1.1 2 | Host: localhost:8080 3 | Connection: keep-alive 4 | Cache-Control: max-age=0 5 | sec-ch-ua: "Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123" 6 | sec-ch-ua-mobile: ?0 7 | sec-ch-ua-platform: "Windows" 8 | Upgrade-Insecure-Requests: 1 9 | User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 10 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 11 | Sec-Fetch-Site: none 12 | Sec-Fetch-Mode: navigate 13 | Sec-Fetch-User: ?1 14 | Sec-Fetch-Dest: document 15 | Accept-Encoding: gzip, deflate, br, zstd 16 | Accept-Language: zh-CN,zh;q=0.9 -------------------------------------------------------------------------------- /src/test/webapps/servlet.war: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houbb/minicat/a999267461725148272d8d043828003f25be7592/src/test/webapps/servlet.war -------------------------------------------------------------------------------- /src/test/webapps/servlet/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Archiver-Version: Plexus Archiver 3 | Built-By: Administrator 4 | Created-By: Apache Maven 3.6.3 5 | Build-Jdk: 1.8.0_192 6 | 7 | -------------------------------------------------------------------------------- /src/test/webapps/servlet/META-INF/maven/com.github.houbb/servlet-webxml/pom.properties: -------------------------------------------------------------------------------- 1 | #Generated by Maven 2 | #Sat Apr 06 16:11:54 CST 2024 3 | version=1.0-SNAPSHOT 4 | groupId=com.github.houbb 5 | artifactId=servlet-webxml 6 | -------------------------------------------------------------------------------- /src/test/webapps/servlet/META-INF/maven/com.github.houbb/servlet-webxml/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.github.houbb 8 | servlet-webxml 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 8 13 | 8 14 | UTF-8 15 | 2.2 16 | 17 | 18 | war 19 | 20 | 21 | 22 | javax.servlet 23 | servlet-api 24 | 2.5 25 | provided 26 | 27 | 28 | 29 | org.apache.tomcat 30 | tomcat-servlet-api 31 | 9.0.0.M8 32 | provided 33 | 34 | 35 | 36 | 37 | servlet 38 | 39 | 40 | org.apache.tomcat.maven 41 | tomcat7-maven-plugin 42 | ${plugin.tomcat.version} 43 | 44 | 8080 45 | / 46 | ${project.build.sourceEncoding} 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/test/webapps/servlet/WEB-INF/classes/com/github/houbb/servlet/webxml/IndexServlet.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houbb/minicat/a999267461725148272d8d043828003f25be7592/src/test/webapps/servlet/WEB-INF/classes/com/github/houbb/servlet/webxml/IndexServlet.class -------------------------------------------------------------------------------- /src/test/webapps/servlet/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | /index.html 10 | 11 | 12 | 13 | index 14 | com.github.houbb.servlet.webxml.IndexServlet 15 | 16 | 17 | 18 | index 19 | /index 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/test/webapps/servlet/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Hello Servlet! 5 | 6 | --------------------------------------------------------------------------------