从零手写实现 tomcat-03-请求和响应的抽象

简介: 该文档介绍了 MiniCat 项目,它是一个简单的 HTTP 服务器实现。v1 版本中, MiniCatRequest 对象解析 HTTP 请求,包括方法、URL 和输入流,而 MiniCatResponse 使用输出流处理响应。start 方法使用这些封装后的对象处理网络通信。在 v2 版本,服务器添加了返回静态资源文件的功能,如 HTML,通过解析 URL 并读取对应本地文件内容来响应请求。测试示例展示了如何访问和显示 index.html。

整体思路

我们针对入参 request 和 出参 response 做一个简单的封装。

v1-出入参的抽象

request

    /**
     * 请求方式 例如:GET/POST
     */
    private String method;


    /**
     * / , /index.html
     */

    private String url;


    /**
     * 其他的属性都是通过inputStream解析出来的。
     */

    private InputStream inputStream;

    public MiniCatRequest(InputStream inputStream) {
   
        this.inputStream = inputStream;

        this.readFromStream();
    }

    private void readFromStream() {
   
        try {
   
            //从输入流中获取请求信息
            int count = inputStream.available();
            byte[] bytes = new byte[count];
            int readResult = inputStream.read(bytes);
            String inputsStr = new String(bytes);
            logger.info("[MiniCat] readCount={}, input stream {}", readResult, inputsStr);

            //获取第一行数据
            String firstLineStr = inputsStr.split("\\n")[0];  //GET / HTTP/1.1
            String[] strings = firstLineStr.split(" ");
            this.method = strings[0];
            this.url = strings[1];
        } catch (IOException e) {
   
            logger.error("[MiniCat] readFromStream meet ex", e);
            throw new RuntimeException(e);
        }
    }

这里是针对 http 请求的解析处理。

response

    private final OutputStream outputStream;

    public MiniCatResponse(OutputStream outputStream) {
   
        this.outputStream = outputStream;
    }


    public void write(byte[] bytes) {
   
        try {
   
            outputStream.write(bytes);
        } catch (IOException e) {
   
            throw new MiniCatException(e);
        }
    }

start 方法调整

直接改动为对应的出入参对象。

while(runningFlag && !serverSocket.isClosed()){
   
    Socket socket = serverSocket.accept();
    // 输入流
    InputStream inputStream = socket.getInputStream();
    MiniCatRequest request = new MiniCatRequest(inputStream);
    // 输出流
    MiniCatResponse response = new MiniCatResponse(socket.getOutputStream());
    response.write(InnerHttpUtil.httpResp("Hello miniCat!").getBytes());
    socket.close();
}

测试

[INFO] [2024-04-02 16:27:32.455] [Thread-0] [c.g.h.m.b.MiniCatBootstrap.startSync] - [MiniCat] start listen on port 8080
[INFO] [2024-04-02 16:27:32.455] [Thread-0] [c.g.h.m.b.MiniCatBootstrap.startSync] - [MiniCat] visit url http://127.0.0.1:8080

我们浏览器访问 http://127.0.0.1:8080

读取到的流内容为:

[INFO] [2024-04-02 16:28:17.825] [Thread-0] [c.g.h.m.d.MiniCatRequest.readFromStream] - [MiniCat] readCount=664, input stream GET / HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
sec-ch-ua: "Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36
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
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9

v2-返回静态资源文件

思路

我们根据 url 解析本地的 html 等静态资源信息。

resources 下面的文件,mvn clean install 之后,默认在 ~\target\classes 路径下

核心代码

// 输出流
MiniCatResponse response = new MiniCatResponse(socket.getOutputStream());
// 判断文件是否存在
String staticHtmlPath = request.getUrl();
if (staticHtmlPath.endsWith(".html")) {
   
    String absolutePath = ResourceUtil.buildFullPath(ResourceUtil.getClassRootResource(MiniCatBootstrap.class), staticHtmlPath);
    String content = FileUtil.getFileContent(absolutePath);
    logger.info("[MiniCat] static html path: {}, content={}", absolutePath, content);
    String html = InnerHttpUtil.http200Resp(content);
    response.write(html);
} else {
   
    String html = InnerHttpUtil.http404Resp();
    response.write(html);
}

主要是两个步骤:

1)获取当前 class 文件对应的资源文件根路径。

2)然后拼接完整文件路径,读取文件内容。

测试

比如我们在 resource 下面放一个 index.html

内容如下:

mini cat index html!

启动后,访问:

访问 http://127.0.0.1:8080/index.html

页面返回:

mini cat index html!

开源地址

 /\_/\  
( o.o ) 
 > ^ <

mini-cat 是简易版本的 tomcat 实现。别称【嗅虎】(心有猛虎,轻嗅蔷薇。)

开源地址:https://github.com/houbb/minicat

相关文章
|
10月前
|
JSON 前端开发 Java
图解HTTP请求Tomcat服务器实现前后端交互-2
图解HTTP请求Tomcat服务器实现前后端交互
90 0
|
10月前
|
前端开发 JavaScript Java
图解HTTP请求Tomcat服务器实现前后端交互-1
图解HTTP请求Tomcat服务器实现前后端交互
177 0
|
16天前
|
监控 网络协议 应用服务中间件
【Tomcat源码分析】从零开始理解 HTTP 请求处理 (第一篇)
本文详细解析了Tomcat架构中复杂的`Connector`组件。作为客户端与服务器间沟通的桥梁,`Connector`负责接收请求、封装为`Request`和`Response`对象,并传递给`Container`处理。文章通过四个关键问题逐步剖析了`Connector`的工作原理,并深入探讨了其构造方法、`init()`与`start()`方法。通过分析`ProtocolHandler`、`Endpoint`等核心组件,揭示了`Connector`初始化及启动的全过程。本文适合希望深入了解Tomcat内部机制的读者。欢迎关注并点赞,持续更新中。如有问题,可搜索【码上遇见你】交流。
【Tomcat源码分析】从零开始理解 HTTP 请求处理 (第一篇)
|
4月前
|
缓存 负载均衡 NoSQL
Redis系列学习文章分享---第十四篇(Redis多级缓存--封装Http请求+向tomcat发送http请求+根据商品id对tomcat集群负载均衡)
Redis系列学习文章分享---第十四篇(Redis多级缓存--封装Http请求+向tomcat发送http请求+根据商品id对tomcat集群负载均衡)
72 1
|
4月前
|
应用服务中间件
tomcat服务器get、post请求及响应中文乱码问题
tomcat服务器get、post请求及响应中文乱码问题
|
4月前
|
Java 应用服务中间件 API
Tomcat处理一个HTTP请求的执行流程的详细解析
Tomcat处理一个HTTP请求的执行流程的详细解析
107 4
|
3月前
|
应用服务中间件
tomcat8.5处理get请求时,控制台输出中文乱码问题的解决
tomcat8.5处理get请求时,控制台输出中文乱码问题的解决
45 0
|
前端开发 Java 应用服务中间件
TOMCAT 源码分析 -- 一次请求
TOMCAT 源码分析 -- 一次请求
95 0
|
2月前
|
网络协议 Java 应用服务中间件
tomcat配置域名及HTTPS
tomcat配置域名及HTTPS
|
2月前
|
Java 应用服务中间件 Windows
【应用服务 App Service】App Service 中部署Java项目,查看Tomcat配置及上传自定义版本
【应用服务 App Service】App Service 中部署Java项目,查看Tomcat配置及上传自定义版本