用有限状态机处理http请求

#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
//主状态机有两种状态,当前正在分析请求行,当前正在分析头部字段
enum CHECK_STATE{CHECK_STATE_REQUESTLINE = 0,CHECK_STATE_HEADER};
//从状态机三种状态,读取完整一行,行出错,行数据读取不完整
enum LINE_STATUS{ LINE_OK = 0,LINE_BAD,LINE_OPEN};
/*服务器处理结果:NO_REQUEST表示请求不完整,需要继续读取客户数据;GET_REQUEST表示获得了一个完整的客户端请求;
BAD_REQUEST表示客户请求有语法错误;FORBIDDEN_REQUEST表示客户对资源没有足够的访问权限;INTERNAL_ERROR表示服务器内部错误;
 CLOSE_CONNECTION表示客户端已关闭连接*/
enum HTTP_CODE{NO_REQUEST,GET_REQUEST,BAD_REQUEST,FORBIDDEN_REQUEST,INTERNAL_ERROR,CLOSED_CONNECTION};
//从状态机解析出一行的内容,buf指向缓冲区,check_index指向当前正分析的字节,read_index指向缓冲区数据尾部的下一个字节
LINE_STATUS parse_line(char* buf,int& check_index,int& read_index)
{
	char temp;
	for(;check_index < read_index;check_index++)
	{
		//获得当前要分析的字节
		temp = buf[check_index];
		if(temp == '\r')
		{
			if((check_index + 1) == read_index)
			{
				return LINE_OPEN;
			}
			else if(buf[check_index + 1] == '\n')
			{
				//将\r\n转换为\0方便后续处理
				buf[check_index++] = '\0';
				buf[check_index++] = '\0';
				return LINE_OK;
			}
			return LINE_BAD;
		}
		else if(temp == '\n')
		{
			if((check_index > 0) && buf[check_index - 1] == '\r')
			{
				//将\r\n转换为\0方便后续处理
				buf[check_index - 1] = '\0';
				buf[check_index++] = '\0';
				return LINE_OK;
			}
			return LINE_BAD;
		}
	}
	return LINE_OPEN;
}
//分析请求行
HTTP_CODE parse_requestline(char *temp,CHECK_STATE& checkstate)
{
	char *url = strpbrk(temp," \t");//返回temp字符串中第一个出现空白字符或者'/t'字符的位置
	if(!url)//若请求行中没有空白字符或者'/t'字符,则http请求必有问题
	{
		return BAD_REQUEST;
	}
	*url++ = '\0';

	//分析请求方法是否为GET方法
	char *p_1 = temp;
	if(strcasecmp(p_1,"GET") == 0)//忽略大小写比较字符串
	{
		printf("The request method is GET\n");
	}
	else return BAD_REQUEST;

	url += strspn(url," \t");//清除前面多余的空格
	char *p_2 = strpbrk(url," \t");
	if(!p_2)
	{
		return BAD_REQUEST;
	}
	*p_2++ = '\0';

	//分析协议版本字段的正确性
	p_2 += strspn(p_2," \t");
	if(strcasecmp(p_2,"HTTP/1.1") == 0)
	{
		printf("协议版本为:%s\n",p_2);
	}
	else return BAD_REQUEST;

	//分析url的正确性
	if(strncasecmp(url,"http://",7) == 0)
	{
		url += 7;
		url = strchr(url,'/');
	}
	if(!url || url[0] != '/')//若http://后面没有/字符或者url前七个字节不是"http://",则语法错误
	{
		return BAD_REQUEST;
	}
	printf("The request URL is: %s\n",url);

	//HTTP请求行处理完毕,状态转移到头部字段进行分析
	checkstate = CHECK_STATE_HEADER;
	return NO_REQUEST;
}
//分析头部字段
HTTP_CODE parse_headers(char *temp)
{
	if(temp[0] == '\0')//若出现空行,则得到了一个正确的http请求
		return GET_REQUEST;
	else if(strncasecmp(temp,"Host:",5) == 0)//处理host头部字段
	{
		temp += 5;
		temp += strspn(temp," \t");
		printf("the request host is: %s\n",temp);
	}
	else{//不处理其他头部字段
		printf("I can not handle this header\n");
	}
	return NO_REQUEST;
}
/*分析http请求入口函数*/
HTTP_CODE parse_content(char *buf,int &check_index,int &read_index,CHECK_STATE& checkstate,int& start_line)
{
	LINE_STATUS linestatus = LINE_OK;	//当前行的读取状态
	HTTP_CODE httpcode;					//http请求的处理状态
	
	while( (linestatus = parse_line(buf,check_index,read_index)) == LINE_OK)	//读取一行数据
	{
		char *temp = buf + start_line;	//定位行在buf中的位置
		start_line = check_index;		//更新行的位置
		switch(checkstate)				
		{
			case CHECK_STATE_REQUESTLINE:	//第一个状态分析请求行
			{
				httpcode = parse_requestline(temp,checkstate);
				if(httpcode == BAD_REQUEST)
					return BAD_REQUEST;
				break;
			}
			case CHECK_STATE_HEADER:		//第二个状态,分析头部字段
			{
				httpcode = parse_headers(temp);
				if(httpcode == GET_REQUEST)
				{
					return GET_REQUEST;
				}
				break;
			}
			default:
			{
				return INTERNAL_ERROR;
			}
		}
	}
	//若没有读取到一个完整的行,则表示还需要继续读取客户端数据才能进一步分析
	if(linestatus == LINE_OPEN)
	{
		return NO_REQUEST;
	}
	else{
		return BAD_REQUEST;
	}
}
int main(int argc,char *argv[])
{
	if(argc < 3)
	{
		printf("usage: %s ip port\n",argv[0]);
		exit(1);
	}
	char* ip = argv[1];
	int port = atoi(argv[2]);

	int listenfd,clifd;
	struct sockaddr_in ser_addr,cli_addr;
	socklen_t cli_len = sizeof(cli_addr);

	listenfd = socket(AF_INET,SOCK_STREAM,0);

	ser_addr.sin_family = AF_INET;
	ser_addr.sin_port = htons(port);
	inet_pton(AF_INET,ip,&ser_addr.sin_addr);
	bind(listenfd,(struct sockaddr*)&ser_addr,sizeof(ser_addr));

	listen(listenfd,3);
	clifd = accept(listenfd,(struct sockaddr*)&cli_addr,&cli_len);
	if(clifd == -1)
	{
		perror("accept error:");
		exit(1);
	}

	char buf[BUFSIZ];
	int read_num = 0;
	int read_index = 0;//当前已经读入多少字节的客户数据
	int check_index = 0;//当前已经分析完了多少字节的客户数据
	int start_line = 0;//行在buf中的起始位置
	CHECK_STATE checkstate = CHECK_STATE_REQUESTLINE;//设置主状态机的初始状态
	while(1)//循环读取数据并分析
	{
		read_num =read(clifd,buf+read_index,BUFSIZ-read_index);
		if(read_num == -1)
		{
			perror("read error:");
			break;
		}
		else if(read_num == 0)
		{
			printf("remote client has closed the connection\n");
			break;
		}
		read_index += read_num;

		//分析目前已经获得的所有数据
		HTTP_CODE httpcode = parse_content(buf,check_index,read_index,checkstate,start_line);
		if(httpcode == NO_REQUEST)		//没有获得完整的数据
		{
			continue;
		}
		else if(httpcode == GET_REQUEST)//得到一个完整的、正确的HTTP请求
		{
			printf("we get a GET_REQUEST\n");
			break;
		}
		else{							//其他情况为发生错误
			printf("we get a bad_request\n");
			break;
		}
	}
	close(clifd);
	close(listenfd);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章