HttpContext请求接收上下文模块设计与实现(http模块四)

目录

类功能

类定义

类实现

编译测试


类功能

类定义

// HttpContext接收请求上下文模块功能设计
typedef enum
{
    RECV_HTTP_ERROR,
    RECV_HTTP_LINE,
    RECV_HTTP_HEAD,
    RECV_HTTP_BODY,
    RECV_HTTP_OVER
} HttpRecvStatu;

class HttpContext
{
private:
    int _resp_statu;           // 响应状态码
    HttpRecvStatu _recv_statu; // 当前接收及解析的阶段状态
    HttpRequest _request;      // 已经解析得到的请求信息
private:
    bool ParseHttpLine(); // 解析请求行
    bool RecvHttpLine();
    bool RecvHttpHead();
    bool ParseHttpHead();
    bool RecvHttpBody();

public:
    HttpContext();
    int RespStatu();
    HttpRecvStatu RecvStatu();
    HttpRequest &Request();
    void RecvHttpRequest(); // 接收并解析HTTP请求
};

类实现

// HttpContext接收请求上下文模块功能设计
typedef enum
{
    RECV_HTTP_ERROR,
    RECV_HTTP_LINE,
    RECV_HTTP_HEAD,
    RECV_HTTP_BODY,
    RECV_HTTP_OVER
} HttpRecvStatu;

#define MAX_LINE 8192 // 8K
class HttpContext
{
private:
    int _resp_statu;           // 响应状态码
    HttpRecvStatu _recv_statu; // 当前接收及解析的阶段状态
    HttpRequest _request;      // 已经解析得到的请求信息
private:
    // 解析请求行
    bool ParseHttpLine(const std::string &line)
    {
        std::smatch matches;
        std::regex e("(GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?");
        bool ret = std::regex_match(line, matches, e);
        if (ret == false)
        {
            _recv_statu = RECV_HTTP_ERROR;
            _resp_statu = 400; // BAD REQUEST
            return false;
        }
        // 0 : GET /qingfeng/login?user=xiaoming&pass=123123 HTTP/1.1
        // 1 : GET
        // 2 : /qingfeng/login
        // 3 : user=xiaoming&pass=123123
        // 4 : HTTP/1.1
        // 请求方法的获取
        _request._method = matches[1];
        // 资源路径的获取,需要进行URL解码操作,但是不需要+转空格
        _request._path = Util::UrlDecode(matches[2], false);
        // 协议版本的获取
        _request._version = matches[4];
        // 查询字符串的获取与处理
        std::vector<std::string> query_string_arry;
        std::string query_string = matches[3];
        // 查询字符串的格式, key=val&key=val......, 先以 & 符号进行分割,得到各个子串
        Util::Split(query_string, "&", &query_string_arry);
        // 针对各个子串,以 = 符号进行分割,得到 key 和 val, 得到之后也需要进行URL解码
        for (auto &str : query_string_arry)
        {
            size_t pos = str.find("=");
            if (pos == std::string::npos)
            {
                _recv_statu = RECV_HTTP_ERROR;
                _resp_statu = 400; // BAD REQUEST
                return false;
            }
            std::string key = Util::UrlDecode(str.substr(0, pos), true);
            std::string val = Util::UrlDecode(str.substr(pos + 1), true);
            _request.SetParam(key, val);
        }
        return true;
    }
    // 获取请求行
    bool RecvHttpLine(Buffer *buf)
    {
        if (_recv_statu != RECV_HTTP_LINE)
            return false;
        // 1. 获取一行数据
        std::string line = buf->GetLineAndPop();
        // 2. 需要考虑的一些要素,缓冲区的数据不足一行, 获取的一行数据超大
        if (line.size() == 0)
        {
            // 缓冲区中的数据不足一行,则需要判断缓冲区的可读数据长度,如果很长了都不足一行,这是有问题的
            if (buf->ReadAbleSize() > MAX_LINE)
            {
                _recv_statu = RECV_HTTP_ERROR;
                _resp_statu = 414; // URI TOO LONG 查看statu文件中可以看到414状态对应的信息
                return false;
            }
            // 缓冲区中数据不足一行,但是也不多,就等待新数据的到来
            return true;
        }
        if (line.size() > MAX_LINE)
        {
            _recv_statu = RECV_HTTP_ERROR;
            _resp_statu = 414; // URI TOO LONG
            return false;
        }
        bool ret = ParseHttpLine(line);
        if (ret == false)
            return false;
        // 首行处理完毕,进入头部处理阶段
        _recv_statu == RECV_HTTP_HEAD;
        return true;
    }

