关于postman测试通过civetweb实现的HTTP接口报错Error: Parse Error: Expected HTTP/问题
创始人
2025-05-28 20:06:48
0

最近需要新增一个 REST API 的功能,然后使用到常用的开源库 civetweb 来实现这个功能

在这里插入图片描述

文章目录

      • 01 | 问题描述
      • 02 | 问题追踪
      • 03 | 问题原因
      • 04 | 问题解决
      • 05 | 总结

测试HTTP接口时,发现 postman 响应报错 ** Error: Parse Error: Expected HTTP/**

01 | 问题描述

使用 postman 测试使用开源库 civetweb 实现的 HTTP 接口时,postman 报错 Error: Parse Error: Expected HTTP/

在这里插入图片描述

02 | 问题追踪

错误信息 Error: Parse Error: Expected HTTP/有一个关键字:Parse,简要的说明了响应报文中存在解析校验错误的模块,那么现在问题就变成了找到报文中那里出错导致解析/校验失败

网络相关问题解决三步骤:

  1. 检查自己响应报文处代码
```cpp
void send_Http_cmd_error_rsp(struct mg_connection *conn)
{if (NULL == conn){return;}Json::Value root;root["errorCode"] = 400;root["errorMsg"] = "Bad Request";string resBody = root.toStyledString();int resLen = resBody.length();printf("resLen = [%d], resBody = %s\n", resLen, resBody.c_str());int ret = mg_printf(conn, "%s","HTTP/1.1 400 Bad Request\r\n""Cache-Control: no-cache\r\n""Content-Type: text/html;charset=utf-8\r\n");printf("ret = [%d]", ret);ret = mg_printf(conn, "Connection: %s\r\n", "keep-alive");printf("ret = [%d]", ret);ret = mg_printf(conn, "Content-Length: %d\r\n\r\n", resLen);printf("ret = [%d]", ret);ret = mg_printf(conn, "%s", resBody.c_str());printf("ret = [%d]", ret);
}
```检查中并没有发现函数用错、变量赋值错误、传值错误等问题,而且在每一次调用`mg_printf()`的时候我都把返回值打印了,并没有执行出错的情况,那么基本可以排除了代码逻辑问题
  1. Wireshark 抓包分析
这里通过 Wireshark 捕抓 ARM 和 PC 之间交互时的网络数据包![在这里插入图片描述](https://img-blog.csdnimg.cn/d18e200e3ba645b1a1f48bb77e2d67ea.png#pic_center)

在这里插入图片描述

从上面抓包获取到的图可以看到,交互时产生的数据包确实存在问题,HTTP对应的数据包尾缀`Malformed Packet`代表的意思是*畸形包(在这里畸形包就是不符合各层协议规范,无法被Wireshark解析的错误数据包)*,这里就开始找到异常现象了

在这里插入图片描述

通过进一步的追踪这个HTTP数据包,发现HTTP流层数据是我所发送的数据无误,但是TCP流层却多出了其他数据
  • 那么多出这坨东西,为什么会导致解析失败呢?

    回头看了下代码里我指定的 Context-Length字段长度是我发送数据的长度,那么现在多出来的数据就导致了实际长度比我预设的长度要大,所以这个数据包校验不通过,认为是不合法,是个畸形包

  • 为什么我没有发送这坨东西,但是它却真真实实的出现在了数据包里?

    因为一开始已经确定了自己并没有发送这坨多出来的数据,而且这坨数据显得十分的官方化,所以基本上确定这是源码本身自己处理发送的数据,所以问题最后就是找到这个多出来的数据是在源码哪个位置进行处理发送的

  1. 开源库源码解析
> 众所周知 HTTP 状态码 404 对应的错误消息就是 Not Found

因为这个错误出现的位置是在回复响应报文阶段,所以在查看源码解析的时候,可以大大的缩小范围,直接找 request_handler的文件进行搜索mg_printf() Not Found | Not found关键词来确定问题代码处。
civetweb.c文件搜索 Not Foundmg_printf()找到了内置的发送错误消息的函数mg_send_http_error()mg_send_http_error_impl()

	intmg_send_http_error(struct mg_connection *conn, int status, const char *fmt, ...){va_list ap;int ret;va_start(ap, fmt);ret = mg_send_http_error_impl(conn, status, fmt, ap);va_end(ap);return ret;}

mg_send_http_error_impl()函数最后找到了这个消息发送的代码

   /* HTTP responses 1xx, 204 and 304 MUST NOT send a body */if (has_body) {/* For other errors, send a generic error message. */const char *status_text = mg_get_response_code_text(conn, status);mg_printf(conn, "Error %d: %s\n", status, status_text);mg_write(conn, errmsg_buf, strlen(errmsg_buf));} else {/* No body allowed. Close the connection. */DEBUG_TRACE("Error %i", status);}

