1. HttpServer类封装测试
目的:是否能够正常的收发HTTP报文,以及对接受到的HTTTP请求报文是否能够正确解析
1.1 调用如下
void run()
{
http::HttpServer::ptr server(new http::HttpServer);
auto addr = Address::LookUpAny("0.0.0.0:8888");
while(!server->bind(addr))
sleep(1);
server->start();
}
int main(int argc, char *argv[])
{
IOManager iom("http server");
iom.schedule(&run);
return 0;
}
BUG:HTTP请求报文 不支持分段解析
现象:在协程执行函数中抛出异常:basic_string::_M_create
。可以判断是某一处string
对象构造有问题。
尝试使用addr2line工具定位问题出现在哪一个文件的哪一个函数中
$ addr2line 0x7f86a6e17b55 -e ./bin/test_http_server -f
这种结果有些让人无语。。。。
尝试使用Linux下的coredump调试工具
设置好能够生成core文件的条件后,GDB调试一下该文件:$ gdb bin/test_http_server ./codefile/code-39233
可以清楚地看到由于接受报文不完整,产生报文的”割裂”导致在on_request_http_field()
解析首部字段时出现问题,因此可以得出结论:之前引用项目中的HTTP报文解析的操作中并不支持分段解析,必须传入一个完整的报文才能正确解析。
解决:修改由.rl文件生成的.cpp对HTTP解析的代码
效果:只能让其不会因为报文分段而抛出异常,对于一些不能正常解析的首部字段将其进行抛弃,还是并不能真正的正确解析报文,仍然有一些瑕疵。
一般而言,只要服务器接收报文的缓存足够大应该不会出现这种分段现象。
疑惑点:以上修改之后报文仍然再出现断层现象
$ cat /proc/sys/net/ipv4/tcp_rmem
$ cat /proc/sys/net/ipv4/tcp_wmem
或者$ cat /etc/sysctl.conf
惊奇发现,之前压测自己写的一个服务器框架没有把TCP收发缓冲区改回来,导致收发包一直在用很小的缓冲区在操作,就挺意外的……
修正:将TCP收发缓冲区改大一些
问题得到解决……
BUG:报文解析fragment未能正确解析
现象:利用POSTMAN软件,发送请求测试时,发现fragment
部分未能解析出该部分
可能的原因:
- 请求报文已经发送但是没能解析出来
- POSTMAN软件自己将
fragment
这部分抹去没有加入请求报文
利用LInux telnet 尝试连接发送一下
发现能够发送fragment
这一部分的内容,并且回发的响应报文中也包含该部分。
结论:POATMAN将该部分隐藏抹去了
2. Servlet + HttpServer联调
目的:测试新加的Servlet
类是否正确有效,面对指定资源uri
的请求访问,是否能做出对应的请求处理操作。
2.1 调用如下
void run()
{
http::HttpServer::ptr server(new http::HttpServer);
auto addr = Address::LookUpAny("0.0.0.0:8888");
while(!server->bind(addr))
sleep(1);
//获取Servlet管理器对象
auto sd = server->getServletDispatch();
//添加一个精确匹配的 服务
sd->addServlet("/kit/aaa", [](http::HttpRequest::ptr req,
http::HttpResponse::ptr rsp,
http::HttpSession::ptr s){
//响应报文实体 置为请求报文
rsp->setBody(req->toString());
return 0;
});
//添加一个模糊匹配的 服务
sd->addGlobServlet("/kit/*", [](http::HttpRequest::ptr req,
http::HttpResponse::ptr rsp,
http::HttpSession::ptr s){
//响应报文实体 置为请求报文
rsp->setBody("Glob:\r\n" + req->toString());
return 0;
});
server->start();
}
int main(int argc, char *argv[])
{
IOManager iom("http server", 2);
iom.schedule(&run);
return 0;
}
2.2 运行结果
分别观察使用不存在的资源、精准匹配的资源、模糊匹配的资源来测试,服务器针对这几类请求能否做出正确的响应。
- 访问一个不存在的资源:
http://192.168.77.136:8888/k
服务器在面对无法从精准匹配、模糊匹配找到的任何合法项时,会默认返回一项服务,通常是404页面
- 访问一个精准匹配的资源:
http://192.168.77.136:8888/kit/aaa
- 访问一个模糊匹配的资源:
http://192.168.77.136:8888/kit/aaa2
3. HttpConnection类封装测试
目的:测试由服务器主动发起连接,是否能够正常接收chunck/非chunck响应报文。
3.1 调用如下
static Logger::ptr g_logger = KIT_LOG_NAME("connect");
static Logger::ptr g_logger2 = KIT_LOG_ROOT();
void run()
{
//添加一个往文件输出的日志器
g_logger->addAppender(LogAppender::ptr(new FileLogAppender("./connection.txt")));
//想该网站发起请求
Address::ptr addr = Address::LookUpAny("www.sylar.top:80");
KIT_LOG_DEBUG(g_logger) << "addr:" << *addr;
if(!addr)
{
KIT_LOG_ERROR(g_logger) << "get addr error";
return;
}
Socket::ptr sock = Socket::CreateTCP(addr);
bool ret = sock->connect(addr);
if(!ret)
{
KIT_LOG_ERROR(g_logger) << "connect " << *addr << "error";
return;
}
http::HttpConnection::ptr con(new http::HttpConnection(sock));
http::HttpRequest::ptr req(new http::HttpRequest);
req->setHeader("host", "www.sylar.top");
req->setPath("/blog/");
KIT_LOG_INFO(g_logger) << "req:\n" << *req;
con->sendRequest(req);
auto rsp = con->recvResponse();
if(!rsp)
{
KIT_LOG_ERROR(g_logger) << "recv response error";
return;
}
KIT_LOG_INFO(g_logger) << "recv response:\n" << *rsp;
KIT_LOG_INFO(g_logger2) << "body size:" << rsp->getBody().size();
}
int main()
{
IOManager iom("test connection", 2);
iom.schedule(&run);
return 0;
}
BUG:只解析了一块chunck报文就解析失败
- 查看
httpclient_parser.rl
文件中的解析规则,发现对于报文主体的长度而言换行符被算在主体长度中,将换行符算在首部长度中,应跳过\r\n
才能正确解析。
- 做chunck包的解析的时候,每一次做完一个chunck包的解析,需要对剩余的主体长度减2,即:减去
\r\n
的两个字符的长度。
3.2 运行结果
各个分块报文实体的长度之和:8032 + 8184 * 4 + 4639 = 45407 ,和最终合并后的报文主体的长度是相吻合的。
4. Uri类封装测试
目的:测试输入一个uri字符串能否正确拆分解析为各个要素,然后又重新组装还原并打印显示
4.1 调用如下
#include "../kit_server/uri.h"
#include "../kit_server/Log.h"
using namespace std;
using namespace kit_server;
static Logger::ptr g_logger = KIT_LOG_ROOT();
int main()
{
KIT_LOG_DEBUG(g_logger) << "test begin";
//传入一个uri字符串解析创建一个Uri类对象
Uri::ptr uri = Uri::Create("http://www.baidu.com/test/uri?a=100&name=kit#fra");
//根据已经解析好的信息, 根据uri中的host创建一个Adress类对象
auto addr = uri->createAddress();
KIT_LOG_INFO(g_logger) << "addr:\n" << *addr;
KIT_LOG_INFO(g_logger) << "uri:\n" << uri->toString();
KIT_LOG_DEBUG(g_logger) << "test end";
return 0;
}
BUG:uri解析失败返回了一个nullptr
,造成段错误
GDB跟踪后发现应该是解析失败返回了一个nullptr
,操作空指针导致的错误。