    bool RecvHttpHead(Buffer *buf)
    {
        // 状态不对直接返回
        if (_recv_statu != RECV_HTTP_HEAD)
            return false;
        // 一行一行取出数据,直到遇到空行为止,头部的格式 key: val\r\nkey: val\r\n......
        while (1)
        {
            std::string line = buf->GetLineAndPop();
            // 2. 需要考虑的一些要素,缓冲区的数据不足一行, 获取的一行数据超大
            if (line.size() == 0)
            {
                // 缓冲区中的数据不足一行,则需要判断缓冲区的可读数据长度,如果很长了都不足一行,这是有问题的
                if (buf->ReadAbleSize() > MAX_LINE)
                {
                    _recv_statu = RECV_HTTP_ERROR;
                    _resp_statu = 414; // URI TOO LONG 查看statu文件中可以看到414状态对应的信息
                    return false;
                }
                // 缓冲区中数据不足一行,但是也不多,就等待新数据的到来
                return true;
            }
            if (line.size() > MAX_LINE)
            {
                _recv_statu = RECV_HTTP_ERROR;
                _resp_statu = 414; // URI TOO LONG
                return false;
            }
            if (line == "\n" || line == "\r\n")
                break;
            bool ret = ParseHttpHead(line);
            if (ret == false)
                return false;
        }
        // 头部处理完毕,进入正文处理阶段
        _recv_statu == RECV_HTTP_BODY;
        return true;
    }
    bool ParseHttpHead(const std::string &line)
    {
        // key: val\r\nkey: val\r\n......
        size_t pos = line.find(": ");
        if (pos == std::string::npos)
        {
            if (pos == std::string::npos)
            {
                _recv_statu = RECV_HTTP_ERROR;
                _resp_statu = 400; // BAD REQUEST
                return false;
            }
            std::string key = line.substr(0, pos);
            std::string val = line.substr(pos + 2);
            _request.SetHeader(key, val);
        }
    }
    bool RecvHttpBody(Buffer *buf)
    {
        if (_recv_statu != RECV_HTTP_BODY)
            return false;
        // 1. 获取正文长度
        size_t content_length = _request.ContentLength();
        if (content_length == 0)
        {
            // 没有正文,则请求接收解析完毕
            _recv_statu = RECV_HTTP_OVER;
            return true;
        }
        // 2. 当前已经接收了多少正文 取决于_request._body, 其中放了多少数据
        size_t real_len = content_length - _request._body.size(); // 实际还需要接收的正文长度
        // 3. 接收正文放到body中,但是也要考虑当前缓冲区中的数据,是否是全部的正文
        //  3.1 缓冲区中的数据,包含了当前请求的所有正文,则取出所需的数据
        if (buf->ReadAbleSize() >= real_len)
        {
            _request._body.append(buf->ReadPosition(), real_len);
            buf->MoveReadOffset(real_len);
            _recv_statu = RECV_HTTP_OVER;
            return true;
        }
        //  3.2 缓冲区中的数据,无法满足当前正文的需要,数据不足,取出数据,然后等待新数据到来
        _request._body.append(buf->ReadPosition(), buf->ReadAbleSize());
        buf->MoveReadOffset(buf->ReadAbleSize());
        return true;
    }

public:
    HttpContext() : _resp_statu(200), _recv_statu(RECV_HTTP_LINE) {}
    void ReSet()
    {
        _resp_statu = 200;
        _recv_statu = RECV_HTTP_LINE;
        _request.ReSet();
    }
    int RespStatu() { return _resp_statu; }
    HttpRecvStatu RecvStatu() { return _recv_statu; }
    HttpRequest &Request() { return _request; }
    // 接收并解析HTTP请求
    void RecvHttpRequest(Buffer *buf)
    {
        // 不同的状态,做不同的事情,但是这里不要break,因为处理完请求行后,应该立即处理头部,而不是退出等待新数据的到来
        // 后面并不需要break,因为要保证后续都解析完,并且不用担心接收失败
        // 在各个函数的开头就检查了处理阶段,如果不对就会错误返回了
        switch (_recv_statu)
        {
        case RECV_HTTP_LINE:
            RecvHttpLine(buf);
        case RECV_HTTP_HEAD:
            RecvHttpHead(buf);
        case RECV_HTTP_BODY:
            RecvHttpBody(buf);
        }
        return;
    }
};

