高性能服务编程:有限状态机解析HTTP请求代码
相关函数:
//该函数返回str1中第一个匹配字符串str2的字符数,未找到返回null。 char *strpbrk(const char *str1, const char *str2)
/* 若参数s1 和s2 字符串相同则返回0。s1 长度大于s2 长度则返回大于0 的值,s1 长度若小于s2 长度则返回小于0 的值。 */ int strcasecmp (const char *s1, const char *s2);
/* 返回字符串 str 开头连续包含字符串 accept 内的字符数目。所以,如果 str 所包含的字符都属于 accept,那么返回 str 的长度;如果 str 的第一个字符不属于 accept,那么返回 0。 */ size_t strspn(const char *str, const char * accept);
//该函数返回在字符串 str 中第一次出现字符 c 的位置,如果未找到该字符则返回 NULL。 char *strchr(const char *str, int c)
1 #include <sys/socket.h> 2 #include <netinet/in.h> 3 #include <arpa/inet.h> 4 #include <assert.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <unistd.h> 8 #include <errno.h> 9 #include <string.h> 10 #include <fcntl.h> 11 12 #define BUFFER_SIZE 4096 13 enum CHECK_STATE { CHECK_STATE_REQUESTLINE = 0, CHECK_STATE_HEADER, CHECK_STATE_CONTENT }; 14 enum LINE_STATUS { LINE_OK = 0, LINE_BAD, LINE_OPEN }; 15 enum HTTP_CODE { NO_REQUEST, GET_REQUEST, BAD_REQUEST, FORBIDDEN_REQUEST, INTERNAL_ERROR, CLOSED_CONNECTION }; 16 static const char* szret[] = { "I get a correct result\n", "Something wrong\n" }; 17 18 LINE_STATUS parse_line( char* buffer, int& checked_index, int& read_index ) 19 { 20 char temp; 21 for ( ; checked_index < read_index; ++checked_index ) 22 { 23 temp = buffer[ checked_index ]; 24 if ( temp == '\r' ) 25 { 26 if ( ( checked_index + 1 ) == read_index ) 27 { 28 return LINE_OPEN; 29 } 30 else if ( buffer[ checked_index + 1 ] == '\n' ) 31 { 32 buffer[ checked_index++ ] = '\0'; 33 buffer[ checked_index++ ] = '\0'; 34 return LINE_OK; 35 } 36 return LINE_BAD; 37 } 38 else if( temp == '\n' ) 39 { 40 if( ( checked_index > 1 ) && buffer[ checked_index - 1 ] == '\r' ) 41 { 42 buffer[ checked_index-1 ] = '\0'; 43 buffer[ checked_index++ ] = '\0'; 44 return LINE_OK; 45 } 46 return LINE_BAD; 47 } 48 } 49 return LINE_OPEN; 50 } 51 52 HTTP_CODE parse_requestline( char* szTemp, CHECK_STATE& checkstate ) 53 { 54 char* szURL = strpbrk( szTemp, " \t" ); 55 if ( ! szURL ) 56 { 57 return BAD_REQUEST; 58 } 59 *szURL++ = '\0'; 60 61 char* szMethod = szTemp; 62 if ( strcasecmp( szMethod, "GET" ) == 0 ) 63 { 64 printf( "The request method is GET\n" ); 65 } 66 else 67 { 68 return BAD_REQUEST; 69 } 70 71 szURL += strspn( szURL, " \t" ); 72 char* szVersion = strpbrk( szURL, " \t" ); 73 if ( ! szVersion ) 74 { 75 return BAD_REQUEST; 76 } 77 *szVersion++ = '\0'; 78 szVersion += strspn( szVersion, " \t" ); 79 if ( strcasecmp( szVersion, "HTTP/1.1" ) != 0 ) 80 { 81 return BAD_REQUEST; 82 } 83 84 if ( strncasecmp( szURL, "http://", 7 ) == 0 ) 85 { 86 szURL += 7; 87 szURL = strchr( szURL, '/' ); 88 } 89 90 if ( ! szURL || szURL[ 0 ] != '/' ) 91 { 92 return BAD_REQUEST; 93 } 94 95 //URLDecode( szURL ); 96 printf( "The request URL is: %s\n", szURL ); 97 checkstate = CHECK_STATE_HEADER; 98 return NO_REQUEST; 99 } 100 101 HTTP_CODE parse_headers( char* szTemp ) 102 { 103 if ( szTemp[ 0 ] == '\0' ) 104 { 105 return GET_REQUEST; 106 } 107 else if ( strncasecmp( szTemp, "Host:", 5 ) == 0 ) 108 { 109 szTemp += 5; 110 szTemp += strspn( szTemp, " \t" ); 111 printf( "the request host is: %s\n", szTemp ); 112 } 113 else 114 { 115 printf( "I can not handle this header\n" ); 116 } 117 118 return NO_REQUEST; 119 } 120 121 HTTP_CODE parse_content( char* buffer, int& checked_index, CHECK_STATE& checkstate, int& read_index, int& start_line ) 122 { 123 LINE_STATUS linestatus = LINE_OK; 124 HTTP_CODE retcode = NO_REQUEST; 125 while( ( linestatus = parse_line( buffer, checked_index, read_index ) ) == LINE_OK ) 126 { 127 char* szTemp = buffer + start_line; 128 start_line = checked_index; 129 switch ( checkstate ) 130 { 131 case CHECK_STATE_REQUESTLINE: 132 { 133 retcode = parse_requestline( szTemp, checkstate ); 134 if ( retcode == BAD_REQUEST ) 135 { 136 return BAD_REQUEST; 137 } 138 break; 139 } 140 case CHECK_STATE_HEADER: 141 { 142 retcode = parse_headers( szTemp ); 143 if ( retcode == BAD_REQUEST ) 144 { 145 return BAD_REQUEST; 146 } 147 else if ( retcode == GET_REQUEST ) 148 { 149 return GET_REQUEST; 150 } 151 break; 152 } 153 default: 154 { 155 return INTERNAL_ERROR; 156 } 157 } 158 } 159 if( linestatus == LINE_OPEN ) 160 { 161 return NO_REQUEST; 162 } 163 else 164 { 165 return BAD_REQUEST; 166 } 167 } 168 169 int main( int argc, char* argv[] ) 170 { 171 if( argc <= 2 ) 172 { 173 printf( "usage: %s ip_address port_number\n", basename( argv[0] ) ); 174 return 1; 175 } 176 const char* ip = argv[1]; 177 int port = atoi( argv[2] ); 178 179 struct sockaddr_in address; 180 bzero( &address, sizeof( address ) ); 181 address.sin_family = AF_INET; 182 inet_pton( AF_INET, ip, &address.sin_addr ); 183 address.sin_port = htons( port ); 184 185 int listenfd = socket( PF_INET, SOCK_STREAM, 0 ); 186 assert( listenfd >= 0 ); 187 188 int ret = bind( listenfd, ( struct sockaddr* )&address, sizeof( address ) ); 189 assert( ret != -1 ); 190 191 ret = listen( listenfd, 5 ); 192 assert( ret != -1 ); 193 194 struct sockaddr_in client_address; 195 socklen_t client_addrlength = sizeof( client_address ); 196 int fd = accept( listenfd, ( struct sockaddr* )&client_address, &client_addrlength ); 197 if( fd < 0 ) 198 { 199 printf( "errno is: %d\n", errno ); 200 } 201 else 202 { 203 char buffer[ BUFFER_SIZE ]; 204 memset( buffer, '\0', BUFFER_SIZE ); 205 int data_read = 0; 206 int read_index = 0; 207 int checked_index = 0; 208 int start_line = 0; 209 CHECK_STATE checkstate = CHECK_STATE_REQUESTLINE; 210 while( 1 ) 211 { 212 data_read = recv( fd, buffer + read_index, BUFFER_SIZE - read_index, 0 ); 213 if ( data_read == -1 ) 214 { 215 printf( "reading failed\n" ); 216 break; 217 } 218 else if ( data_read == 0 ) 219 { 220 printf( "remote client has closed the connection\n" ); 221 break; 222 } 223 224 read_index += data_read; 225 HTTP_CODE result = parse_content( buffer, checked_index, checkstate, read_index, start_line ); 226 if( result == NO_REQUEST ) 227 { 228 continue; 229 } 230 else if( result == GET_REQUEST ) 231 { 232 send( fd, szret[0], strlen( szret[0] ), 0 ); 233 break; 234 } 235 else 236 { 237 send( fd, szret[1], strlen( szret[1] ), 0 ); 238 break; 239 } 240 } 241 close( fd ); 242 } 243 244 close( listenfd ); 245 return 0; 246 }