1. HttpServer类封装测试

目的:是否能够正常的收发HTTP报文,以及对接受到的HTTTP请求报文是否能够正确解析

1.1 调用如下

  1. void run()
  2. {
  3. http::HttpServer::ptr server(new http::HttpServer);
  4. auto addr = Address::LookUpAny("0.0.0.0:8888");
  5. while(!server->bind(addr))
  6. sleep(1);
  7. server->start();
  8. }
  9. int main(int argc, char *argv[])
  10. {
  11. IOManager iom("http server");
  12. iom.schedule(&run);
  13. return 0;
  14. }

BUG:HTTP请求报文 不支持分段解析

现象:在协程执行函数中抛出异常:basic_string::_M_create。可以判断是某一处string对象构造有问题。
image.png
image.png
image.png

尝试使用addr2line工具定位问题出现在哪一个文件的哪一个函数中

$ addr2line 0x7f86a6e17b55 -e ./bin/test_http_server -f
image.png
这种结果有些让人无语。。。。

尝试使用Linux下的coredump调试工具

参考出处:https://blog.csdn.net/byxdaz/article/details/90682397?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164315647216780274117227%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=164315647216780274117227&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-1-90682397.pc_search_result_cache&utm_term=ubuntu+codedump&spm=1018.2226.3001.4187

设置好能够生成core文件的条件后,GDB调试一下该文件:
$ gdb bin/test_http_server ./codefile/code-39233

可以清楚地看到由于接受报文不完整,产生报文的”割裂”导致在on_request_http_field()解析首部字段时出现问题,因此可以得出结论:之前引用项目中的HTTP报文解析的操作中并不支持分段解析,必须传入一个完整的报文才能正确解析。
image.png

解决:修改由.rl文件生成的.cpp对HTTP解析的代码

效果:只能让其不会因为报文分段而抛出异常,对于一些不能正常解析的首部字段将其进行抛弃,还是并不能真正的正确解析报文,仍然有一些瑕疵。

一般而言,只要服务器接收报文的缓存足够大应该不会出现这种分段现象。
image.png
image.png

疑惑点:以上修改之后报文仍然再出现断层现象

$ cat /proc/sys/net/ipv4/tcp_rmem
$ cat /proc/sys/net/ipv4/tcp_wmem
或者
$ cat /etc/sysctl.conf
惊奇发现,之前压测自己写的一个服务器框架没有把TCP收发缓冲区改回来,导致收发包一直在用很小的缓冲区在操作,就挺意外的……
image.png

修正:将TCP收发缓冲区改大一些

问题得到解决……
image.png

BUG:报文解析fragment未能正确解析

现象:利用POSTMAN软件,发送请求测试时,发现fragment部分未能解析出该部分
image.png

可能的原因:

  1. 请求报文已经发送但是没能解析出来
  2. POSTMAN软件自己将fragment这部分抹去没有加入请求报文

利用LInux telnet 尝试连接发送一下

发现能够发送fragment这一部分的内容,并且回发的响应报文中也包含该部分。

结论:POATMAN将该部分隐藏抹去了
image.png
image.png

2. Servlet + HttpServer联调

目的:测试新加的Servlet类是否正确有效,面对指定资源uri的请求访问,是否能做出对应的请求处理操作。

2.1 调用如下

  1. void run()
  2. {
  3. http::HttpServer::ptr server(new http::HttpServer);
  4. auto addr = Address::LookUpAny("0.0.0.0:8888");
  5. while(!server->bind(addr))
  6. sleep(1);
  7. //获取Servlet管理器对象
  8. auto sd = server->getServletDispatch();
  9. //添加一个精确匹配的 服务
  10. sd->addServlet("/kit/aaa", [](http::HttpRequest::ptr req,
  11. http::HttpResponse::ptr rsp,
  12. http::HttpSession::ptr s){
  13. //响应报文实体 置为请求报文
  14. rsp->setBody(req->toString());
  15. return 0;
  16. });
  17. //添加一个模糊匹配的 服务
  18. sd->addGlobServlet("/kit/*", [](http::HttpRequest::ptr req,
  19. http::HttpResponse::ptr rsp,
  20. http::HttpSession::ptr s){
  21. //响应报文实体 置为请求报文
  22. rsp->setBody("Glob:\r\n" + req->toString());
  23. return 0;
  24. });
  25. server->start();
  26. }
  27. int main(int argc, char *argv[])
  28. {
  29. IOManager iom("http server", 2);
  30. iom.schedule(&run);
  31. return 0;
  32. }

