HttpContext模块 --- http上下文模块

            

目录

模块设计思想

模块代码实现


模块设计思想

上下文模块是Http协议模块中最重要的一个模块,他需要控制请求处理的节奏,需要保存一个HttpRequest对象,后续关于这个连接的http的处理的信息全部都是在这个上下文中保存。

首先,上下文模块需要控制http请求接收和处理的节奏,那么我们其实是需要用一个变量来表明当前处于处理报文的哪一个阶段。可以用一个枚举量来作为处理的进度或者状态。

//处理状态
enum HttpRecvStatu{RECV_ERR,   //接收错误RECV_LINE,  //接收请求行RECV_HEAD,  //接收头部RECV_BODY,  //接收正文RECV_OVER   //接收完毕
};

同时,由于收到的http请求报文可能是会出错的,而出错的话,我们是不会将这个报文进行业务的处理的,而是直接返回一个请求错误的状态码的报文,那么我们的上下文当中不可避免的还需要保存一个变量用来保存状态码。

当然还需要保存一个HttpRequest对象用来存储获取到的请求的要素。

//请求处理上下文
class HttpContext
{
public:HttpRecvStatu _recv_statu;  //处理进度int _resp_statu;            //响应状态码HttpRequest _req;           //请求
public:HttpContext():_recv_statu(RECV_LINE),_resp_statu(200){}
};

接口分析

他提供给外部的接口其实很少,一个是获取响应状态码,一个是获取处理进度,还有就是获取到内部的Request对象,以及接收并解析http请求的接口,当然也还需要一个Reset接口,还是一样的,可能是长连接,后续还有报文需要处理。

    HttpContext():_recv_statu(RECV_LINE),_resp_statu(200){}void Reset();int RespStatu()const;HttpRecvStatu RecvStatu()const;HttpRequest& GetRequest();void RecvHttpRequest();

这其中最复杂的接口就是RecvHttpRequest也就是接收并解析Http请求的接口,这个接口我们需要使用多个接口进行辅助处理。

模块代码实现

首先实现前四个简单的接口:

void Reset(){_recv_statu = RECV_LINE;_resp_statu = 200;_req.Reset();}int RespStatu()const {return _resp_statu;}HttpRecvStatu RecvStatu()const {return _recv_statu;}HttpRequest& GetRequest() {return _req;}

然后就是最重要得接收数据得接口了。

我们先来把解析请求的几个小接口写出来。

首先,我们第一步需要解析请求行,要解析请求行首先需要获取请求行,那么我们就需要两个接口,一个是提取出请求行,一个是解析请求行。

解析请求行的时候我们使用的正则表达式是这个:

(GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?

在这个正则表达式的匹配结果中,如果我们的url中没有携带参数,那么参数部分的匹配结果就是一个空串,他也是在matches里面的,这一点我们不需要关心,因为后续我们解析参数的时候会将这种情况给他处理了。

//解析处理请求行void RecvLine(Buffer* buffer) {if(_recv_statu != RECV_LINE) return;//1 获取请求行std::string line = buffer->GetLineAndPop();if(line == "")  //没有获取到一行,此时需要判断是数据不够还是因为数据接受错误了{if(buffer->ReadSize() > MAX_LINE_SIZE)  //大于8192{_recv_statu = RECV_ERR;                 //说明解析错误,报文在接收的时候有问题_resp_statu = 414;                      //url too long}   return;}//2 解析请求行return HandlerLine(line);}void HandlerLine(const std::string& line){if(_recv_statu != RECV_LINE) return;//使用正则表达式进行解析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_ERR;_resp_statu = 400;      //Bad Requestreturn;}//走到这里说明正则匹配成功,而smacth是重载了方括号运算符的,我们可以直接使用_req._method = matches[1];//这里我们需要对方法进行处理,将其转换成大写,因为可能会有不标准的请求将方法写成小写。std::transform(_req._method.begin(),_req._method.end(),_req._method.begin(),::toupper);//前两个参数表示转换的数据的范围,第三个参数表示转换之后的目的地址,第四个参数表示转换的方法,使用C库的全局的toupper函数来转大写_req._path = Util::UrlDecode(matches[2],false);     //url需要进行解码,不需要+转空格std::string params = Util::UrlDecode(matches[3],true); //参数需要及逆行解码,需要+转空格//然后就是将参数解析为kv的格式std::vector<std::string> arr;Util::Split(params,"&",&arr);   //  参数以param进行分割for(auto&s:arr) //然后逐个提取每一个kv式的参数{  std::vector<std::string> kv;int ret = Util::Split(s,"=",&kv);if(ret != 2) //如果不是一个kv,那么就报错{_recv_statu = RECV_ERR;_resp_statu = 400;  //Bad Requestreturn;}//提取出来就放到参数的 map 中_req.AddParam(kv[0],kv[1]);}//最后就是提取版本号_req._version = matches[4];}   

然后就是获取解析头部字段

头部字段和正文之间的间隔就是一个 \r\n ,也就是说如果我们某一次提取一行内容,只提取到一个 \r\n 或者\n,那么说明这就是我们的空行了,头部字段提取完了。

//获取解析头部字段void RecvHeader(Buffer* buffer){if(_recv_statu != RECV_HEAD) return;//提取每一行while(1){//1 获取头部字段std::string line = buffer->GetLineAndPop();if(line == "")  //没有获取到一行,此时需要判断是数据不够还是因为数据接受错误了{if(buffer->ReadSize() > MAX_LINE_SIZE)  //大于8192{_recv_statu = RECV_ERR;                 //说明解析错误,报文在接收的时候有问题_resp_statu = 414;                      //url too long}   return;}//2 解析头部字段if(line == "\r\n" || line == "\n"){//头部字段解析完了_recv_statu = RECV_BODY;return;}//否则解析这一行bool ret = HandlerHeader(line);if(!ret) return;    //因为头部字段可能会有问题,需要使用一个返回值来判别}}bool HandlerHeader(std::string& line){if(_recv_statu != RECV_HEAD) return false; //先去掉回车和换行if(line.back() == '\n') line.pop_back();if(line.back() == '\r') line.pop_back();std::vector<std::string> kv;int ret = Util::Split(line,": ",&kv);if(ret != 2) {_recv_statu = RECV_ERR;_resp_statu = 400;      //Bad Requestreturn false;}_req.AddHeader(kv[0],kv[1]);return true;}

最后就是获取正文的接口,正文的获取其实也很简答。 如果请求中携带正文,那么头部字段中就一定会携带 Content-Length 字段,所以我们可以通过这个正文长度字段来决定我们要获取的正文的大小。 但是并不是说必须一次就把所有的正文全部接受,有可能当前缓冲区的数据并不是完整的正文,而是正文的一部分,这时候我们也要先接收,不能让他烂在缓冲区中。 那么未来我们需要根据_body.size 和正文长度这两个字段来判断从缓冲区中拿多少数据。

//获取正文void RecvBody(Buffer* buffer){if(_recv_statu != RECV_BODY) return;int len = _req.ContentLength();if(len ==0) {_recv_statu = RECV_OVER; //没有正文,直接返回return;}int size = len - _req._body.size(); //还需要接收的正文的长度if(size > buffer->ReadSize()) //说明不够,能读多少就读多少{_req._body.append(buffer->ReadPosition(),buffer->ReadSize());buffer->MoveReadOffset(buffer->ReadSize());return;}//走到这里说明能读完当前请求的正文_req._body.append(buffer->ReadPosition(),size);buffer->MoveReadOffset(size);_recv_statu = RECV_OVER;}

那么功能接口都写出来了,读取请求的接口无非就是调用上面的这些接口了,那么怎么设计呢?很简单,使用一个switch case 语句就完事了。

    void RecvHttpRequest(Buffer* buffer){switch(_recv_statu){case RECV_LINE: RecvLine(buffer);   //不需要break,因为可能提取完请求行之后还有后续的内容可以提取,就算请求行没提取完,每一个提取的函数前面有一个判断,可以直接返回case RECV_HEAD: RecvHeader(buffer);case RECV_BODY: RecvBody(buffer);}}

那么我们的上下文模块的接口就实现完了,目前我们也只进行编译,没有发现问题。

后续测试整个服务器的时候如果出现问题我们会再来修正bug。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

等保测评与风险管理:识别、评估和缓解潜在的安全威胁

在信息化时代&#xff0c;数据已成为企业最宝贵的资产之一&#xff0c;而信息安全则成为守护这份资产免受侵害的重中之重。等保测评&#xff08;信息安全等级保护测评&#xff09;作为保障信息系统安全的重要手段&#xff0c;其核心在于通过科学、规范、专业的评估手段&#xf…

autMan奥特曼机器人-内置容器安装依赖报错:externally-managed-environment

在 Manjaro 22、Ubuntu 23.04、Fedora 38 等最新的linux发行版中运行pip install时&#xff0c;通常会收到一个错误提示&#xff1a;error: externally-managed-environment&#xff0c;即“外部管理环境”错误&#xff0c;但这不是一个 bug。 $ sudo pip3 install please-cli…

word记录

1.段落 https://word-agent.zaixianjisuan.com/fangan/wordwen-dang-ru-he-zi-xing-fen-duan-luo.html 1.1 Word中自己分段落的方法 在Word文档中&#xff0c;合理地分段落可以使文档更加清晰易读。以下是几种在Word中自己分段落的方法。 使用回车键分段&#xff1a;最简单的…

【Android】Convenient ADB Commands

Install adb install -r <path>Uninstall adb uninstall <pkg>Start adb shell am start -n <pkg>/.SplashActivityStop adb shell am force-stop <pkg>Reset adb shell pm clear <pkg>Reboot adb rebootShutdown adb reboot -p

开发涉及的安全规范整理

文章目录 前言安全场景与措施API调用方式鉴权参数校验日志打印数据保存加密 总结 前言 这篇文章我们来整理下写代码和方案设计中的安全规范问题&#xff0c;内容偏服务端&#xff0c;即使是入门的新人&#xff0c;如果你对安全有所了解会让成熟规范的团队对你高看一眼。安全经常…

训练集alpaca、sharegpt格式

LLaMA-Factory微调支持的格式 支持 alpaca 格式和 sharegpt 格式的数据集。 Alpaca格式 格式: [{"instruction": "人类指令(必填)","input": "人类输入(选填)","output": "模型回答(必填)","syst…

LLaMA Factory环境配置

LLaMA-Factory官方文档 安装正确的torch和cuda版本 参考&#xff1a; PyTorch 报错解决 1.ImportError: /usr/lib/x86_64-linux-gnu/libstdc.so.6: version GLIBCXX_3.4.29 not found 参考这个解决&#xff1a;丝滑解决ImportError: /usr/lib/x86_64-linux-gnu/libstdc.s…

用HTML构建酷炫的文件上传下载界面

1. 基础HTML结构 首先&#xff0c;我们构建一个基本的HTML结构&#xff0c;包括一个表单用于文件上传&#xff0c;以及一个列表用于展示已上传文件&#xff1a; HTML <!DOCTYPE html> <html> <head><title>酷炫文件上传下载</title><link …

健康养生的重要性

养生之道&#xff0c;健康相随 在快节奏的现代生活中&#xff0c;养生健康已成为我们不可忽视的话题。随着生活水平的提高&#xff0c;人们越来越注重身体的保养与健康的维护。那么&#xff0c;如何才能做到养生健康&#xff0c;让身体与心灵都得到滋养呢&#xff1f; 首先&a…

鱼跃医疗助力退役军人事务部“高原情暖老兵项目”

10月17日-22日&#xff0c;在退役军人事务部指导下&#xff0c;中国老龄事业发展基金会联合腾讯SSV时光实验室、腾讯天籁实验室等机构发起的“情暖老兵&#xff0c;守望相助—老兵听力关怀计划”项目走进西藏&#xff0c;为退伍老兵提供听力健康筛查服务。西藏鱼跃医疗投资有限…

JSON Schema

简介 JSON Schema是一种用于描述 JSON 数据结构的规范&#xff0c;它提供了一种标准化的方式来定义JSON的数据格式、验证规则以及约束&#xff0c;使得开发者可以确保传递的数据符合预期的结构和格式。通常我们把它用来描述 OpenAPI 的入参和出参。 相关文档&#xff1a; htt…

我的电脑问题

联想小新Pro14外接显示器无法显示 问题描述&#xff1a; 联想小新 Pro14 电脑的外界显示器突然不好使了&#xff0c;使用以下办法成功解决 解决办法—重置EC 重置EC的方法&#xff1a; 关机后&#xff0c;移除电脑的外接电源以及u盘、鼠标等所有的外接设备&#xff1b; 在不接…

fastGpt

参考本地部署FastGPT使用在线大语言模型 1 rockylinx 1 ollama安装 在rockylinux中安装的&#xff0c;ollama由1.5G&#xff0c;还是比较大&#xff0c;所有采用在windows下下载&#xff0c;然后安装的方式&#xff0c;linux安装 tar -C /usr -xzf ollama-linux-amd64.tgz #…

U-net医学分割网络——学习笔记

《U-Net: Convolutional Networks for Biomedical Image Segmentation》 一、提出背景 U-Net 的提出是为了解决生物医学图像分割的几个关键问题&#xff1a;需要像素级的精确分割、标注数据稀缺、滑动窗口方法效率低以及多尺度特征融合的需求。U-Net 通过对称的 U 型全卷积结…

Redis+Lua限流的四种算法

1. 固定窗口&#xff08;Fixed Window&#xff09; 原理&#xff1a; 固定窗口算法将时间划分为固定的时间段&#xff08;窗口&#xff09;&#xff0c;比如 1 秒、1 分钟等。在每个时间段内&#xff0c;允许最多一定数量的请求。如果请求超出配额&#xff0c;则拒绝。 优点…

【linux网络编程】| 网络套接字socket | 初识网络开发

前言&#xff1a;本篇内容将要正式进入网络的编程当中。 本篇的目的是为了能够看完就可以上手写一些网络代码了。 但是本篇也并不会单纯的只讲接口&#xff0c; 前面还是会铺垫一些理论知识更好的认识网络传输。下面&#xff0c; 开始我们的学习吧! ps&#xff1a;本篇内容的某…

摄像头点击器常见问题——摄像头视窗打开慢

【嵌入式开发】可编程4k蓝牙摄像头点击器_能编程的摄像头-CSDN博客 拥有上述文章产品的朋友出现标题所述问题&#xff0c;可继续往下阅读 出现以上问题&#xff0c;摄像头画面打开较慢&#xff0c;可以按以下操作进行设置 在环境变量里设置一下这个参数&#xff0c;值设置为1&…

代码工艺:SQL 优化的细节

1. 巧用 limit 当出现深分页的时候&#xff0c;例如&#xff1a; select id, name, status, detail from product limit 100000, 30; 那么MySQL的执行方式为&#xff1a;一共需要查100030条数据&#xff0c;然后丢弃前面的100000条&#xff0c;只返回后面的30条数据&#xf…

美国超大型数据泄露事件曝光:超1亿人数据被盗

联合健康&#xff08;UnitedHealth&#xff09;首次证实&#xff0c;在 Change Healthcare 勒索软件攻击中&#xff0c;有超过 1 亿人的个人信息和医疗保健数据被盗&#xff0c;这是近年来最大的医疗保健数据泄露事件。 今年 5 月&#xff0c;UnitedHealth 首席执行官安德鲁-威…

深入理解gPTP时间同步过程

泛化精确时间协议(gPTP)是一个用于实现精确时间同步的协议,特别适用于分布式系统中需要高度协调的操作,比如汽车电子、工业自动化等。 gPTP通过同步主节点(Time Master)和从节点(Time Slave)的时钟,实现全局一致的时间参考。 以下是gPTP实现主从时间同步的详细过程:…