编译测试

关于正则表达式的使用可以参考之前的文章

正则表达式的使用-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/455053.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Games101笔记-变换

Scale Reflection Shear Rotate 没有额外提示默认绕原点旋转 线性变换 Transiation 不属于线性变换&#xff0c;仿射变换 齐次坐标 二维的点和向量增加一个维度 点加点等于两个点的中点 所有的仿射变换都可以写成齐次坐标的形式 在表示二维情况下的仿射变换时&#…

Linux驱动分离与分层的简介

一. 简介 我们在前面几章编写的设备驱动都非常的简单&#xff0c;都是对 IO 进行最简单的读写操作。 像 I2C 、SPI 、 LCD 等这些复杂外设的驱动就不能这么去写了&#xff0c; Linux 系统要考虑到驱动的可重用性&#xff0c;因 此&#xff0c;提出了驱动的分离与分层这样的软…

Maven: There are test failures.(已解决)

问题解决办法 进行package打包时报错如下&#xff1a; 然后这些并不能看出是测试的哪里的问题&#xff0c;可以点击上一级进行查看更详细的错误&#xff0c;越向上日志越详细&#xff0c;可以看到是52行出了错误&#xff0c; 52对应代码如下&#xff1a; 原因是存在注册的测…

基于FPGA的图像锐化算法(USM)设计

免费获取源码请关注微信号《FPGA学习笔记册》&#xff01; 1.图像锐化算法说明 图像锐化算法在实际的图像处理应用很广泛&#xff0c;例如&#xff1a;医学成像、工业检测和军事领域等&#xff1b;它的作用就是将模糊的图像变的更加清晰。常用的图像锐化算法有拉普拉斯算子、s…

记录一下在Pycharm中虚拟环境的创建

如果在Pycharm中要新建一个虚拟环境&#xff0c;那你可以在Terminal中选择Command Prompt&#xff0c;在这里面执行相关命令 一、安装了Anaconda&#xff0c;创建虚拟环境 当你使用解释器是Anaconda提供的时&#xff0c;你可以使用conda命令执行&#xff0c;见以下操作&#x…

Acwing.4261 孤独的照片(贡献法)

题目 Farmer John 最近购入了 N 头新的奶牛&#xff0c;每头奶牛的品种是更赛牛&#xff08;Guernsey&#xff09;或荷斯坦牛&#xff08;Holstein&#xff09;之一。 奶牛目前排成一排&#xff0c;Farmer John 想要为每个连续不少于三头奶牛的序列拍摄一张照片。 然而&…

华为组网:核心交换机旁挂防火墙,基于ACL重定向配置实验

如图所示&#xff0c;由于业务需要&#xff0c;用户有访问Internet的需求。 用户通过接入层交换机SwitchB和核心层交换机SwitchA以及接入网关Router与Internet进行通信。为了保证数据和网络的安全性&#xff0c;用户希望保证Internet到服务器全部流量的安全性&#xff0c;配置重…

Flask开发类似jenkins构建自动化测试任务工具

1、自动化 某一天你入职了一家高大上的科技公司&#xff0c;开心的做着软件测试的工作&#xff0c;每天点点点&#xff0c;下班就走&#xff0c;晚上陪女朋友玩王者&#xff0c;生活很惬意。 但是美好时光一般不长&#xff0c;这种生活很快被女主管打破。为了提升公司测试效率…

如何“使用Docker快速安装Jenkins,在CentOS7”?

1、运行 docker run -d --namejenkins -p 8080:8080 jenkins/jenkins 2、查看日志 &#xff0c;使用 "docker logs -f jenkins",可以持续刷新日志 docker logs jenkins 3、通过命令查看密码 docker exec -it jenkins cat /var/jenkins_home/secrets/initialAdminP…

用云服务器构建gpt和stable-diffusion大模型