2.2 运行结果

分别观察使用不存在的资源、精准匹配的资源、模糊匹配的资源来测试,服务器针对这几类请求能否做出正确的响应。

  • 访问一个不存在的资源:http://192.168.77.136:8888/k

服务器在面对无法从精准匹配、模糊匹配找到的任何合法项时,会默认返回一项服务,通常是404页面
image.png

  • 访问一个精准匹配的资源:http://192.168.77.136:8888/kit/aaa

image.png

  • 访问一个模糊匹配的资源:http://192.168.77.136:8888/kit/aaa2

image.png

3. HttpConnection类封装测试

目的:测试由服务器主动发起连接,是否能够正常接收chunck/非chunck响应报文。

3.1 调用如下

  1. static Logger::ptr g_logger = KIT_LOG_NAME("connect");
  2. static Logger::ptr g_logger2 = KIT_LOG_ROOT();
  3. void run()
  4. {
  5. //添加一个往文件输出的日志器
  6. g_logger->addAppender(LogAppender::ptr(new FileLogAppender("./connection.txt")));
  7. //想该网站发起请求
  8. Address::ptr addr = Address::LookUpAny("www.sylar.top:80");
  9. KIT_LOG_DEBUG(g_logger) << "addr:" << *addr;
  10. if(!addr)
  11. {
  12. KIT_LOG_ERROR(g_logger) << "get addr error";
  13. return;
  14. }
  15. Socket::ptr sock = Socket::CreateTCP(addr);
  16. bool ret = sock->connect(addr);
  17. if(!ret)
  18. {
  19. KIT_LOG_ERROR(g_logger) << "connect " << *addr << "error";
  20. return;
  21. }
  22. http::HttpConnection::ptr con(new http::HttpConnection(sock));
  23. http::HttpRequest::ptr req(new http::HttpRequest);
  24. req->setHeader("host", "www.sylar.top");
  25. req->setPath("/blog/");
  26. KIT_LOG_INFO(g_logger) << "req:\n" << *req;
  27. con->sendRequest(req);
  28. auto rsp = con->recvResponse();
  29. if(!rsp)
  30. {
  31. KIT_LOG_ERROR(g_logger) << "recv response error";
  32. return;
  33. }
  34. KIT_LOG_INFO(g_logger) << "recv response:\n" << *rsp;
  35. KIT_LOG_INFO(g_logger2) << "body size:" << rsp->getBody().size();
  36. }
  37. int main()
  38. {
  39. IOManager iom("test connection", 2);
  40. iom.schedule(&run);
  41. return 0;
  42. }

BUG:只解析了一块chunck报文就解析失败

image.png

  • 查看httpclient_parser.rl文件中的解析规则,发现对于报文主体的长度而言换行符被算在主体长度中,将换行符算在首部长度中,应跳过\r\n才能正确解析。

image.png

  • 做chunck包的解析的时候,每一次做完一个chunck包的解析,需要对剩余的主体长度减2,即:减去\r\n的两个字符的长度。

image.png

3.2 运行结果

各个分块报文实体的长度之和:8032 + 8184 * 4 + 4639 = 45407 ,和最终合并后的报文主体的长度是相吻合的。
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png

4. Uri类封装测试

目的:测试输入一个uri字符串能否正确拆分解析为各个要素,然后又重新组装还原并打印显示

4.1 调用如下

  1. #include "../kit_server/uri.h"
  2. #include "../kit_server/Log.h"
  3. using namespace std;
  4. using namespace kit_server;
  5. static Logger::ptr g_logger = KIT_LOG_ROOT();
  6. int main()
  7. {
  8. KIT_LOG_DEBUG(g_logger) << "test begin";
  9. //传入一个uri字符串解析创建一个Uri类对象
  10. Uri::ptr uri = Uri::Create("http://www.baidu.com/test/uri?a=100&name=kit#fra");
  11. //根据已经解析好的信息, 根据uri中的host创建一个Adress类对象
  12. auto addr = uri->createAddress();
  13. KIT_LOG_INFO(g_logger) << "addr:\n" << *addr;
  14. KIT_LOG_INFO(g_logger) << "uri:\n" << uri->toString();
  15. KIT_LOG_DEBUG(g_logger) << "test end";
  16. return 0;
  17. }

BUG:uri解析失败返回了一个nullptr,造成段错误

GDB跟踪后发现应该是解析失败返回了一个nullptr,操作空指针导致的错误。
image.png
image.png