解析这个函数了解到,Error 404: Not Found 这个消息是通过传进来的状态码 404,调用mg_get_response_code_txt()获取到的,而 Not found 这个消息是通过外部传进来的,那么就可以查找调用最外层函数mg_send_http_error(conn, 404, "%s", "Not found)的地方所以确定了这多出来的消息是调用这个函数发送的,那么在它的最外层mg_send_http_error()函数进行查找在哪里设置了 404 Not found
在找到的三处地方,通过把 Not found 修改成其他消息,重新走一下分析过程,最终确定了调用位置是handle_request()函数处

在这里插入图片描述

   /* 11. File does not exist, or it was configured that it should be* hidden */if (!is_found || (must_hide_file(conn, path))) {mg_send_http_error(conn, 404, "%s", "fuck fuck2");return;}

我对上面这段代码的理解大概是

> 如果响应消息中需要的配置信息不存在或者需要被隐藏的情况下,会触发自动发送 404 Not found 的动作

03 | 问题原因

综上所述:

  1. 问题表面上是效应报文中实际数据长度大于设定的Context-Length值,导致数据包错误

  2. 问题实际上是在执行我设定的mg_printf()之前,civetweb 本身就已经在 handle_request()处做了响应处理,这时候自然识别到的报文是没有任何响应头字段的,所以触发了上面自动发送 404 Not Found 的动作

04 | 问题解决

  • 如果仅针对表面现象

    可以直接修改响应头中的 Content-Length 值,使其足够大即可,实验证明这个方法确实可以(网上其他碰到类似问题的解决方法也都采用的是这个),但是如果下次触发的是其他奇奇怪怪的动作,这个值还是不够大呢?

    所以还是得从根源出发,解决问题

  • 根源解决

    既然我调用 mg_printf()处理完成前, civetweb 本身的 handle_request()就已经开始运作了,那么只要确保*等我执行完mg_printf()之后再让handle_request()进行运作就好

    处理方式就是在代码的最后加上一定的延时,确保我的消息已经完成了即可,我这里设置了10秒的延时

    void send_Http_cmd_error_rsp(struct mg_connection *conn)
    {if (NULL == conn){return;}Json::Value root;root["errorCode"] = 400;root["errorMsg"] = "Bad Request";string resBody = root.toStyledString();int resLen = resBody.length();printf("resLen = [%d], resBody = %s\n", resLen, resBody.c_str());int ret = mg_printf(conn, "%s","HTTP/1.1 400 Bad Request\r\n""Cache-Control: no-cache\r\n""Content-Type: text/html;charset=utf-8\r\n");printf("ret = [%d]", ret);ret = mg_printf(conn, "Connection: %s\r\n", "keep-alive");printf("ret = [%d]", ret);ret = mg_printf(conn, "Content-Length: %d\r\n\r\n", resLen);priintf("ret = [%d]", ret);ret = mg_printf(conn, "%s", resBody.c_str());printf("ret = [%d]", ret);sleep(10);
    }
    

05 | 总结

好好干,总得练习两年半

在这里插入图片描述

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
JAVA多线程知识整理 Java多线程基础 线程的创建和启动 继承Thread类来创建并启动 自定义Thread类的子类&#...
【洛谷 P1090】[NOIP... [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G ...
国民技术LPUART介绍 低功耗通用异步接收器(LPUART) 简介 低功耗通用异步收发器...
城乡供水一体化平台-助力乡村振... 城乡供水一体化管理系统建设方案 城乡供水一体化管理系统是运用云计算、大数据等信息化手段࿰...
程序的循环结构和random库...   第三个参数就是步长     引入文件时记得指明字符格式,否则读入不了 ...
中国版ChatGPT在哪些方面... 目录 一、中国巨大的市场需求 二、中国企业加速创新 三、中国的人工智能发展 四、企业愿景的推进 五、...
报名开启 | 共赴一场 Flu... 2023 年 1 月 25 日,Flutter Forward 大会在肯尼亚首都内罗毕...
汇编00-MASM 和 Vis... Qt源码解析 索引 汇编逆向--- MASM 和 Visual Studio入门 前提知识ÿ...
【简陋Web应用3】实现人脸比... 文章目录🍉 前情提要🌷 效果演示🥝 实现过程1. u...
前缀和与对数器与二分法 1. 前缀和 假设有一个数组,我们想大量频繁的去访问L到R这个区间的和,...
windows安装JDK步骤 一、 下载JDK安装包 下载地址:https://www.oracle.com/jav...
分治法实现合并排序(归并排序)... 🎊【数据结构与算法】专题正在持续更新中,各种数据结构的创建原理与运用✨...
在linux上安装配置node... 目录前言1,关于nodejs2,配置环境变量3,总结 前言...
Linux学习之端口、网络协议... 端口:设备与外界通讯交流的出口 网络协议:   网络协议是指计算机通信网...
Linux内核进程管理并发同步... 并发同步并发 是指在某一时间段内能够处理多个任务的能力,而 并行 是指同一时间能够处理...
opencv学习-HOG LO... 目录1. HOG(Histogram of Oriented Gradients,方向梯度直方图)1...
EEG微状态的功能意义 导读大脑的瞬时全局功能状态反映在其电场结构上。聚类分析方法一致地提取了四种头表面脑电场结构ÿ...
【Unity 手写PBR】Bu... 写在前面 前期积累: GAMES101作业7提高-实现微表面模型你需要了解的知识 【技...