用云服务器构建gpt和stable-diffusion大模型 一、前置知识二、用云端属于自己的聊天chatGLM3step1、项目配置step2、环境配置1、前置知识2、环境配置流程 step3、创建镜像1、前置知识2、创建镜像流程 step4、通过 Gradio 创建ChatGLM交互界面1、前置知识2、创建ChatGLM交互界面…

YOLOv8改进 | 图像去雾 | 特征融合注意网络FFA-Net增强YOLOv8对于模糊图片检测能力(北大和北航联合提出)

一、本文介绍 本文给大家带来的改进机制是由北大和北航联合提出的FFA-net: Feature Fusion Attention Network for Single Image Dehazing图像增强去雾网络&#xff0c;该网络的主要思想是利用特征融合注意力网络&#xff08;Feature Fusion Attention Network&#xff09;直接…

基于单片机的Buck型变换器控制

摘要&#xff1a;对于电子产品而言&#xff0c;必不可少的供电电源&#xff0c;随着人们对电子产品的安全性能要求越来越高&#xff0c;变相的对供电电源提出了新的机遇和挑战。Buck型变换器控制的研究一直是该领域重要的一方面&#xff0c;对于直流斩波电路而言&#xff0c;研…

C#在未安装Halcon环境中调用Halcon的方法

1.1 找到Halcon的dll 将Halcon安装路径下的所有dll复制进一个文件夹内 1.2 放入程序目录下 1.3 设置程序引用目录文件 在App.config中添加如下代码 <runtime><assemblyBinding xmlns"urn:schemas-microsoft-com:asm.v1"><probing privatePath"H…

前端开发小技巧【Vue篇】 - 样式穿透 + 绑定变量

前言 样式穿透 Vue都是通过深度选择器来样式穿透的。当我们在写项目的时候&#xff0c;经常会导入第三方库&#xff0c;有些特殊的情况&#xff0c;就是在导入第三方库后&#xff0c;呈现的样式并不是我们想要的样式&#xff0c;所以我们需要对第三方的样式进行修改&#xff1…

JVM简单调优

jdk自带了许多对jvm进行监控的程序&#xff0c;例如JVisualVM、jstack等等。 现在进行一些简单的对jvm的监控。 我们可以使用JVisualVM来对堆区进行图形化监控。 我们可以在命令行输入jvisualvm&#xff0c;然后就进入了jvisualvm的图形化界面。 然后我们随便执行一个主方法…

选型|匠芯创工业级显示控制MCU

D13x系列微控制器 匠芯创D13x系列是一款基于RISC-V架构的高性能、国产自主、工业级跨界MCU&#xff0c;配备强大的2D图形加速、PNG解码、JPEG编解码引擎&#xff0c;具有丰富的屏接口&#xff0c;具有工业宽温、高可靠性、高开放性&#xff0c;可广泛应用于工业HMI、网关、串口…

20240313寻找集成联调交付的具体方式

集成联调交付&#xff08;Integrated Joint Debugging and Delivery&#xff09;是软件开发过程中的一个阶段&#xff0c;主要涉及将不同的软件模块或组件整合在一起&#xff0c;并进行联合调试和测试&#xff0c;以确保它们能够作为一个整体正常工作。这个过程通常发生在开发周…

ASFF自适应空间特征融合

paper&#xff1a;Learning Spatial Fusion for Single-Shot Object Detection official implementation&#xff1a;https://github.com/GOATmessi7/ASFF 背景 金字塔特征表示pyramid feature representation是解决目标检测中尺度变化挑战的常用方法。特征金字塔的一个主要…

Spring Cloud项目整合Sentinel及简单使用

说明&#xff1a;Sentinel是阿里巴巴开发的微服务治理中间件&#xff0c;可用于微服之间请求的流量管控、权限控制、熔断降级等场景。本文介绍如何在Spring Cloud项目中整合Sentinel&#xff0c;以及Sentinel的简单使用。 环境 首先搭建一个简单的微服务环境&#xff0c;有以…

Linux下的编辑器——Vim

vi/vim 的区别简单点来说&#xff0c;它们都是多模式编辑器&#xff0c;不同的是 vim 是 vi 的升级版本&#xff0c;它不仅兼容 vi 的所有指令&#xff0c;而且还有一些新的特性在里面。例如语法加亮&#xff0c;可视化操作不仅可以在终端运行&#xff0c;也可以运行于x window…