HTTP Parser and message builder / objects in plain C Snapshot
|
00001 #include "http.h" 00002 #include <sutils.h> 00003 #include <charclass.h> 00004 #include <stdio.h> 00005 00006 int init_parsers_request_header( HTTP_PARSER *parser ); 00007 int init_parsers_general_header( HTTP_PARSER *parser ); 00008 00009 #define S_METHOD_GET "GET" 00010 #define S_METHOD_GET_LEN 3 00011 #define S_METHOD_POST "POST" 00012 #define S_METHOD_POST_LEN 4 00013 #define S_METHOD_PUT "PUT" 00014 #define S_METHOD_PUT_LEN 3 00015 #define S_METHOD_DELETE "DELETE" 00016 #define S_METHOD_DELETE_LEN 6 00017 #define S_METHOD_HEAD "HEAD" 00018 #define S_METHOD_HEAD_LEN 4 00019 #define S_METHOD_OPTIONS "OPTIONS" 00020 #define S_METHOD_OPTIONS_LEN 7 00021 #define S_METHOD_TRACE "TRACE" 00022 #define S_METHOD_TRACE_LEN 5 00023 #define S_METHOD_CONNECT "CONNECT" 00024 #define S_METHOD_CONNECT_LEN 7 00025 00026 #define HTTP_START_S "HTTP/1." 00027 #define HTTP_START_S_LEN 7 00028 00029 00030 00031 #define HTTP_EOF_LINE "\r\n" 00032 #define HTTP_EOF_LINE_LEN 2 00033 00034 00035 00036 //========================================================================== 00037 00038 #define to_lower(pos) \ 00039 if (is_upper_case( *pos )) { \ 00040 *pos = *pos - 'A' + 'a'; \ 00041 } 00042 00043 00044 //CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)> 00045 M_INLINE int http_is_ctl( char ch ) 00046 { 00047 return (ch >=0 && ch <= 31) || ch == 127; 00048 } 00049 00050 M_INLINE int http_is_ctl_or_space( char ch ) 00051 { 00052 return (ch >=0 && ch <= 32) || ch == 127; 00053 } 00054 00055 M_INLINE int http_is_space( char ch ) 00056 { 00057 return ch == ' ' || ch == '\t'; 00058 } 00059 00060 // separators = "(" | ")" | "<" | ">" | "@" 00061 // | "," | ";" | ":" | "\" | <"> 00062 // | "/" | "[" | "]" | "?" | "=" 00063 // | "{" | "}" | SP | HT 00064 M_INLINE int http_is_separator( char ch ) 00065 { 00066 return ch == '(' || ch == ')' || ch == '<' || ch == '>' || ch == '@' 00067 || ch == ',' || ch == ';' || ch == ':' || ch == '\\' || ch == '"' 00068 || ch == '/' || ch == '[' || ch == ']' || ch == '?' || ch == '=' 00069 || ch == '{' || ch == '}' || ch == ' ' || ch == '\t'; 00070 } 00071 00072 00073 static void free_stringpair( DLISTUNR *list, void *entry, void *context) 00074 { 00075 STRINGPAIR *pair = (STRINGPAIR *) entry; 00076 00077 M_UNUSED(list); 00078 M_UNUSED(context); 00079 00080 free( pair->key ); 00081 free( pair->value ); 00082 } 00083 00084 00085 //========================================================================== 00086 int HTTP_MESSAGE_init( HTTP_MESSAGE *message ) 00087 { 00088 message->flags = 0; 00089 message->content_length = -1; 00090 00091 if (DLISTUNR_init( &message->header_values, sizeof(STRINGPAIR), 10) ) { 00092 return -1; 00093 } 00094 return 0; 00095 } 00096 00097 void HTTP_MESSAGE_free( HTTP_MESSAGE *message ) 00098 { 00099 message->flags = 0; 00100 message->content_length = -1; 00101 DLISTUNR_free( &message->header_values, free_stringpair, 0); 00102 00103 } 00104 00105 int HTTP_MESSAGE_add_header( HTTP_MESSAGE *message, const char *name , const char *value ) 00106 { 00107 STRINGPAIR entry; 00108 00109 entry.key = strdup(name); 00110 entry.value = strdup(value); 00111 00112 return DLISTUNR_push_back( &message->header_values, &entry, sizeof(entry)); 00113 } 00114 00115 const char * HTTP_MESSAGE_find_header( HTTP_MESSAGE *message, const char *name ) { 00116 DLISTUNR_position idx; 00117 STRINGPAIR *cur; 00118 00119 DLISTUNR_FOREACH( idx, &message->header_values ) { 00120 cur = (STRINGPAIR *) DLISTUNR_at( &message->header_values, idx ); 00121 if (strcmp(cur->key, name) == 0) { 00122 return cur->value; 00123 } 00124 } 00125 return 0; 00126 } 00127 00128 STRINGPAIR * HTTP_MESSAGE_first_header( HTTP_MESSAGE *message, DLISTUNR_position *pos ) 00129 { 00130 *pos = DLISTUNR_get_first( &message->header_values ); 00131 return (STRINGPAIR *) DLISTUNR_at( &message->header_values, *pos ); 00132 } 00133 00134 STRINGPAIR * HTTP_MESSAGE_next_header(HTTP_MESSAGE *message, DLISTUNR_position *pos ) 00135 { 00136 if (pos->entry == (DLISTUNR_entry *) &message->header_values.root) { 00137 return 0; 00138 } 00139 *pos = DLISTUNR_next( *pos ); 00140 return (STRINGPAIR *) DLISTUNR_at( &message->header_values, *pos ); 00141 } 00142 00143 00144 00145 //========================================================================== 00146 00147 typedef struct tagHEADER_HASH_ACTION { 00148 HASH_Entry entry; 00149 HEADER_ACTION action; 00150 char *key; 00151 size_t key_size; 00152 } HEADER_HASH_ACTION; 00153 00154 #define MAX_HTTP_TOKEN_SIZE 120 00155 00156 static int hash_compare(HASH_Entry *entry, void * key, ssize_t key_length) 00157 { 00158 HEADER_HASH_ACTION *lhs; 00159 00160 lhs = (HEADER_HASH_ACTION *) entry; 00161 00162 return strncmp(lhs->key, key, key_length); 00163 } 00164 00165 int HTTP_PARSER_init( HTTP_PARSER *parser ) 00166 { 00167 parser->state = HTTP_STATE_PARSING_REQUEST_LINE; 00168 parser->token = malloc( MAX_HTTP_TOKEN_SIZE ); 00169 if (! parser->token ) { 00170 return -1; 00171 } 00172 parser->token_length = MAX_HTTP_TOKEN_SIZE; 00173 00174 if (HASH_init( &parser->header_action, 32, 0, hash_compare, 0 ) ) { 00175 return -1; 00176 } 00177 00178 return 0; 00179 } 00180 00181 int HTTP_PARSER_free( HTTP_PARSER *parser ) 00182 { 00183 if (parser->token) { 00184 free( parser->token ); 00185 parser->token = 0; 00186 } 00187 return 0; 00188 } 00189 00190 00191 int HTTP_add_header_parser( HTTP_PARSER *parser, const char *header_name, HEADER_ACTION action_func ) 00192 { 00193 HEADER_HASH_ACTION * action; 00194 00195 action = ( HEADER_HASH_ACTION *) malloc( sizeof( HEADER_HASH_ACTION ) ) ; 00196 if (!action) { 00197 return -1; 00198 } 00199 action->key = strdup( header_name ); 00200 action->key_size = strlen( header_name ); 00201 action->action = action_func; 00202 00203 00204 return HASH_insert( &parser->header_action, &action->entry, (void *) header_name, strlen(header_name) ); 00205 } 00206 00207 00208 PARSER_STATUS HTTP_get_line( BF *bf, char **start_line ) 00209 { 00210 uint8_t *pos; 00211 #if 0 00212 uint8_t *prev; 00213 #endif 00214 char *line; 00215 00216 pos = bf->get_pos; 00217 line = BF_get_line_ext( bf, HTTP_EOF_LINE, HTTP_EOF_LINE_LEN ); 00218 if (!line) { 00219 if (BF_is_full( bf ) ) { 00220 // whole buffer does not even contain a line. untennable. 00221 return PARSER_STATUS_ERROR; 00222 } 00223 BF_compact(bf); 00224 return PARSER_STATUS_NEED_MORE_DATA; 00225 } 00226 00227 00228 #if 0 00229 // check if this line is continued. 00230 while ( 1 ) { 00231 prev = bf->get_pos; 00232 line = BF_get_line_ext( bf, HTTP_EOF_LINE, HTTP_EOF_LINE_LEN ); 00233 if (!line) { 00234 // restore the line marker. 00235 *(bf->get_pos - 2) = '\r'; 00236 00237 if (BF_is_full( bf ) ) { 00238 // a continuation line spans the whole input buffer, 00239 // That's very bad, unfortunately we can't support this. 00240 return PARSER_STATUS_ERROR; 00241 } 00242 bf->get_pos = pos; 00243 return PARSER_STATUS_NEED_MORE_DATA; 00244 } 00245 00246 if (!is_space( *line )) { 00247 *(bf->get_pos - 2) = '\r'; 00248 bf->get_pos = prev; 00249 break; 00250 } 00251 // adjoin the previous line to this line. 00252 *(bf->get_pos - 2) = ' '; 00253 *(bf->get_pos - 1) = ' '; 00254 } 00255 #endif 00256 00257 *start_line = (char *) pos; 00258 return PARSER_STATUS_COMPLETED; 00259 } 00260 00261 /* 00262 token = 1*<any CHAR except CTLs or separators> 00263 separators = "(" | ")" | "<" | ">" | "@" 00264 | "," | ";" | ":" | "\" | <"> 00265 | "/" | "[" | "]" | "?" | "=" 00266 | "{" | "}" | SP | HT 00267 00268 00269 quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) 00270 qdtext = <any TEXT except <">> 00271 quoted-pair = "\" CHAR 00272 00273 */ 00274 00275 int HTTP_get_header_token( HTTP_PARSER *parser) 00276 { 00277 char ch; 00278 size_t token_pos; 00279 00280 if (*parser->tokpos == '\0') { 00281 return HTTP_TK_EOF; 00282 } 00283 00284 00285 // skip leading spaces 00286 for( ;http_is_space( *parser->tokpos ); ++parser->tokpos ); 00287 00288 ch = *parser->tokpos; 00289 00290 if (http_is_separator(ch)) { 00291 parser->token[ 0 ] = ch; 00292 parser->token[ 1 ] = '\0'; 00293 ++ parser->tokpos; 00294 return HTTP_TK_SEPARATOR; 00295 } 00296 00297 00298 if (ch == '"') { 00299 token_pos = 0; 00300 do { 00301 ch = *parser->tokpos ++ ; 00302 chk: 00303 if (http_is_ctl( ch )) { 00304 return -1; 00305 } 00306 if (ch == '"') { 00307 if (token_pos > parser->token_length) { 00308 return -1; 00309 } 00310 parser->token[ token_pos++ ] = '\0'; 00311 return HTTP_TK_QUOTED_STRING; 00312 } else { 00313 if (ch == '\\') { 00314 goto chk; 00315 } 00316 } 00317 00318 // add character. 00319 if (token_pos > parser->token_length) { 00320 return -1; 00321 } 00322 parser->token[ token_pos++ ] = ch; 00323 00324 } while (ch != '\0'); 00325 00326 } 00327 00328 if ( !http_is_separator( ch ) ) { 00329 token_pos = 0; 00330 do { 00331 00332 if (token_pos >= parser->token_length) { 00333 return -1; 00334 } 00335 parser->token[ token_pos++ ] = ch; 00336 00337 ch = *( ++ parser->tokpos ); 00338 } while( ch != 0 && ! http_is_ctl( ch ) && ! http_is_separator( ch ) ); 00339 00340 parser->token[ token_pos ] = '\0'; 00341 return HTTP_TK_TEXT; 00342 } 00343 00344 return -1; 00345 } 00346 00347 PARSER_STATUS HTTP_parse_header_line( HTTP_PARSER *parser, HTTP_MESSAGE *request, BF *bf, int *eof_header) 00348 { 00349 PARSER_STATUS rt; 00350 HEADER_HASH_ACTION *action; 00351 char *line, *pos; 00352 00353 *eof_header = 0; 00354 00355 rt = HTTP_get_line( bf, &line ); 00356 if (rt != PARSER_STATUS_COMPLETED) { 00357 return rt; 00358 } 00359 00360 if (! *line ) { 00361 *eof_header = 1; 00362 return PARSER_STATUS_COMPLETED; 00363 } 00364 00365 if (is_space(*line)) { 00366 return -1; 00367 } 00368 // good enough, do not test for all separators though. 00369 for( pos = line ;!http_is_ctl_or_space( *pos ) && *pos != ':'; ++pos ) { 00370 to_lower( pos ); 00371 } 00372 if (*pos != ':') { 00373 return -1; 00374 } 00375 *pos = '\0'; 00376 00377 if (HTTP_MESSAGE_add_header( request, line, pos+1 )) { 00378 return -1; 00379 } 00380 00381 00382 // got the header line. find header processor. 00383 action = (HEADER_HASH_ACTION *) HASH_find( &parser->header_action, line, pos - line ); 00384 if (action) { 00385 00386 parser->tokpos = pos + 1; 00387 action->action( request, parser ); 00388 } 00389 00390 // recored header value in http request (optional) 00391 00392 return 0; 00393 } 00394 00395 int HTTP_PARSER_content_length_init( HTTP_PARSER *parser, HTTP_MESSAGE *msg ) 00396 { 00397 parser->state = HTTP_STATE_PARSING_BODY_CONTENT_LENGTH; 00398 parser->content_left = msg-> content_length; 00399 return 0; 00400 } 00401 00402 int HTTP_PARSER_content_length_process( HTTP_PARSER *parser, BF *bf, HTTP_PROCESS_MSG_DATA cb, HTTP_MESSAGE *msg, void *ctx) 00403 { 00404 size_t bytes_to_take; 00405 size_t bytes_available; 00406 00407 bytes_available = BF_get_size( bf ); 00408 if (bytes_available == 0) { 00409 return PARSER_STATUS_NEED_MORE_DATA; 00410 } 00411 00412 bytes_to_take = parser->content_left; 00413 if (bytes_to_take == 0) { 00414 return PARSER_STATUS_ERROR; 00415 } 00416 00417 if (bytes_available < bytes_to_take) { 00418 bytes_to_take = bytes_available; 00419 } 00420 00421 if (cb( msg, bf->get_pos, bytes_to_take, ctx )) { 00422 return PARSER_STATUS_ERROR; 00423 } 00424 00425 parser->content_left -= bytes_to_take; 00426 bf->get_pos += bytes_to_take; 00427 00428 if (parser->content_left == 0) { 00429 parser->state = HTTP_STATE_PARSING_REQUEST_LINE; 00430 return PARSER_STATUS_COMPLETED; 00431 } 00432 00433 BF_compact(bf); 00434 return PARSER_STATUS_NEED_MORE_DATA; 00435 } 00436 00437 int HTTP_PARSER_chunked_data_init( HTTP_PARSER *parser ) 00438 { 00439 parser->state = HTTP_STATE_PARSING_BODY_CHUNK_HEADER; 00440 return 0; 00441 } 00442 00443 00444 00445 /* 00446 Chunked-Body = *chunk 00447 last-chunk 00448 trailer 00449 CRLF 00450 chunk = chunk-size [ chunk-extension ] CRLF 00451 chunk-data CRLF 00452 chunk-size = 1*HEX 00453 last-chunk = 1*("0") [ chunk-extension ] CRLF 00454 chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) 00455 chunk-ext-val = token | quoted-string 00456 chunk-data = chunk-size(OCTET) 00457 trailer = *(entity-header CRLF) 00458 */ 00459 PARSER_STATUS HTTP_PARSER_chunked_data_process( HTTP_PARSER *parser, BF *bf, HTTP_PROCESS_MSG_DATA cb, HTTP_MESSAGE *msg, void *ctx) 00460 { 00461 char *line,*pos,*cur, *endptr; 00462 int rt, eof_header; 00463 long val; 00464 00465 switch( parser->state ) { 00466 case HTTP_STATE_PARSING_BODY_CHUNK_HEADER: 00467 chunk_header: 00468 line = BF_get_line_ext( bf, HTTP_EOF_LINE, HTTP_EOF_LINE_LEN ); 00469 if (!line) { 00470 BF_compact(bf); 00471 return PARSER_STATUS_NEED_MORE_DATA; 00472 } 00473 for(pos = line; is_space(*pos); ++pos); 00474 for(cur = pos; is_hex(*cur); ++cur); 00475 *cur = '\0'; 00476 00477 val = strtol( pos, &endptr, 16 ); 00478 if (val != 0) { 00479 parser->content_left = (size_t) val; 00480 parser->state = HTTP_STATE_PARSING_BODY_CHUNK_DATA; 00481 goto chunk_data; 00482 } else { 00483 parser->state = HTTP_STATE_PARSING_BODY_CHUNK_TRAILER; 00484 goto chunk_trailer; 00485 } 00486 00487 00488 case HTTP_STATE_PARSING_BODY_CHUNK_DATA: 00489 chunk_data: 00490 rt = HTTP_PARSER_content_length_process( parser, bf, cb, msg, ctx ); 00491 if (rt != 0) { 00492 return rt; 00493 } 00494 00495 parser->state = HTTP_STATE_PARSING_BODY_CHUNK_EOF_AFTER_DATA; 00496 goto chunk_eof_data; 00497 00498 case HTTP_STATE_PARSING_BODY_CHUNK_EOF_AFTER_DATA: 00499 chunk_eof_data: 00500 line = BF_get_line_ext( bf, HTTP_EOF_LINE, HTTP_EOF_LINE_LEN ); 00501 if (!line) { 00502 BF_compact(bf); 00503 return PARSER_STATUS_NEED_MORE_DATA; 00504 } 00505 parser->state = HTTP_STATE_PARSING_BODY_CHUNK_HEADER; 00506 goto chunk_header; 00507 00508 case HTTP_STATE_PARSING_BODY_CHUNK_TRAILER: 00509 chunk_trailer: 00510 rt = HTTP_parse_header_line( parser, msg , bf, &eof_header ); 00511 if (rt != PARSER_STATUS_COMPLETED) { 00512 return rt; 00513 } 00514 if (eof_header) { 00515 BF_compact(bf); 00516 return PARSER_STATUS_COMPLETED; 00517 } 00518 return PARSER_STATUS_NEED_MORE_DATA; 00519 00520 default: 00521 return -1; 00522 } 00523 return 0; 00524 } 00525 00526 00527 //========================================================================== 00528 00529 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF 00530 // Method = "OPTIONS" ; Section 9.2 00531 // | "GET" ; Section 9.3 00532 // | "HEAD" ; Section 9.4 00533 // | "POST" ; Section 9.5 00534 // | "PUT" ; Section 9.6 00535 // | "DELETE" ; Section 9.7 00536 // | "TRACE" ; Section 9.8 00537 // | "CONNECT" ; Section 9.9 00538 // | extension-method 00539 // 00540 // extension-method = token 00541 // 00542 // Request-URI = "*" | absoluteURI | abs_path | authority 00543 // 00544 static PARSER_STATUS parse_request_line( HTTP_REQUEST *request, BF *bf ) 00545 { 00546 Http_method_type method; 00547 char *eof,*tok, *line; 00548 PARSER_STATUS rt; 00549 00550 rt = HTTP_get_line( bf, &line ); 00551 if (rt != PARSER_STATUS_COMPLETED) { 00552 return rt; 00553 } 00554 00555 switch( *line ) { 00556 case 'G': 00557 if (strncmp( line, S_METHOD_GET, S_METHOD_GET_LEN ) != 0) { 00558 return PARSER_STATUS_ERROR; 00559 } 00560 method = HTTP_METHOD_GET; 00561 line += S_METHOD_GET_LEN; 00562 break; 00563 case 'P': 00564 if (strncmp( line, S_METHOD_POST, S_METHOD_POST_LEN ) == 0) { 00565 method = HTTP_METHOD_POST; 00566 line += S_METHOD_POST_LEN; 00567 } else if (strncmp( line, S_METHOD_PUT, S_METHOD_PUT_LEN ) == 0) { 00568 method = HTTP_METHOD_PUT; 00569 line += S_METHOD_PUT_LEN; 00570 } else { 00571 return PARSER_STATUS_ERROR; 00572 } 00573 break; 00574 case 'H': 00575 if (strncmp( line, S_METHOD_HEAD, S_METHOD_HEAD_LEN ) != 0) { 00576 return PARSER_STATUS_ERROR; 00577 } 00578 method = HTTP_METHOD_HEAD; 00579 line += S_METHOD_HEAD_LEN ; 00580 break; 00581 case 'D': 00582 if (strncmp( line, S_METHOD_DELETE, S_METHOD_DELETE_LEN ) != 0) { 00583 return PARSER_STATUS_ERROR; 00584 } 00585 method = HTTP_METHOD_DELETE; 00586 line += S_METHOD_DELETE_LEN ; 00587 break; 00588 case 'T': 00589 if (strncmp( line, S_METHOD_TRACE, S_METHOD_TRACE_LEN ) != 0) { 00590 return PARSER_STATUS_ERROR; 00591 } 00592 method = HTTP_METHOD_TRACE; 00593 line += S_METHOD_TRACE_LEN ; 00594 break; 00595 case 'C': 00596 if (strncmp( line, S_METHOD_CONNECT, S_METHOD_CONNECT_LEN ) != 0) { 00597 return PARSER_STATUS_ERROR; 00598 } 00599 method = HTTP_METHOD_CONNECT; 00600 line += S_METHOD_CONNECT_LEN; 00601 break; 00602 default: 00603 return -1; 00604 } 00605 request->method = method; 00606 00607 tok = get_token( line, &eof ); 00608 if (!tok) { 00609 return PARSER_STATUS_ERROR; 00610 } 00611 00612 *eof = 0; 00613 request->raw_url = strdup( tok ); 00614 00615 if (method != HTTP_METHOD_CONNECT) { 00616 if (URI_parse( &request->url, tok )) { 00617 return PARSER_STATUS_ERROR; 00618 } 00619 } else { 00620 if (strcmp(tok, "*") != 0) { 00621 return PARSER_STATUS_ERROR; 00622 } 00623 URI_init( &request->url ); 00624 } 00625 00626 tok = get_token( eof+1, &eof ); 00627 if (strncmp( tok, HTTP_START_S, HTTP_START_S_LEN ) != 0) { 00628 return PARSER_STATUS_ERROR; 00629 } 00630 tok += HTTP_START_S_LEN; 00631 00632 switch(tok[0]) { 00633 case '0': 00634 request->version = HTTP_VERSION_1_0; 00635 break; 00636 case '1': 00637 request->version = HTTP_VERSION_1_1; 00638 break; 00639 default: 00640 return PARSER_STATUS_ERROR; 00641 } 00642 return PARSER_STATUS_COMPLETED; 00643 } 00644 00645 00646 int HTTP_REQUEST_PARSER_init( HTTP_REQUEST_PARSER *parser, 00647 HTTP_REQ_HEADER_PARSED header_parsed, 00648 HTTP_REQ_MESSAGE_BODY_DATA on_message_body_data, 00649 HTTP_REQ_FINISHED on_request_finished, 00650 void *ctx) 00651 { 00652 parser->ctx = ctx; 00653 parser->ev_header = header_parsed; 00654 parser->ev_body = on_message_body_data; 00655 parser->ev_finish = on_request_finished; 00656 00657 if (HTTP_PARSER_init( &parser->base )) { 00658 return -1; 00659 } 00660 00661 if (init_parsers_request_header( &parser->base ) || 00662 init_parsers_general_header( &parser->base ) ) { 00663 return -1; 00664 } 00665 00666 return 0; 00667 } 00668 00669 00670 00671 PARSER_STATUS HTTP_REQUEST_PARSER_process( HTTP_REQUEST_PARSER *parser, HTTP_REQUEST *request, BF *bf ) 00672 { 00673 int rt, eof_header; 00674 00675 00676 while(1) { 00677 switch( parser->base.state ) { 00678 case HTTP_STATE_PARSING_REQUEST_LINE: 00679 rt = parse_request_line( request, bf ); 00680 if (rt != 0) { 00681 goto req_do_return; 00682 } 00683 parser->base.state = HTTP_STATE_PARSING_HEADERS; // 00684 //break; - fall through 00685 00686 00687 case HTTP_STATE_PARSING_HEADERS: 00688 rt = HTTP_parse_header_line( (HTTP_PARSER *) parser, (HTTP_MESSAGE *) request, bf, &eof_header ); 00689 if (rt != 0) { 00690 goto req_do_return; 00691 } 00692 00693 if (eof_header) { 00694 #if 0 00695 // this check will we done by web server; if no host header then send back HTTP status 400. 00696 00697 // if http 1.1 then host header is a must. no host header is an error. 00698 if (request->version == HTTP_VERSION_1_1 && 00699 !request->has_host_header) { 00700 return PARSER_STATUS_ERROR; 00701 } 00702 #endif 00703 00704 // eof parsing http header. 00705 if (parser->ev_header( request, parser->ctx )) { 00706 return PARSER_STATUS_ERROR; 00707 } 00708 BF_compact(bf); 00709 00710 if (request->base.flags & HTTP_MESSAGE_FLAG_TRANSFER_CHUNKED) { 00711 HTTP_PARSER_chunked_data_init( &parser->base ); 00712 continue; 00713 } 00714 00715 00716 if (request->base.flags & HTTP_MESSAGE_FLAG_HAS_CONTENT_LENGTH) { 00717 HTTP_PARSER_content_length_init( &parser->base, &request->base ); 00718 continue; 00719 } 00720 00721 next_request: 00722 if (parser->ev_finish( request, parser->ctx )) { 00723 return PARSER_STATUS_ERROR; 00724 } 00725 parser->base.state = HTTP_STATE_PARSING_REQUEST_LINE; 00726 HTTP_REQUEST_free(request); 00727 return PARSER_STATUS_COMPLETED; 00728 } 00729 break; 00730 00731 case HTTP_STATE_PARSING_BODY_CONTENT_LENGTH: 00732 rt = HTTP_PARSER_content_length_process( 00733 &parser->base, bf, 00734 (HTTP_PROCESS_MSG_DATA) parser->ev_body, 00735 &request->base, parser->ctx); 00736 if (rt != 0) { 00737 goto req_do_return; 00738 } 00739 goto next_request; 00740 00741 default: // remaining states deal with chunks 00742 rt = HTTP_PARSER_chunked_data_process( 00743 &parser->base, bf, 00744 (HTTP_PROCESS_MSG_DATA) parser->ev_body, 00745 &request->base, parser->ctx); 00746 00747 if (rt != 0) { 00748 goto req_do_return; 00749 } 00750 goto next_request; 00751 } 00752 } 00753 00754 req_do_return: 00755 if (rt == PARSER_STATUS_NEED_MORE_DATA) { 00756 BF_compact( bf ); 00757 } 00758 return rt; 00759 } 00760 00761 int HTTP_REQUEST_is_persistent( HTTP_REQUEST *message ) 00762 { 00763 if (message->version == HTTP_VERSION_1_1) { 00764 if (message->base.flags & HTTP_MESSAGE_FLAG_CONNECTION_CLOSE) { 00765 return 0; 00766 } 00767 return 1; 00768 } 00769 00770 if (message->base.flags & HTTP_MESSAGE_FLAG_KEEPALIVE) { 00771 return 0; 00772 } 00773 return 0; 00774 } 00775 00776 00777 // ============================================================================= 00778 //Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF 00779 static PARSER_STATUS parse_response_line( HTTP_RESPONSE *response, BF *bf ) 00780 { 00781 PARSER_STATUS rt; 00782 char *line, *tok, *eof; 00783 int st; 00784 00785 rt = HTTP_get_line( bf, &line ); 00786 if (rt != PARSER_STATUS_COMPLETED) { 00787 return rt; 00788 } 00789 00790 tok = line; 00791 if (strncmp( tok, HTTP_START_S, HTTP_START_S_LEN ) != 0) { 00792 return PARSER_STATUS_ERROR; 00793 } 00794 tok += HTTP_START_S_LEN; 00795 00796 switch(tok[0]) { 00797 case '0': 00798 response->version = HTTP_VERSION_1_0; 00799 break; 00800 case '1': 00801 response->version = HTTP_VERSION_1_1; 00802 break; 00803 default: 00804 return PARSER_STATUS_ERROR; 00805 } 00806 00807 tok = get_token( line, &eof ); 00808 if (!tok) { 00809 return PARSER_STATUS_ERROR; 00810 } 00811 00812 st = atoi( tok ); 00813 if (st < 100 || st >= 600) { 00814 return PARSER_STATUS_ERROR; 00815 } 00816 00817 response->status_code = st; 00818 00819 return PARSER_STATUS_COMPLETED; 00820 } 00821 00822 int HTTP_RESPONSE_PARSER_init( HTTP_RESPONSE_PARSER *parser, 00823 HTTP_RESP_HEADER_PARSED header_parsed, 00824 HTTP_RESP_MESSAGE_BODY_DATA on_message_body_data, 00825 HTTP_RESP_FINISHED on_request_finished, 00826 void *ctx) 00827 { 00828 parser->ctx = ctx; 00829 parser->ev_header = header_parsed; 00830 parser->ev_body = on_message_body_data; 00831 parser->ev_finish = on_request_finished; 00832 00833 if (! HTTP_PARSER_init( &parser->base )) { 00834 return -1; 00835 } 00836 00837 if (init_parsers_general_header( &parser->base ) ) { 00838 return -1; 00839 } 00840 return 0; 00841 } 00842 00843 00844 00845 PARSER_STATUS HTTP_RESPONSE_PARSER_process( HTTP_RESPONSE_PARSER *parser, HTTP_RESPONSE *response, BF *bf ) 00846 { 00847 int rt, eof_header; 00848 00849 while(1) { 00850 switch( parser->base.state ) { 00851 case HTTP_STATE_PARSING_REQUEST_LINE: 00852 rt = parse_response_line( response, bf ); 00853 if (rt != 0) { 00854 goto resp_do_return; 00855 } 00856 parser->base.state = HTTP_STATE_PARSING_HEADERS; // 00857 //break; - fall through 00858 00859 00860 case HTTP_STATE_PARSING_HEADERS: 00861 rt = HTTP_parse_header_line( (HTTP_PARSER *) parser, (HTTP_MESSAGE *) response, bf, &eof_header ); 00862 if (rt != 0) { 00863 goto resp_do_return; 00864 } 00865 00866 if (eof_header) { 00867 // eof parsing http header. 00868 parser->ev_header( response, parser->ctx ); 00869 00870 if (response->base.flags & HTTP_MESSAGE_FLAG_TRANSFER_CHUNKED) { 00871 parser->base.state = HTTP_STATE_PARSING_BODY_CHUNK_HEADER; 00872 continue; 00873 } 00874 00875 00876 if (response->base.flags & HTTP_MESSAGE_FLAG_HAS_CONTENT_LENGTH) { 00877 HTTP_PARSER_content_length_init( &parser->base, &response->base ); 00878 continue; 00879 } 00880 00881 next_response: 00882 HTTP_RESPONSE_free( response ); 00883 parser->ev_finish( response, parser->ctx ); 00884 parser->base.state = HTTP_STATE_PARSING_REQUEST_LINE; 00885 BF_compact(bf); 00886 return PARSER_STATUS_COMPLETED; 00887 } 00888 break; 00889 00890 case HTTP_STATE_PARSING_BODY_CONTENT_LENGTH: 00891 rt = HTTP_PARSER_content_length_process( 00892 &parser->base, bf, 00893 (HTTP_PROCESS_MSG_DATA) parser->ev_body, 00894 &response->base, parser->ctx); 00895 if (rt != 0) { 00896 goto resp_do_return; 00897 } 00898 goto next_response; 00899 00900 default: // remaining states deal with chunks 00901 rt = HTTP_PARSER_chunked_data_process( 00902 &parser->base, bf, 00903 (HTTP_PROCESS_MSG_DATA) parser->ev_body, 00904 &response->base, parser->ctx); 00905 00906 if (rt != 0) { 00907 goto resp_do_return; 00908 } 00909 goto next_response; 00910 } 00911 } 00912 resp_do_return: 00913 if (rt == PARSER_STATUS_NEED_MORE_DATA) { 00914 BF_compact( bf ); 00915 } 00916 return rt; 00917 } 00918 00919 // ============================================================================= 00920 00921 static const char *http_version_to_str( Http_version_type ver ) 00922 { 00923 switch(ver) { 00924 case HTTP_VERSION_1_0: 00925 return "1.0"; 00926 case HTTP_VERSION_1_1: 00927 return "1.1"; 00928 } 00929 return ""; 00930 } 00931 00932 #define S_CONNECTION_CLOSE "Connection: close\r\n" 00933 #define S_CONNECTION_CLOSE_LEN 19 00934 00935 #define S_TRANSFER_CHUNKED "Transfer-encoding: chunked\r\n" 00936 #define S_TRANSFER_CHUNKED_LEN 28 00937 00938 00939 #define RETURN_WRITER_MORE_DATA \ 00940 if (bf->start == bf->put_pos) { \ 00941 return PARSER_STATUS_ERROR; /* the buffer can't hold a single value */ \ 00942 } \ 00943 return PARSER_STATUS_NEED_MORE_DATA; 00944 00945 static const char *get_reason_phrase( int code ) 00946 { 00947 switch( code ) { 00948 case 100: return "Continue"; 00949 case 101: return "Switching Protocols"; 00950 case 200: return "OK"; 00951 case 201: return "Created"; 00952 case 202: return "Accepted"; 00953 case 203: return "Non-Authoritative Information"; 00954 case 204: return "No Content"; 00955 case 205: return "Reset Content"; 00956 case 206: return "Partial Content"; 00957 case 300: return "Multiple Choices"; 00958 case 301: return "Moved Permanently"; 00959 case 302: return "Found"; 00960 case 303: return "See Other"; 00961 case 304: return "Not Modified"; 00962 case 305: return "Use Proxy"; 00963 case 307: return "Temporary Redirect"; 00964 case 400: return "Bad Request"; 00965 case 401: return "Unauthorized"; 00966 case 402: return "Payment Required"; 00967 case 403: return "Forbidden"; 00968 case 404: return "Not Found"; 00969 case 405: return "Method Not Allowed"; 00970 case 406: return "Not Acceptable"; 00971 case 407: return "Proxy Authentication Required"; 00972 case 408: return "Request Time-out"; 00973 case 409: return "Conflict"; 00974 case 410: return "Gone"; 00975 case 411: return "Length Required"; 00976 case 412: return "Precondition Failed"; 00977 case 413: return "Request Entity Too Large"; 00978 case 414: return "Request-URI Too Large"; 00979 case 415: return "Unsupported Media Type"; 00980 case 416: return "Requested range not satisfiable"; 00981 case 417: return "Expectation Failed"; 00982 case 500: return "Internal Server Error"; 00983 case 501: return "Not Implemented"; 00984 case 502: return "Bad Gateway"; 00985 case 503: return "Service Unavailable"; 00986 case 504: return "Gateway Time-out"; 00987 case 505: return "HTTP Version not supported"; 00988 } 00989 return ""; 00990 } 00991 00992 PARSER_STATUS HTTP_RESPONSE_WRITER_write( HTTP_RESPONSE_WRITER *writer, BF *bf ) 00993 { 00994 HTTP_RESPONSE *resp = writer->response; 00995 char line_buf[40]; 00996 int nlen; 00997 00998 while(1) { 00999 switch( writer->state ) { 01000 case HTTP_RESPONSE_WR_STATUS_LINE: 01001 nlen = sprintf(line_buf,"HTTP/%s %d %s\r\n", http_version_to_str( resp->version ), resp->status_code, get_reason_phrase( resp->status_code ) ); 01002 if (BF_putn( bf, line_buf, nlen) ) { 01003 RETURN_WRITER_MORE_DATA; 01004 } 01005 writer->state = HTTP_RESPONSE_WR_CONNECTION_CLOSE; 01006 //break; 01007 case HTTP_RESPONSE_WR_CONNECTION_CLOSE: 01008 if (resp->base.flags & HTTP_MESSAGE_FLAG_CONNECTION_CLOSE ) { 01009 if (BF_putn( bf, S_CONNECTION_CLOSE, S_CONNECTION_CLOSE_LEN ) ) { 01010 RETURN_WRITER_MORE_DATA; 01011 } 01012 } 01013 writer->state = HTTP_RESPONSE_WR_CHUNKED; 01014 //break; 01015 case HTTP_RESPONSE_WR_CHUNKED: 01016 if (resp->base.flags & HTTP_MESSAGE_FLAG_TRANSFER_CHUNKED ) { 01017 if (BF_putn( bf, S_TRANSFER_CHUNKED, S_TRANSFER_CHUNKED_LEN ) ) { 01018 RETURN_WRITER_MORE_DATA; 01019 } 01020 } 01021 writer->state = HTTP_RESPONSE_WR_CONTENT_LENGTH; 01022 //break; 01023 case HTTP_RESPONSE_WR_CONTENT_LENGTH: 01024 if (resp->base.flags & HTTP_MESSAGE_FLAG_HAS_CONTENT_LENGTH ) { 01025 nlen = sprintf(line_buf,"Content-Length: %d\r\n", resp->base.content_length ); 01026 if (BF_putn( bf, line_buf, nlen) ) { 01027 RETURN_WRITER_MORE_DATA; 01028 } 01029 } 01030 writer->state = HTTP_RESPONSE_WR_HEADERS; 01031 writer->header_position = DLISTUNR_get_first( &resp->base.header_values ); 01032 writer->state_header = 0; 01033 //break; 01034 case HTTP_RESPONSE_WR_HEADERS: 01035 if ( ! DLISTUNR_is_eof( &resp->base.header_values, writer->header_position)) { 01036 STRINGPAIR *header = (STRINGPAIR *) DLISTUNR_at(&resp->base.header_values, writer->header_position); 01037 switch(writer->state_header) { 01038 case 0: 01039 if (BF_putn( bf, header->key, strlen(header->key))) { 01040 RETURN_WRITER_MORE_DATA; 01041 } 01042 writer->state_header = 1; 01043 //break; 01044 01045 case 1: 01046 if (BF_putn( bf, ": ", 2)) { 01047 RETURN_WRITER_MORE_DATA; 01048 } 01049 writer->state_header = 2; 01050 //break; 01051 01052 case 2: 01053 if (BF_putn( bf, header->value, strlen(header->value))) { 01054 RETURN_WRITER_MORE_DATA; 01055 } 01056 writer->state_header = 3; 01057 //break; 01058 01059 case 3: 01060 if (BF_putn( bf, "\r\n", 2)) { 01061 RETURN_WRITER_MORE_DATA; 01062 } 01063 writer->header_position = DLISTUNR_next(writer->header_position); 01064 writer->state_header = 0; 01065 break; 01066 } 01067 } else { 01068 writer->state = HTTP_RESPONSE_WR_EOF; 01069 goto wr_eof; 01070 01071 } 01072 break; 01073 case HTTP_RESPONSE_WR_EOF: 01074 wr_eof: 01075 if (BF_putn( bf, "\r\n", 2)) { 01076 RETURN_WRITER_MORE_DATA; 01077 } 01078 return PARSER_STATUS_COMPLETED; 01079 } 01080 } 01081 01082 } 01083 01084