解析HTTP报文
HTTP报文格式
一些状态描述符
HTTP请求方法,此项目只支持GET
enum METHOD {GET = 0, POST, HEAD, PUT, DELETE, TRACE, OPTIONS, CONNECT};
/*
解析客户端请求时,主状态机的状态
CHECK_STATE_REQUESTLINE:当前正在分析请求行
CHECK_STATE_HEADER:当前正在分析头部字段
CHECK_STATE_CONTENT:当前正在解析请求体
*/
enum CHECK_STATE { CHECK_STATE_REQUESTLINE = 0, CHECK_STATE_HEADER, CHECK_STATE_CONTENT };
/*
服务器处理HTTP请求的可能结果,报文解析的结果
NO_REQUEST : 请求不完整,需要继续读取客户数据
GET_REQUEST : 表示获得了一个完成的客户请求
BAD_REQUEST : 表示客户请求语法错误
NO_RESOURCE : 表示服务器没有资源
FORBIDDEN_REQUEST : 表示客户对资源没有足够的访问权限
FILE_REQUEST : 文件请求,获取文件成功
INTERNAL_ERROR : 表示服务器内部错误
CLOSED_CONNECTION : 表示客户端已经关闭连接了
*/
enum HTTP_CODE { NO_REQUEST, GET_REQUEST, BAD_REQUEST, NO_RESOURCE, FORBIDDEN_REQUEST, FILE_REQUEST, INTERNAL_ERROR, CLOSED_CONNECTION };
// 从状态机的三种可能状态,即行的读取状态,分别表示
// 1.读取到一个完整的行 2.行出错 3.行数据尚且不完整
enum LINE_STATUS { LINE_OK = 0, LINE_BAD, LINE_OPEN
状态机
在CHECK_STATE_REQUESTLINE、CHECK_STATE_HEADER和CHECK_STATE_CONTENT三中状态切换,一次去处理HTTP报文的请求行,请求头和请求体
//主状态机,解析请求
HTTP_CODE http_conn::process_read(){
LINE_STATUS line_status = LINE_OK;
HTTP_CODE ret = NO_REQUEST;
char* text = 0;
while (((m_check_state == CHECK_STATE_CONTENT) && (line_status == LINE_OK))
|| ((line_status = parse_line()) == LINE_OK)) {
//获取一行数据
text = get_line();
m_start_line = m_checked_idx;
switch (m_check_state)
{
case CHECK_STATE_REQUESTLINE:{
ret = parse_request_line(text);
if(ret == BAD_REQUEST){
return BAD_REQUEST;
}
break;
}
case CHECK_STATE_HEADER:{
ret = parse_headers(text);
if(ret == BAD_REQUEST){
return BAD_REQUEST;
}else if(ret == GET_REQUEST){
return do_request();
}
break;
}
case CHECK_STATE_CONTENT:{
ret = parse_content(text);
if(ret == BAD_REQUEST){
return BAD_REQUEST;
}else if(ret == GET_REQUEST){
解析HTTP请求报文的相关函数
解析请求行
通过解析请求行获取请求方法,目标URL,以及HTTP版本号
//解析HTTP请求行,获得请求方法,目标URL,以及HTTP版本号
HTTP_CODE http_conn::parse_request_line(char * text){
// GET /index.html HTTP/1.1
m_url = strpbrk(text, " \t");
if(!m_url){
return BAD_REQUEST;
}
*m_url = '\0';
m_url++;
char* method = text;
if(strcasecmp(method, "GET") == 0){
m_method = GET;
}else{
return BAD_REQUEST;
}
m_version = strpbrk(m_url, " \t");
if(!m_version){
return BAD_REQUEST;
}
*m_version = '\0';
m_version++;
if(strcasecmp(m_version, "HTTP/1.1") != 0){
return BAD_REQUEST;
}
if(strcasecmp(m_url,"http://", 7) == 0){
m_url += 7;
m_url = strchr(m_url, '/');
}
if(!m_url || m_url[0] != '/'){
return BAD_REQUEST;
}
m_check_state = CHECK_STATE_HEADER; //简称状态变成检查头
return NO_REQUEST;
}
解析请求头
//解析HTTP请求的一个头部信息
HTTP_CODE http_conn::parse_headers(char * text){
//遇到空行表示请求头解析完毕,检查是否有请求体
//如有还需转换到CHECK_STATE_CONTENT状态
if(text[0] == '\0'){
if(m_content_length != 0) {
m_check_state = CHECK_STATE_CONTENT;
return NO_REQUEST;
}
//否则说明我们已经得到了一个完整的HTTP请求
return GET_REQUEST;
}else if(strcasecmp(text, "Host:", 5) == 0) {
text += 5;
text += strspn(text, " \t");//strspn()检查text开头有多少个连续的字符是str2里的,返回数量
m_host = text;
}else if(strcasecmp(text, "Connection:", 11) == 0){
text += 11;
text += strspn(text, " \t");//strspn()检查text开头有多少个连续的字符是str2里的,返回数量
if(strcasecmp(text, "keep-alive") == 0) {
m_linger = true;
}
}else if(strcasecmp(text, "Connect-Length:", 15) == 0){
text += 15;
text += strspn(text, " \t");//strspn()检查text开头有多少个连续的字符是str2里的,返回数量
m_content_length = atol(text);
}else{
printf("oop! unkonw header %s\n", text);
}
return NO_REQUEST;
}
解析请求体
//解析HTTP请求的请求体
//我们没有真正解析HTTP请求的消息体
HTTP_CODE http_conn::parse_content(char * text){
if(m_read_idx >= (m_content_length + m_checked_idx)) {
text[m_content_length] = '\0';
return GET_REQUEST;
}
return NO_REQUEST;
}