自定义miniTomcat功能(1)

该博客介绍了如何实现一个简单的迷你Tomcat服务器,具备HTTP协议解析、责任链模式请求处理、自定义并发请求池和动态处理等功能。通过启动服务监听指定端口,接收并处理客户端连接,使用线程池执行请求。同时,博客还展示了如何解析HTTP的GET和POST请求,包括请求头、请求体和请求参数。
摘要由CSDN通过智能技术生成

 

自定义miniTomcat功能(不利用spring)

  1. 具有简单解析http协议,get Post等
  2. 在请求处理上具有责任链功能
  3. 自定义并发请求池
  4. 自定义动态处理
  5. 自定义服务端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();
    }





}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值