自定义miniTomcat功能(不利用spring)
- 具有简单解析http协议,get Post等
- 在请求处理上具有责任链功能
- 自定义并发请求池
- 自定义动态处理
- 自定义服务端session保存以及cookie的生成
整体流程
启动服务监听-启动类
package com.hole.server;
import com.hole.servlet.ServletContext;
import com.hole.util.ITheadPollFactory;
import com.hole.util.TreadPoolFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.concurrent.ExecutorService;
/**
* @describtion: nimiTomcat 服务器
* @author: guozi
* @create: 2021-01-25
*/
public class MiniTomcat {
public static final Logger logger = LoggerFactory.getLogger(MiniTomcat.class);
/**
* minitomcat 启动入口
*/
public void run(){
logger.info("miniTomcat服务器准备启动");
//TODO:读取yml文件
int port = getServerPort();
ServerSocket server = null;
try{
server = new ServerSocket(port);
logger.info("服务器启动成功,监听端口为{}", port);
}catch (Exception e){
logger.error("服务启动失败,原因:{}", e.getMessage());
//TODO:大概率是端口占用,选择另一个端口
System.exit(0);
}
//线程池自定义生成
ITheadPollFactory threadFactory = new TreadPoolFactory();
ExecutorService httpThread = threadFactory.createThreadPoolsByTaskDefinition(100,
100, "MINI_TOMCAT", 1000, 0.7, null, null);
//请求拦截容器生成
ServletContext.interceptorContext.put("http1","com.hole.server.InterceptorImpHttp1");
ServletContext.interceptorContext.put("http2","com.hole.server.InterceptorImpHttp2");
while(true){
Socket client = null;
SocketAddress socketAddress = null;
try{
client = server.accept();
socketAddress = client.getRemoteSocketAddress();
logger.info("客户端{}连接", socketAddress);
//处理连接
ServerService serverService = new ServerService(client);
//使用线程池执行客户端连接(http协议)
httpThread.submit(serverService);
}catch (Exception e){
logger.error("客户端{}连接断开,原因:{}", socketAddress, e.getMessage());
}
}
}
/**
*启动server
*/
private void startServer(){
MiniTomcat miniTomcat = new MiniTomcat();
miniTomcat.run();
}
public static void main(String[] args){
MiniTomcat miniTomcat = new MiniTomcat();
miniTomcat.run();
}
/**
* 获取服务监听的端口
*/
private int getServerPort(){
return 8000;
}
}
简单解析http协议
获取两个关键对象 HttpServletRequest 和HttpServletResponse
get 请求内容和post请求内容
2021-03-22 16:21:46,075 INFO [com.hole.http.HttpServletRequest] - url String is :GET /favicon.ico HTTP/1.1
Host: localhost:8000
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36
Accept: image/avif,image/webp,image/apng,image/*,*/*;q=0.8
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: image
Referer: http://localhost:8000/4组件规格配置.png
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
2021-03-20 16:49:34,331 INFO [com.hole.http.HttpServletRequest] - url String is :POST /login.action?username=1&password=1 HTTP/1.1
Content-Type: application/json
User-Agent: PostmanRuntime/7.26.8
Accept: */*
Postman-Token: edbf42c4-bedf-4537-b4d7-e04c7883e9a8
Host: localhost:8000
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 27
{
"a":1,
"b":2
}
package com.hole.http;
import com.alibaba.fastjson.JSONObject;
import com.hole.models.User;
import com.hole.servlet.ServletContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.net.URLDecoder;
import java.util.*;
/**
* @description: HttpRequest class
* @author: guozi
* @create: 2021-01-22
*/
public class HttpServletRequest {
/**
* 请求方法
*/
private String method;
/**
* 请求地址
*/
private String requestUrl;
/**
* 请求体
*/
private String requestContent;
/**
* 请求头
*/
private Map<String, String> requestHeaders = new Hashtable<>();
/**
* http cookie
*/
private List<HttpCookie> requestCookies = new ArrayList<>();
/**
* cookie SessionID
*/
private String jsessionid = null;
public static final Logger logger = LoggerFactory.getLogger(HttpServletRequest.class);
/**
* 请求参数
*/
private Map<String,String> requestParam = new Hashtable<>();
public HttpServletRequest(InputStream inputStream){
StringBuilder sb = new StringBuilder();
byte[] bytes = new byte[100*1024];
int length = -1;
try{
length = inputStream.read(bytes);
}catch (IOException e){
logger.error("读取请求数据失败,原因:{}", e.getMessage());
}
for(int i = 0; i < length; i++){
sb.append((char) bytes[i]);
}
try{
this.requestContent = URLDecoder.decode(sb.toString(), "UTF-8") ;
}catch (Exception e){
logger.error("解析url失败,原因:{}", e.getMessage());
}
logger.info("url String is :{}", this.requestContent);
try{
parseProtocol();
}catch (Exception e){
logger.error("解析url错误 :{}", e.getMessage());
}
}
/**
* 获取请求方式
*/
public String getMethod() {
return method;
}
/**
* 解析请求数据中的content到httpRequest对象
*/
private void parseProtocol(){
String[] ss = this.requestContent.split(" ");
this.method = ss[0];
this.requestUrl = ss[1];
parseParameter();
parseHeader();
parseCookie();
this.jsessionid = parseSessionId();
}
public String getRequestUrl(){
return this.requestUrl;
}
/**
* 解析请求参数
* requestURI: user.action?name=z&password=a
*/
private void parseParameter(){
int index = this.requestUrl.indexOf("?");
//请求存在参数
if(index >= 0){
String[] params = this.requestUrl.substring(index+1).split("&");
for(String param : params){
String[] kv = param.split("=");
this.requestParam.put(kv[0], kv[1]);
}
}
if(this.method.equals("POST")){
String[] parts = this.requestContent.split("\r\n\r\n");
if(parts.length >= 1){
String entity = parts[1];
JSONObject jsonObject = JSONObject.parseObject(entity);
String[] params = entity.split("&");
for(String param : jsonObject.keySet()){
this.requestParam.put(param, jsonObject.getString(param));
}
}
}
}
public String getParameters(String key){
return this.requestParam.get(key);
}
public String setParameters(String key, String val){
return this.requestParam.put(key, val);
}
/**
* 解析请求头,GET /请求地址 HTTP/1.1
*/
private void parseHeader(){
//请求头
String[] parts = this.requestContent.split("\r\n\r\n");
//GET /请求地址 HTTP/1.1
String[] headers = parts[0].split("\r\n");
//Host: localhost:8888 Connection: keep-alive ...
for(String header : headers){
if(!header.contains(":")){
continue;
}
String[] kv = header.split(": ");
this.requestHeaders.put(kv[0], kv[1]);
}
}
public String getHeader(String key){
return this.requestHeaders.get(key);
}
/**
* 解析cookie
* Cookie: name=value; serssionId=sss
*/
private void parseCookie(){
if(this.requestHeaders == null || this.requestHeaders.size() == 0){
return;
}
String cookieString = this.requestHeaders.get("Cookie");
if(cookieString != null && cookieString.length() > 0){
String[] params = cookieString.split("; ");
for(String param : params){
String[] kv = param.split("=");
if(kv.length > 0){
this.requestCookies.add(new HttpCookie(kv[0], kv[1]));
}
}
}
}
/**
* 解析sessionId
*/
private String parseSessionId(){
for(HttpCookie cookie : this.requestCookies){
if(cookie.getName().equals("JSESSIONID") ){
if(ServletContext.sessionContext.get( cookie.getValue()) != null){
return cookie.getValue();
}
}
}
return null;
}
public String getSessionID(){
return this.jsessionid;
}
public HttpSession getHttpSession(){
HttpSession httpSession = null;
if(this.jsessionid == null || !ServletContext.sessionContext.containsKey(this.jsessionid)){
this.jsessionid = UUID.randomUUID().toString();
httpSession = new HttpSession(this.jsessionid);
ServletContext.sessionContext.put(this.jsessionid, httpSession);
}else{
httpSession = ServletContext.sessionContext.get(this.jsessionid);
}
return httpSession;
}
}
package com.hole.http;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import java.io.*;
import java.util.Date;
/**
* @description: HttpResponse class
* @author: guozi
* @create: 2021-01-22
*/
public class HttpServletResponse {
/**
*http请求
*/
private HttpServletRequest httpServletRequest;
/**
* 响应输出流
*/
private OutputStream outputStream;
/**
* 服务的资源路径
*/
private String webRoot = this.getClass().getClassLoader().getResource("").getPath() + File.separator + "stastic";
/*private String webRoot3 = this.getClass().getClassLoader().getResource("").getPath();//编译后的classes路径
private String webRoot4 = this.getClass().getResource("").getPath();//编译后的具体类路径
private String webRoot2 = this.getClass().getClassLoader().getResource("log4j.properties").getPath();//查找文件的路径*/
public static final Logger logger = LoggerFactory.getLogger(HttpServletResponse.class);
public HttpServletResponse(HttpServletRequest httpServletRequest, OutputStream outputStream){
this.httpServletRequest = httpServletRequest;
this.outputStream = outputStream;
}
public PrintWriter getWriter(){
return new PrintWriter(this.outputStream);
}
/**
* 判断url输出html jpg png
* 响应码404,200,500...
*
*/
public void sendRedirect(){
String url = this.httpServletRequest.getRequestUrl();
String fileName = null;
int code = 200;
if(url.endsWith("/")){
fileName = url + "index.html";
}else{
fileName = url;
}
String contenType = "";
String parentPath = this.webRoot;
//根据不同文件类型返回不同的响应(http协议)
if(url.endsWith(".jpg")){
contenType = "application/x-jpg";
parentPath = parentPath + "/picture";
}else if(url.endsWith(".jpe")||url.endsWith(".jpeg")){
contenType = "image/jpeg";
parentPath = parentPath + "/picture";
}else if(url.endsWith(".png")){
contenType = "image/png";
parentPath = parentPath + "/picture";
} else if(url.endsWith(".gif")){
contenType = "image/gif";
parentPath = parentPath + "/picture";
}else if(url.endsWith(".css")){
contenType = "text/css";
parentPath = parentPath + "/css";
}else if(url.endsWith(".js")){
contenType = "application/x-javascript";
parentPath = parentPath + "/js";
}else if(url.endsWith(".swf")){
contenType = "application/x-shockwave-flash";
parentPath = parentPath + "/font";
}else if(url.endsWith(".ico")){
contenType = "image/*";
parentPath = parentPath + "/picture";
}
else{
contenType = "text/html";
parentPath = parentPath + "/html";
}
File file = new File(parentPath, fileName);
if(!file.exists()){
file = new File(this.webRoot + "/html", "404.html");
contenType = "text/html";
}
sendRes(file, contenType, code);
}
/**
* 返回响应流
*/
private void sendRes(File file, String contentType, int code){
String responseHeader = genProtocol(file.length(), contentType, code);
byte[] bs = readFile(file);
try{
this.outputStream.write(responseHeader.getBytes());
this.outputStream.flush();
this.outputStream.write(bs);
this.outputStream.flush();
}catch (Exception e){
logger.error("返回结果写文件流失败:{}", e.getMessage());
}finally {
try{
this.outputStream.close();
}catch (Exception e){
logger.error("返回结果关闭文件流失败:{}", e.getMessage());
}
}
}
/**
* 拼接响应协议
*/
private String genProtocol(long contentLength, String contentType, int code){
StringBuilder res = new StringBuilder();
res.append("HTTP/1.1 ").append(code).append("OK\r\n")
.append("Server: miniTomcat\r\n")
.append("Content-Type: ").append(contentType).append(";charset:utf-8\r\n")
.append("Content-Length: ").append(contentLength).append("\r\n")
.append("Date: ").append(new Date()).append("\r\n")
.append("\r\n");
return res.toString();
}
/**
* 读取文件流
*/
private byte[] readFile(File file){
FileInputStream fileInputStream = null;
ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
try{
byte[] buffer = new byte[1024];
fileInputStream = new FileInputStream(file);
int length;
while((length = fileInputStream.read(buffer, 0, buffer.length)) != -1){
arrayOutputStream.write(buffer, 0, length);
arrayOutputStream.flush();
}
}catch (Exception e){
logger.error("读取文件流失败:{}", e.getMessage());
}finally {
try{
if(fileInputStream != null){
fileInputStream.close();
}
}catch (Exception e){
logger.error("关闭文件流失败:{}", e.getMessage());
}
}
return arrayOutputStream.toByteArray();
}
}